[M5+] add resolver::verify_link + provisional overlay lifecycle
This commit is contained in:
118
tests/verify_link_unit.cpp
Normal file
118
tests/verify_link_unit.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
// 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 <catch2/catch_test_macros.hpp>
|
||||
|
||||
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<void>{};
|
||||
});
|
||||
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<void>{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<void>{};
|
||||
});
|
||||
|
||||
REQUIRE_FALSE(captured.empty());
|
||||
REQUIRE_FALSE(std::filesystem::exists(captured));
|
||||
}
|
||||
Reference in New Issue
Block a user