package engine import ( "testing" "testing/fstest" "gitea.maximumdirect.net/eric/ConfigKit/internal/document" ) func TestDestinationPlacementCountsAgainstSharedNodeBudget(t *testing.T) { t.Parallel() limits := defaultImportLimits() destination := Keys("collections", "entry") empty := Mapping("empty", map[string]any{}) parsed, diagnostics := parseSource(empty, "fixture", defaultParseLimits()) if len(diagnostics) != 0 { t.Fatal(diagnostics) } perInput := countInputNodes(parsed.document) + destinationWrapperNodeCount(destination) for _, test := range []struct { name string delta int wantError bool wantCharge int }{ {name: "below aggregate bound", delta: -1, wantCharge: perInput}, {name: "at aggregate bound", delta: 0, wantCharge: perInput}, {name: "above aggregate bound", delta: 1, wantError: true}, } { t.Run(test.name, func(t *testing.T) { t.Parallel() accounting := &importAccounting{nodes: limits.nodes - perInput + test.delta} composition, _, _, diagnostics := acquireUnderlayGroup(loadGroup{inputs: []loadInput{{ source: empty, destination: destination, }}}, 1, nil, accounting) if test.wantError { if composition != nil || len(diagnostics) != 1 || diagnostics[0].Code != CodeSourceLimitExceeded { t.Fatalf("over-budget placement = %#v, %#v", composition, diagnostics) } return } if composition == nil || len(diagnostics) != 0 || composition.inputNodes != test.wantCharge { t.Fatalf("bounded placement = %#v, %#v", composition, diagnostics) } }) } } func TestMultipleMountedInputsSharePlacementCharges(t *testing.T) { t.Parallel() limits := defaultImportLimits() inputs := []loadInput{ {source: Mapping("one", map[string]any{}), destination: Keys("collection", "one")}, {source: Mapping("two", map[string]any{}), destination: Keys("collection", "two")}, {source: Mapping("three", map[string]any{}), destination: Keys("collection", "three")}, } perInput := countInputNodes(&document.Node{Kind: document.Mapping}) + destinationWrapperNodeCount(Keys("collection", "one")) charge := len(inputs) * perInput accounting := &importAccounting{nodes: limits.nodes - charge} composition, _, _, diagnostics := acquireUnderlayGroup(loadGroup{inputs: inputs}, 1, nil, accounting) if len(diagnostics) != 0 || composition == nil || accounting.nodes != limits.nodes || composition.inputNodes != charge { t.Fatalf("shared placement accounting = %#v, %#v, accounting %#v; want charge %d", composition, diagnostics, accounting, charge) } } func TestImportedEmptyBodiesChargeEachDestinationWrapper(t *testing.T) { t.Parallel() filesystem := NewFilesystem("bundle", fstest.MapFS{ "main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: fragment.yml}]}\n")}, "fragment.yml": &fstest.MapFile{Data: []byte("{}\n")}, }) mainSource := filesystem.File("main.yml") mainParsed, diagnostics := parseSource(mainSource, "fixture", defaultParseLimits()) if len(diagnostics) != 0 { t.Fatal(diagnostics) } fragmentParsed, diagnostics := parseSource(Bytes("fragment", []byte("{}\n")), "fixture", defaultParseLimits()) if len(diagnostics) != 0 { t.Fatal(diagnostics) } rawNodes := countInputNodes(mainParsed.document) + countInputNodes(fragmentParsed.document) destination := Keys("collection", "empty") placementNodes := 2 * destinationWrapperNodeCount(destination) limits := defaultImportLimits() for _, test := range []struct { name string delta int wantError bool }{ {name: "below aggregate bound", delta: -1}, {name: "at aggregate bound", delta: 0}, {name: "above aggregate bound", delta: 1, wantError: true}, } { t.Run(test.name, func(t *testing.T) { t.Parallel() accounting := &importAccounting{nodes: limits.nodes - rawNodes - placementNodes + test.delta} composition, sources, _, diagnostics := acquireUnderlayGroup(loadGroup{inputs: []loadInput{{ source: mainSource, imports: true, destination: destination, }}}, 1, nil, accounting) if test.wantError { if composition != nil || len(diagnostics) == 0 || diagnostics[0].Code != CodeImportLimitExceeded { t.Fatalf("over-budget imports = %#v, %#v", composition, diagnostics) } return } if len(diagnostics) != 0 || composition == nil || len(sources) != 2 { t.Fatalf("bounded imports = %#v, sources %#v, diagnostics %#v", composition, sources, diagnostics) } if composition.inputNodes != rawNodes+placementNodes || accounting.nodes != limits.nodes+test.delta { t.Fatalf("import node charge = %d, accounting %d; want %d and %d", composition.inputNodes, accounting.nodes, rawNodes+placementNodes, limits.nodes+test.delta) } if node, present := documentAtPath(composition.root, Keys("collection", "empty")); !present || node.Kind != document.Mapping || len(node.Mapping) != 0 { t.Fatalf("placed empty import body = %#v, present %v", node, present) } }) } } func TestNoSourceAndRootPlacementAddNoWrapperCharges(t *testing.T) { t.Parallel() accounting := &importAccounting{} composition, sources, _, diagnostics := acquireUnderlayGroup(loadGroup{inputs: []loadInput{ {source: NoSource(), destination: Keys("absent")}, {source: Mapping("root", map[string]any{})}, }}, 1, nil, accounting) if len(diagnostics) != 0 || composition == nil || len(sources) != 1 || accounting.nodes != 1 || composition.inputNodes != 1 { t.Fatalf("root and absent placement = %#v, sources %#v, diagnostics %#v, accounting %#v", composition, sources, diagnostics, accounting) } } func TestSelectedOverlayAndCLIUsePlacementNodeCharges(t *testing.T) { t.Parallel() limits := defaultImportLimits() baseNodes := countInputNodes(&document.Node{Kind: document.Mapping}) + destinationWrapperNodeCount(Keys("mounted")) t.Run("selected overlay", func(t *testing.T) { t.Parallel() filesystem := NewFilesystem("bundle", fstest.MapFS{ "overlays/test.yml": &fstest.MapFile{Data: []byte("{}\n")}, }) request := loadRequest[map[string]any]{ underlays: []loadGroup{{inputs: []loadInput{{source: Mapping("empty", map[string]any{}), destination: Keys("mounted")}}}}, overlay: "test", overlayFilesystem: &FilesystemContext{Root: ".", BaseDir: ".", FS: &filesystem}, } accounting := &importAccounting{nodes: limits.nodes - baseNodes} _, diagnostics := assembleLayeredInput(request, assembledInput{}, accounting) if len(diagnostics) != 1 || diagnostics[0].Code != CodeOverlayLimitExceeded { t.Fatalf("overlay diagnostics = %#v", diagnostics) } }) t.Run("CLI override", func(t *testing.T) { t.Parallel() override := Override{Path: Keys("mounted", "value"), Value: "x", Flag: "--value"} builder := valueBuilder{sourceID: "fixture", active: make(map[valueVisit]bool)} if _, issue := builder.build(override.Value, override.Path, len(override.Path.segments)); issue != nil { t.Fatal(issue) } overrideNodes := builder.nodes + destinationWrapperNodeCount(override.Path) request := loadRequest[map[string]any]{ underlays: []loadGroup{{inputs: []loadInput{{source: Mapping("empty", map[string]any{}), destination: Keys("mounted")}}}}, overrides: []Override{override}, } accounting := &importAccounting{nodes: limits.nodes - baseNodes - overrideNodes + 1} _, diagnostics := assembleLayeredInput(request, assembledInput{}, accounting) if len(diagnostics) != 1 || diagnostics[0].Code != CodeOverrideLimitExceeded { t.Fatalf("override diagnostics = %#v", diagnostics) } }) }