Activate selected overlay collection policies

This commit is contained in:
2026-10-23 19:30:48 +00:00
parent 9a7027a04c
commit 89a9684f0d
11 changed files with 708 additions and 44 deletions

View File

@@ -94,6 +94,263 @@ value: scalar
}
}
func TestLoadSelectedOverlayAppliesCollectionOperationsAndEvidence(t *testing.T) {
t.Parallel()
type item struct {
ID string `yaml:"id"`
Value string `yaml:"value"`
Children []item `yaml:"children"`
}
type config struct {
Items []item `yaml:"items"`
}
tests := []struct {
name string
mode string
overlay string
want []item
}{
{
name: "append",
mode: "append",
overlay: `configkit:
sequences:
- destination: [items]
key: id
mode: append
items: [{id: c, value: added}]
`,
want: []item{{ID: "a", Value: "old"}, {ID: "b", Value: "kept"}, {ID: "c", Value: "added"}},
},
{
name: "upsert replaces complete records at retained positions",
mode: "upsert",
overlay: `configkit:
sequences:
- destination: [items]
key: id
mode: upsert
items: [{id: a, value: new}]
`,
want: []item{{ID: "a", Value: "new"}, {ID: "b", Value: "kept"}},
},
{
name: "replace",
mode: "replace",
overlay: `configkit:
sequences:
- destination: [items]
key: id
mode: replace
items: [{id: c, value: replacement}]
`,
want: []item{{ID: "c", Value: "replacement"}},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "items: [{id: a, value: old, children: [{id: child}]}, {id: b, value: kept}]\n")
writeImportFile(t, directory, "overlays/testing.yml", test.overlay)
result, err := testLoad[config](File(mainPath), Options[config]{Overlay: "testing"})
if err != nil {
t.Fatal(err)
}
if len(result.Config.Items) != len(test.want) {
t.Fatalf("items = %#v, want %#v", result.Config.Items, test.want)
}
for index, want := range test.want {
if result.Config.Items[index].ID != want.ID || result.Config.Items[index].Value != want.Value {
t.Fatalf("items[%d] = %#v, want id %q value %q", index, result.Config.Items[index], want.ID, want.Value)
}
}
if _, found := result.Metadata().Lookup(Keys("configkit")); found {
t.Fatal("overlay collection controls reached application metadata")
}
if test.mode == "append" {
base, _ := result.Metadata().Lookup(Keys("items").Index(0).Key("value"))
added, _ := result.Metadata().Lookup(Keys("items").Index(2).Key("value"))
if base.Origin.Layer != LayerBase || added.Origin.Layer != LayerOverlay {
t.Fatalf("append origins = base %#v added %#v", base.Origin, added.Origin)
}
history := result.Metadata().History(Keys("items").Index(2).Key("value"))
if len(history) == 0 || history[len(history)-1].Origin.Layer != LayerOverlay {
t.Fatalf("append history = %#v", history)
}
}
if test.mode == "upsert" {
if _, found := result.Metadata().Lookup(Keys("items").Index(0).Key("children").Index(0)); found {
t.Fatal("upsert retained a discarded child")
}
history := result.Metadata().History(Keys("items").Index(0).Key("children").Index(0))
if len(history) < 2 {
t.Fatalf("discarded child history = %#v", history)
}
}
})
}
}
func TestLoadSelectedOverlayReconcilesPendingAndRetainedCollectionPolicy(t *testing.T) {
t.Parallel()
requireCode := func(t *testing.T, main, overlay string, code Code) {
t.Helper()
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", main)
writeImportFile(t, directory, "overlays/testing.yml", overlay)
_, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Overlay: "testing"})
var loadError *LoadError
if !errors.As(err, &loadError) || len(loadError.Diagnostics()) == 0 || loadError.Diagnostics()[0].Code != code {
t.Fatalf("load error = %#v, want %s", err, code)
}
}
t.Run("key only validates retained records", func(t *testing.T) {
requireCode(t, "items: [{id: duplicate}, {id: duplicate}]\n", `configkit:
sequences:
- destination: [items]
key: id
`, CodeSequenceConflict)
})
t.Run("mode only retains lower identity", func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
sequences:
- destination: [items]
key: id
mode: append
items: [{id: base}]
`)
writeImportFile(t, directory, "overlays/testing.yml", `configkit:
sequences:
- destination: [items]
mode: append
items: [{id: overlay}]
`)
result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Overlay: "testing"})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(result.Config.Items, []ordinarySequenceItem{{ID: "base"}, {ID: "overlay"}}) {
t.Fatalf("items = %#v", result.Config.Items)
}
})
t.Run("lower explicit mode remains active when overlay omits it", func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
sequences:
- destination: [items]
key: id
mode: append
items: [{id: base}]
`)
writeImportFile(t, directory, "overlays/testing.yml", "items: [{id: overlay}]\n")
result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Overlay: "testing"})
if err != nil || !reflect.DeepEqual(result.Config.Items, []ordinarySequenceItem{{ID: "base"}, {ID: "overlay"}}) {
t.Fatalf("retained operation = %#v, %v", result, err)
}
})
t.Run("overlay identity must agree with retained identity", func(t *testing.T) {
requireCode(t, `configkit:
sequences:
- destination: [items]
key: id
items: [{id: base}]
`, `configkit:
sequences:
- destination: [items]
key: slug
items: [{id: overlay}]
`, CodeSequenceConflict)
})
t.Run("replace accepts null and append rejects it", func(t *testing.T) {
for _, test := range []struct {
mode string
want Code
}{
{mode: "replace"},
{mode: "append", want: CodeSequenceInvalid},
} {
overlay := "configkit:\n sequences:\n - destination: [items]\n key: id\n mode: " + test.mode + "\nitems: null\n"
if test.want != "" {
requireCode(t, "items: [{id: base}]\n", overlay, test.want)
continue
}
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "items: [{id: base}]\n")
writeImportFile(t, directory, "overlays/testing.yml", overlay)
result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Overlay: "testing"})
if err != nil || result.Config.Items != nil {
t.Fatalf("replace null = %#v, %v", result, err)
}
}
})
t.Run("empty contributions and kind mismatches", func(t *testing.T) {
for _, test := range []struct {
name, mode, value string
want int
wantCode Code
}{
{name: "append empty", mode: "append", value: "[]", want: 1},
{name: "upsert empty", mode: "upsert", value: "[]", want: 1},
{name: "replace empty", mode: "replace", value: "[]", want: 0},
{name: "append mapping", mode: "append", value: "{id: overlay}", wantCode: CodeSequenceInvalid},
} {
t.Run(test.name, func(t *testing.T) {
overlay := "configkit:\n sequences:\n - destination: [items]\n key: id\n mode: " + test.mode + "\nitems: " + test.value + "\n"
if test.wantCode != "" {
requireCode(t, "items: [{id: base}]\n", overlay, test.wantCode)
return
}
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "items: [{id: base}]\n")
writeImportFile(t, directory, "overlays/testing.yml", overlay)
result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Overlay: "testing"})
if err != nil || len(result.Config.Items) != test.want {
t.Fatalf("%s result = %#v, %v", test.name, result, err)
}
})
}
})
}
func TestLoadSelectedOverlayKeepsCollectionPoliciesIndependent(t *testing.T) {
t.Parallel()
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
sequences:
- destination: [items]
key: id
mode: append
- destination: [catalog, items]
key: id
mode: append
items: [{id: base}]
catalog: {items: [{id: catalog-base}]}
`)
writeImportFile(t, directory, "overlays/testing.yml", `configkit:
sequences:
- destination: [items]
mode: upsert
- destination: [catalog, items]
mode: append
items: [{id: base}]
catalog: {items: [{id: catalog-overlay}]}
`)
result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Overlay: "testing"})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(result.Config.Items, []ordinarySequenceItem{{ID: "base"}}) ||
!reflect.DeepEqual(result.Config.Catalog.Items, []ordinarySequenceItem{{ID: "catalog-base"}, {ID: "catalog-overlay"}}) {
t.Fatalf("independent overlay policies = items %#v, catalog %#v", result.Config.Items, result.Config.Catalog.Items)
}
}
func TestLoadOverlaySelectionUsesOneFixedYAMLFile(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "settings: {kept: base}\n")