#include import cargoxx.cli; import cargoxx.linkdb; import cargoxx.manifest; import cargoxx.util; import std; using cargoxx::cli::cmd_add; using cargoxx::cli::cmd_new; using cargoxx::linkdb::Database; using cargoxx::linkdb::Recipe; using cargoxx::util::ErrorCode; namespace manifest = cargoxx::manifest; namespace { // Disable resolve_version for the whole TU — these unit tests don't // want to hit search.devbox.sh or clone nixpkgs. Individual cases that // actually want to test the network path still set the env locally. struct DisableAutoResolveScope { DisableAutoResolveScope() { setenv("CARGOXX_NO_AUTORESOLVE", "1", 1); } }; const DisableAutoResolveScope autoresolve_disabled_; auto fresh_dir() -> std::filesystem::path { auto d = std::filesystem::temp_directory_path() / std::format("cargoxx-add-test-{}", std::random_device{}()); std::filesystem::create_directories(d); return d; } auto overlay_path(const std::filesystem::path& dir) -> std::filesystem::path { return dir / "overlay.sqlite"; } auto scaffold(const std::filesystem::path& parent) -> std::filesystem::path { REQUIRE(cmd_new("app", false, parent).has_value()); return parent / "app"; } auto seed_fmt(const std::filesystem::path& overlay) { auto db = Database::open(overlay); REQUIRE(db.has_value()); REQUIRE(db->add_manual("fmt", "*", Recipe{ .nixpkgs_attr = "fmt_10", .find_package = "fmt CONFIG REQUIRED", .targets = {"fmt::fmt"}, .source = "manual", }) .has_value()); } } // namespace TEST_CASE("cmd_add appends a string-form dependency", "[cli][add]") { auto parent = fresh_dir(); auto root = scaffold(parent); seed_fmt(overlay_path(parent)); auto r = cmd_add(root, "fmt", "10.2.0", {}, overlay_path(parent)); REQUIRE(r.has_value()); auto m = manifest::parse(root / "Cargoxx.toml"); REQUIRE(m.has_value()); REQUIRE(m->dependencies.size() == 1); REQUIRE(m->dependencies[0].name == "fmt"); REQUIRE(m->dependencies[0].version_spec == "10.2.0"); REQUIRE(m->dependencies[0].components.empty()); } TEST_CASE("cmd_add accepts an empty version and stores '*'", "[cli][add]") { auto parent = fresh_dir(); auto root = scaffold(parent); seed_fmt(overlay_path(parent)); auto r = cmd_add(root, "fmt", "", {}, overlay_path(parent)); REQUIRE(r.has_value()); auto m = manifest::parse(root / "Cargoxx.toml"); REQUIRE(m.has_value()); REQUIRE(m->dependencies.size() == 1); REQUIRE(m->dependencies[0].name == "fmt"); REQUIRE(m->dependencies[0].version_spec == "*"); } TEST_CASE("cmd_add with wildcard version still rejects unknown packages", "[cli][add]") { auto parent = fresh_dir(); auto root = scaffold(parent); auto r = cmd_add(root, "obscurelib", "", {}, overlay_path(parent)); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::LinkdbUnknownPackage); } TEST_CASE("cmd_add rejects an unknown package", "[cli][add]") { auto parent = fresh_dir(); auto root = scaffold(parent); auto r = cmd_add(root, "obscurelib", "0.0.1", {}, overlay_path(parent)); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::LinkdbUnknownPackage); } TEST_CASE("cmd_add rejects an already-declared dep", "[cli][add]") { auto parent = fresh_dir(); auto root = scaffold(parent); seed_fmt(overlay_path(parent)); REQUIRE(cmd_add(root, "fmt", "10.2.0", {}, overlay_path(parent)).has_value()); auto r = cmd_add(root, "fmt", "10.3.0", {}, overlay_path(parent)); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == ErrorCode::ManifestInvalidField); }