// Unit tests for resolver::verify_link with a mocked BuildFn that never // invokes nix or cmake. Covers the provisional-row lifecycle and the // scratch-project scaffolding without paying the cost of a real build. #include import cargoxx.resolver; import cargoxx.linkdb; import cargoxx.manifest; import cargoxx.util; import std; using cargoxx::resolver::verify_link; using cargoxx::resolver::VerifyLinkRequest; using cargoxx::linkdb::Database; using cargoxx::linkdb::Recipe; namespace { auto fresh_dir() -> std::filesystem::path { auto d = std::filesystem::temp_directory_path() / std::format("cargoxx-verify-link-test-{}", std::random_device{}()); std::filesystem::create_directories(d); return d; } auto sample_recipe() -> Recipe { return Recipe{ .nixpkgs_attr = "fmt", .find_package = "fmt CONFIG REQUIRED", .targets = {"fmt::fmt"}, .source = "conan", }; } auto make_request(const std::filesystem::path& parent) -> VerifyLinkRequest { return VerifyLinkRequest{ .candidate = sample_recipe(), .source = "conan", .package_name = "fmt", .version_spec = "*", .components = {}, .overlay_path = parent / "overlay.sqlite", .scratch_root = parent / "scratch", }; } } // namespace TEST_CASE("verify_link confirms the provisional row when the build succeeds", "[resolver][verify_link]") { auto parent = fresh_dir(); auto req = make_request(parent); bool build_called = false; auto r = verify_link(req, [&](const std::filesystem::path& root) { build_called = true; // Sanity-check the scaffold: the manifest and main.cpp must exist // in the dir handed to the build function. REQUIRE(std::filesystem::exists(root / "Cargoxx.toml")); REQUIRE(std::filesystem::exists(root / "src" / "main.cpp")); auto m = cargoxx::manifest::parse(root / "Cargoxx.toml"); REQUIRE(m.has_value()); REQUIRE(m->dependencies.size() == 1); REQUIRE(m->dependencies[0].name == "fmt"); return cargoxx::util::Result{}; }); REQUIRE(build_called); REQUIRE(r.has_value()); // Overlay row should now be confirmed (verified_at != 0). The simplest // way to check is to resolve through the Database — confirmed rows // satisfy `overlay_is_fresh` and surface as the matching recipe. auto db = Database::open(req.overlay_path); REQUIRE(db.has_value()); auto rec = db->resolve("fmt", "*", {}); REQUIRE(rec.has_value()); REQUIRE(rec->source == "conan"); REQUIRE(rec->find_package == "fmt CONFIG REQUIRED"); } TEST_CASE("verify_link rolls the provisional row back when the build fails", "[resolver][verify_link]") { auto parent = fresh_dir(); auto req = make_request(parent); auto r = verify_link(req, [&](const std::filesystem::path&) { return cargoxx::util::Result{std::unexpected(cargoxx::util::Error{ cargoxx::util::ErrorCode::BuildCmakeFailed, "fake build failure", "", std::nullopt, std::nullopt, })}; }); REQUIRE_FALSE(r.has_value()); REQUIRE(r.error().code == cargoxx::util::ErrorCode::BuildCmakeFailed); // The conan-source row must be gone; resolve falls through to the // curated linkdb (which has its own fmt recipe with source = "curated"). auto db = Database::open(req.overlay_path); REQUIRE(db.has_value()); auto rec = db->resolve("fmt", "10.2.0", {}); REQUIRE(rec.has_value()); REQUIRE(rec->source == "curated"); } TEST_CASE("verify_link cleans up its scratch project", "[resolver][verify_link]") { auto parent = fresh_dir(); auto req = make_request(parent); std::filesystem::path captured; (void)verify_link(req, [&](const std::filesystem::path& root) { captured = root; return cargoxx::util::Result{}; }); REQUIRE_FALSE(captured.empty()); REQUIRE_FALSE(std::filesystem::exists(captured)); }