119 lines
4.0 KiB
C++
119 lines
4.0 KiB
C++
// 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));
|
|
}
|