package engine import ( "errors" "fmt" "testing" "testing/fstest" ) func TestNestedCollectionInitializationIsIndependentOfAncestorPresence(t *testing.T) { t.Parallel() for _, ancestor := range []string{"{}", "group: {}", "group: null"} { for _, test := range []struct { mode, key, value string valid bool }{ {mode: "append", value: "[{id: a}]"}, {mode: "upsert", value: "[{id: a}]"}, {mode: "append", value: "[]"}, {mode: "upsert", value: "[]"}, {mode: "append", key: ", key: id", value: "{}"}, {mode: "upsert", key: ", key: id", value: "null"}, {mode: "append", key: ", key: id", value: "[{id: a}]", valid: true}, {mode: "upsert", key: ", key: id", value: "[]", valid: true}, } { t.Run(fmt.Sprintf("%s/%s/%s/%s", ancestor, test.mode, test.key, test.value), func(t *testing.T) { main := "configkit:\n sequences: [{destination: [group, items], mode: " + test.mode + test.key + "}]\ngroup: {items: " + test.value + "}\n" loaded, err := testGroupedLoad(loadRequest[map[string]any]{ main: loadInput{source: Bytes("main", []byte(main))}, underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte(ancestor))}}}}, }) if test.valid { if err != nil || loaded == nil { t.Fatalf("valid initialization failed: %v", err) } return } var loadErr *LoadError if !errors.As(err, &loadErr) || loaded != nil { t.Fatalf("invalid collection operation succeeded: %#v, %v", loaded, err) } diagnostics := loadErr.Diagnostics() if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceInvalid || diagnostics[0].Path.String() != "/group/items" { t.Fatalf("operation diagnostics = %#v", diagnostics) } }) } } } func TestAddedAncestorTransfersNestedRecordPolicies(t *testing.T) { t.Parallel() type record struct { ID string `yaml:"id"` Steps []map[string]any `yaml:"steps"` } type config struct { Group struct { Items []record `yaml:"items"` } `yaml:"group"` } files := fstest.MapFS{ "main.yml": {Data: []byte(`configkit: sequences: [{destination: [group, items], key: id}] imports: [{path: base.yml, destination: [group, items], placement: sequence-item}] group: items: [{id: child, extends: {id: base}, steps: [{name: child}]}] `)}, "base.yml": {Data: []byte("configkit:\n sequences: [{destination: [steps], key: name}]\nid: base\nsteps: [{name: base}]\n")}, } loaded, err := testGroupedLoad(loadRequest[config]{ main: loadInput{source: NewFilesystem("catalog", files).File("main.yml"), imports: true}, underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte("{}"))}}}}, }) if err != nil { t.Fatal(err) } for _, item := range loaded.Config.Group.Items { if item.ID == "child" { if len(item.Steps) != 2 || item.Steps[0]["name"] != "base" || item.Steps[1]["name"] != "child" { t.Fatalf("record-local identity was lost when adding its ancestor: %#v", item.Steps) } return } } t.Fatal("missing child record") } func TestInheritanceChecksCollectionOperationUnderAddedAncestor(t *testing.T) { t.Parallel() _, err := testLoad[map[string]any](Bytes("inheritance", []byte(`configkit: sequences: [{destination: [child, group, items], mode: append}] base: {} child: extends: base group: {items: [{id: a}]} `))) var loadErr *LoadError if !errors.As(err, &loadErr) { t.Fatalf("missing nested operation error: %v", err) } diagnostics := loadErr.Diagnostics() if len(diagnostics) != 1 || diagnostics[0].Code != CodeSequenceInvalid || diagnostics[0].Path.String() != "/child/group/items" { t.Fatalf("nested inheritance operation diagnostics = %#v", diagnostics) } }