From 9b0efa7f3caec944da7ba239ed02bd9cc653323d Mon Sep 17 00:00:00 2001 From: Victor Borja Date: Thu, 18 Dec 2025 15:52:17 -0600 Subject: [PATCH] provides merge --- .../tests/aspect_merge_duplication.nix | 75 +++++++++++++++++++ .../modules/tests/aspect_provides_merge.nix | 53 +++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 checkmate/modules/tests/aspect_merge_duplication.nix create mode 100644 checkmate/modules/tests/aspect_provides_merge.nix diff --git a/checkmate/modules/tests/aspect_merge_duplication.nix b/checkmate/modules/tests/aspect_merge_duplication.nix new file mode 100644 index 0000000..b769177 --- /dev/null +++ b/checkmate/modules/tests/aspect_merge_duplication.nix @@ -0,0 +1,75 @@ +{ lib, targetLib, ... }: +let + flake-aspects-lib = import targetLib lib; +in +{ + # Test demonstrating duplication issue when merging aspect structures + # from multiple sources using lib.mkMerge at the module level. + # + # The problem: when lib.mkMerge combines pre-evaluated aspect structures, + # the recursive resolution in resolve.nix processes includes multiple times, + # causing multiplicative duplication. + # + # This reproduces the issue seen in den's namespace.nix where: + # config.den.ful.${name} = lib.mkMerge denfuls; + # causes 64x duplication when merging multiple input flakes. + + flake.tests."test mkMerge aspect duplication" = + let + # Create two separate pre-evaluated aspect scopes + inputA = lib.evalModules { + modules = [ + (flake-aspects-lib.new-scope "ns") + { ns.aspects.foo.nixos.vals = [ "A" ]; } + ]; + }; + + inputB = lib.evalModules { + modules = [ + (flake-aspects-lib.new-scope "ns") + { ns.aspects.bar.nixos.vals = [ "B" ]; } + ]; + }; + + # Merge using lib.mkMerge - reproduces den namespace.nix pattern + merged = lib.evalModules { + modules = [ + (flake-aspects-lib.new-scope "ns") + { + ns = lib.mkMerge [ + inputA.config.ns + inputB.config.ns + ]; + } + { + ns.aspects.combined.nixos.vals = [ "C" ]; + ns.aspects.combined.includes = [ + merged.config.ns.aspects.foo + merged.config.ns.aspects.bar + ]; + } + ]; + }; + + # Resolve + resolved = merged.config.ns.aspects.combined.modules.nixos; + result = lib.evalModules { + modules = [ + resolved + { options.vals = lib.mkOption { type = lib.types.listOf lib.types.str; }; } + ]; + }; + + allVals = result.config.vals; + sortedVals = lib.sort lib.lessThan allVals; + + in + { + expr = sortedVals; + expected = [ + "A" + "B" + "C" + ]; + }; +} diff --git a/checkmate/modules/tests/aspect_provides_merge.nix b/checkmate/modules/tests/aspect_provides_merge.nix new file mode 100644 index 0000000..8faa9d9 --- /dev/null +++ b/checkmate/modules/tests/aspect_provides_merge.nix @@ -0,0 +1,53 @@ +{ lib, targetLib, ... }: +{ + flake.tests.test-aspects-merged-provides = + let + flake-aspects-lib = import targetLib lib; + + first = lib.evalModules { + modules = [ + (flake-aspects-lib.new-scope "foo") + (flake-aspects-lib.new-scope "bar") + (flake-aspects-lib.new-scope "baz") + { + foo.aspects.a.provides.b.nixos.x = [ "b" ]; + } + { + bar.aspects.a.provides.c.nixos.x = [ "c" ]; + } + ( + { config, ... }: + { + bar = config.foo; + } + ) + ( + { config, ... }: + { + baz.aspects.all.nixos = { }; + baz.aspects.all.includes = [ + config.bar.aspects.a._.b + config.bar.aspects.a._.c + ]; + } + ) + ]; + }; + + second = lib.evalModules { + modules = [ + first.config.baz.aspects.all.modules.nixos + { options.x = lib.mkOption { type = lib.types.listOf lib.types.str; }; } + ]; + }; + + expr = lib.sort (a: b: a < b) (lib.unique second.config.x); + expected = [ + "b" + "c" + ]; + in + { + inherit expr expected; + }; +}