package engine import ( "errors" "reflect" "strings" "testing" ) type ordinarySequenceItem struct { ID string `yaml:"id"` } type ordinarySequenceRecord struct { ID string `yaml:"id"` Children []ordinarySequenceItem `yaml:"children"` } type ordinarySequenceConfig struct { Items []ordinarySequenceItem `yaml:"items"` Catalog struct { Items []ordinarySequenceItem `yaml:"items"` } `yaml:"catalog"` Records []ordinarySequenceRecord `yaml:"records"` } func TestOrdinaryLoadAcceptsDeclarationsWithoutImports(t *testing.T) { result, err := testLoad[ordinarySequenceConfig](Bytes("catalog.yml", []byte(`configkit: sequences: - destination: [items] key: id items: - id: prompt `))) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(result.Config.Items, []ordinarySequenceItem{{ID: "prompt"}}) { t.Fatalf("items = %#v", result.Config.Items) } if _, ok := result.Metadata().Lookup(Keys("configkit")); ok { t.Fatal("collection controls reached application metadata") } items, ok := result.Metadata().Lookup(Keys("items")) if !ok || !items.Explicit || len(items.Origin.Locations) != 1 { t.Fatalf("items metadata = %#v, %v", items, ok) } } func TestOrdinaryLoadKeepsDisabledImportRejection(t *testing.T) { _, err := testLoad[ordinarySequenceConfig](Bytes("disabled.yml", []byte(`configkit: imports: [{path: fragment.yml}] sequences: [{destination: [items], key: id}] `))) if err == nil { t.Fatal("source with disabled imports succeeded") } var loadError *LoadError if !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != CodeImportDisabled { t.Fatalf("error = %#v", err) } } func TestOrdinaryLoadValidatesSingletonCollectionIdentity(t *testing.T) { _, err := testLoad[ordinarySequenceConfig](Bytes("missing-identity.yml", []byte(`configkit: sequences: - destination: [items] key: id items: [{}] `))) if err == nil { t.Fatal("singleton sequence without identity succeeded") } var loadError *LoadError if !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != CodeComposeInvalid { t.Fatalf("error = %#v", err) } } func TestOrdinaryLoadCombinesImportedCollectionPeers(t *testing.T) { tests := []struct { name string second string want []ordinarySequenceItem wantCode Code }{ {name: "distinct identities", second: "items: [{id: second}]\n", want: []ordinarySequenceItem{{ID: "first"}, {ID: "second"}}}, {name: "duplicate peer identity", second: "items: [{id: first}]\n", wantCode: CodeComposeConflict}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", `configkit: imports: - path: first.yml - path: second.yml sequences: - destination: [items] key: id mode: append `) writeImportFile(t, directory, "first.yml", "items: [{id: first}]\n") writeImportFile(t, directory, "second.yml", test.second) result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Imports: true}) if test.wantCode != "" { if result != nil || err == nil { t.Fatalf("result, err = %#v, %v", result, err) } var loadError *LoadError if !errors.As(err, &loadError) || len(loadError.Diagnostics()) == 0 || loadError.Diagnostics()[0].Code != test.wantCode { t.Fatalf("error = %#v", err) } return } if err != nil { var loadError *LoadError if errors.As(err, &loadError) { t.Fatalf("diagnostics = %#v", loadError.Diagnostics()) } t.Fatal(err) } if !reflect.DeepEqual(result.Config.Items, test.want) { t.Fatalf("items = %#v, want %#v", result.Config.Items, test.want) } inline, err := testLoad[ordinarySequenceConfig](Bytes("inline.yml", []byte(`configkit: sequences: - destination: [items] key: id mode: append items: [{id: first}, {id: second}] `))) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(result.Config.Items, inline.Config.Items) { t.Fatalf("imported items %#v differ from inline items %#v", result.Config.Items, inline.Config.Items) } }) } } func TestImportedCollectionDeclarationsUseRelativePlacement(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", `configkit: imports: - path: first.yml destination: [catalog] - path: second.yml destination: [catalog] `) fragment := `configkit: sequences: - destination: [items] key: id mode: append items: [{id: ITEM}] ` writeImportFile(t, directory, "first.yml", strings.ReplaceAll(fragment, "ITEM", "first")) writeImportFile(t, directory, "second.yml", strings.ReplaceAll(fragment, "ITEM", "second")) result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Imports: true}) if err != nil { t.Fatal(err) } want := []ordinarySequenceItem{{ID: "first"}, {ID: "second"}} if !reflect.DeepEqual(result.Config.Catalog.Items, want) { t.Fatalf("catalog items = %#v, want %#v", result.Config.Catalog.Items, want) } } func TestSequenceItemSourceAppliesItsCollectionDeclarations(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", `configkit: imports: - path: item.yml destination: [records] placement: sequence-item `) writeImportFile(t, directory, "item.yml", `configkit: sequences: - destination: [children] key: id mode: append id: parent children: [{id: child}] `) result, err := testLoad[ordinarySequenceConfig](File(mainPath), Options[ordinarySequenceConfig]{Imports: true}) if err != nil { t.Fatal(err) } want := []ordinarySequenceRecord{{ID: "parent", Children: []ordinarySequenceItem{{ID: "child"}}}} if !reflect.DeepEqual(result.Config.Records, want) { t.Fatalf("records = %#v, want %#v", result.Config.Records, want) } } func TestOrdinaryLoadAllowsDeclarationsWithoutValues(t *testing.T) { result, err := testLoad[ordinarySequenceConfig](Bytes("policy.yml", []byte(`configkit: sequences: - destination: [items] key: id `))) if err != nil { t.Fatal(err) } if result.Config.Items != nil { t.Fatalf("items = %#v, want absent", result.Config.Items) } }