#include import cargoxx.codegen; import cargoxx.manifest; import cargoxx.layout; import cargoxx.lockfile; import cargoxx.linkdb; import std; using cargoxx::codegen::flake_nix; using cargoxx::codegen::GenerateInputs; using cargoxx::layout::DiscoveredLayout; using cargoxx::linkdb::Recipe; using cargoxx::lockfile::Lockfile; using cargoxx::lockfile::LockfilePackage; using cargoxx::manifest::Edition; using cargoxx::manifest::Manifest; using cargoxx::manifest::Package; namespace { auto pkg(std::string name) -> Package { return Package{ .name = std::move(name), .version = "0.1.0", .edition = Edition::Cpp23, .authors = {}, .license = std::nullopt, }; } auto recipe(std::string attr) -> Recipe { return Recipe{ .nixpkgs_attr = std::move(attr), .find_package = "", .targets = {}, .source = "curated", }; } auto root_pkg(std::string name, std::string version, std::optional rev = std::nullopt) -> LockfilePackage { return LockfilePackage{ .name = std::move(name), .version = std::move(version), .dependencies = {}, .nixpkgs_attr = std::nullopt, .nixpkgs_rev = std::move(rev), .linkdb_source = std::nullopt, }; } } // namespace TEST_CASE("flake_nix renders the package description and rev", "[codegen][flake]") { Manifest m{pkg("hello"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("hello", "0.1.0", "abc123def456")}}; GenerateInputs in{m, layout, lock, {}, "/tmp/hello"}; auto out = flake_nix(in); REQUIRE(out.find("description = \"hello\";") != std::string::npos); REQUIRE(out.find("nixpkgs/abc123def456") != std::string::npos); } TEST_CASE("flake_nix uses 'nixos-unstable' when no rev is pinned", "[codegen][flake]") { Manifest m{pkg("hello"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("hello", "0.1.0")}}; GenerateInputs in{m, layout, lock, {}, "/tmp/hello"}; auto out = flake_nix(in); REQUIRE(out.find("nixpkgs/nixos-unstable") != std::string::npos); } TEST_CASE("flake_nix emits an empty buildInputs list when there are no deps", "[codegen][flake]") { Manifest m{pkg("hello"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("hello", "0.1.0")}}; GenerateInputs in{m, layout, lock, {}, "/tmp/hello"}; auto out = flake_nix(in); REQUIRE(out.find("buildInputs = [\n ];") != std::string::npos); } TEST_CASE("flake_nix emits one pkgs. line per dep", "[codegen][flake]") { Manifest m{pkg("app"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("app", "0.1.0", "rev42")}}; std::vector recipes = {recipe("fmt_10"), recipe("spdlog")}; GenerateInputs in{m, layout, lock, recipes, "/tmp/app"}; auto out = flake_nix(in); REQUIRE(out.find("pkgs.fmt_10") != std::string::npos); REQUIRE(out.find("pkgs.spdlog") != std::string::npos); } TEST_CASE("flake_nix dedupes duplicate nixpkgs_attrs", "[codegen][flake]") { Manifest m{pkg("app"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("app", "0.1.0", "rev42")}}; // boost appears twice — same nixpkgs_attr from two component-bearing entries std::vector recipes = {recipe("boost"), recipe("boost")}; GenerateInputs in{m, layout, lock, recipes, "/tmp/app"}; auto out = flake_nix(in); auto first = out.find("pkgs.boost"); REQUIRE(first != std::string::npos); REQUIRE(out.find("pkgs.boost", first + 1) == std::string::npos); } TEST_CASE("flake_nix produces deterministic output", "[codegen][flake]") { Manifest m{pkg("app"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("app", "0.1.0", "rev42")}}; std::vector recipes = {recipe("fmt_10"), recipe("spdlog"), recipe("nlohmann_json")}; GenerateInputs in{m, layout, lock, recipes, "/tmp/app"}; auto a = flake_nix(in); auto b = flake_nix(in); REQUIRE(a == b); } TEST_CASE("flake_nix output ends with a newline", "[codegen][flake]") { Manifest m{pkg("hello"), {}, {}}; DiscoveredLayout layout{}; Lockfile lock{1, {root_pkg("hello", "0.1.0")}}; GenerateInputs in{m, layout, lock, {}, "/tmp/hello"}; auto out = flake_nix(in); REQUIRE_FALSE(out.empty()); REQUIRE(out.back() == '\n'); }