211 lines
6.2 KiB
Go
211 lines
6.2 KiB
Go
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)
|
|
}
|
|
}
|