Activate ordinary source collection declarations
This commit is contained in:
210
internal/engine/ordinary_sequence_test.go
Normal file
210
internal/engine/ordinary_sequence_test.go
Normal file
@@ -0,0 +1,210 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user