Migrate structural and inspection fixtures

This commit is contained in:
2026-11-02 00:42:41 +00:00
parent 54bdc24865
commit eb87a10e1c
23 changed files with 140 additions and 81 deletions

View File

@@ -200,6 +200,8 @@ Resource tests; defer unrelated stale structural fixtures to Stage 6.
## Stage 6 — Migrate engine structural and inspection fixtures
**Status:** Complete. The engine suite passes, including composition, inheritance, and source-evidence regressions.
**Scope:** remaining engine composition, collection, inheritance, lifecycle,
history, provenance, explanation, sensitivity, and rendering tests/benchmarks.

View File

@@ -7,6 +7,28 @@ import (
"testing/fstest"
)
func TestNestedConfigDataSurvivesCompositionAndPrecedence(t *testing.T) {
loaded, err := testGroupedLoad(loadRequest[map[string]any]{
main: loadInput{source: Bytes("main", []byte("service: {config: {enabled: false, count: 0}}\n"))},
underlays: []loadGroup{{inputs: []loadInput{
{source: Bytes("left", []byte("service: {config: {enabled: true, empty: ''}}\n"))},
{source: Bytes("right", []byte("service: {config: {count: 3, optional: null}}\n"))},
}}},
})
if err != nil {
t.Fatal(err)
}
values := loaded.Config["service"].(map[string]any)["config"].(map[string]any)
if values["enabled"] != false || values["count"] != int64(0) || values["empty"] != "" || values["optional"] != nil {
t.Fatalf("composed data = %#v", values)
}
for _, key := range []string{"enabled", "count", "empty", "optional"} {
if info, ok := loaded.Metadata().Lookup(Keys("service", "config", key)); !ok || !info.Explicit {
t.Errorf("presence for %s = %#v, %t", key, info, ok)
}
}
}
func TestNestedCollectionInitializationIsIndependentOfAncestorPresence(t *testing.T) {
t.Parallel()
for _, ancestor := range []string{"{}", "group: {}", "group: null"} {
@@ -24,7 +46,7 @@ func TestNestedCollectionInitializationIsIndependentOfAncestorPresence(t *testin
{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"
main := "config:\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))}}}},
@@ -60,13 +82,13 @@ func TestAddedAncestorTransfersNestedRecordPolicies(t *testing.T) {
} `yaml:"group"`
}
files := fstest.MapFS{
"main.yml": {Data: []byte(`configkit:
"main.yml": {Data: []byte(`config:
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")},
"base.yml": {Data: []byte("config:\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},
@@ -88,7 +110,7 @@ group:
func TestInheritanceChecksCollectionOperationUnderAddedAncestor(t *testing.T) {
t.Parallel()
_, err := testLoad[map[string]any](Bytes("inheritance", []byte(`configkit:
_, err := testLoad[map[string]any](Bytes("inheritance", []byte(`config:
sequences: [{destination: [child, group, items], mode: append}]
base: {}
child:

View File

@@ -6,7 +6,7 @@ import (
)
func TestResolveCollectionPolicyReconcilesDeclarationsBeforeComposition(t *testing.T) {
declarations := extractSequenceDeclarationsYAML(t, `configkit:
declarations := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
key: [namespace, id]
@@ -40,7 +40,7 @@ func TestResolveCollectionPolicyRejectsConflictingPeersAtBothSites(t *testing.T)
}{
{
name: "identity fields",
body: `configkit:
body: `config:
sequences:
- destination: [prompts]
key: id
@@ -50,7 +50,7 @@ func TestResolveCollectionPolicyRejectsConflictingPeersAtBothSites(t *testing.T)
},
{
name: "explicit modes",
body: `configkit:
body: `config:
sequences:
- destination: [prompts]
mode: append
@@ -85,7 +85,7 @@ func TestResolveCollectionPolicySelectsCurrentThenRetainedExplicitMode(t *testin
explicitModePresent: true,
modeLocation: &Location{SourceID: "lower", Line: 2},
}
current := extractSequenceDeclarationsYAML(t, `configkit:
current := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
mode: upsert
@@ -105,7 +105,7 @@ func TestResolveCollectionPolicySelectsCurrentThenRetainedExplicitMode(t *testin
}
func TestResolveCollectionPolicyInfersWithoutPersistingMode(t *testing.T) {
keyOnly := extractSequenceDeclarationsYAML(t, `configkit:
keyOnly := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
key: id
@@ -120,7 +120,7 @@ func TestResolveCollectionPolicyInfersWithoutPersistingMode(t *testing.T) {
t.Fatalf("application identity was not kept distinct from YAML policy: %#v, %#v", resolved, diagnostics)
}
noIdentity := extractSequenceDeclarationsYAML(t, `configkit:
noIdentity := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
`, rootDirectiveContextSource).sequences
@@ -129,7 +129,7 @@ func TestResolveCollectionPolicyInfersWithoutPersistingMode(t *testing.T) {
t.Fatalf("unkeyed mode inference = %#v, %#v", resolved, diagnostics)
}
explicitDefault := extractSequenceDeclarationsYAML(t, `configkit:
explicitDefault := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
mode: replace
@@ -141,7 +141,7 @@ func TestResolveCollectionPolicyInfersWithoutPersistingMode(t *testing.T) {
}
func TestResolveCollectionPolicyChecksKeyOnlyAgainstApplicationConstraint(t *testing.T) {
declarations := extractSequenceDeclarationsYAML(t, `configkit:
declarations := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
key: [namespace, id]
@@ -169,7 +169,7 @@ func TestResolveCollectionPolicyChecksRetainedIdentityAgainstApplicationConstrai
}
func TestResolveCollectionPolicyRequiresCurrentIdentityToAgreeWithRetainedIdentity(t *testing.T) {
declarations := extractSequenceDeclarationsYAML(t, `configkit:
declarations := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
key: name

View File

@@ -12,8 +12,8 @@ func TestSourceReplacementRetiresIdentityForEveryValueKind(t *testing.T) {
t.Parallel()
for _, value := range []string{"null", "42", "{}", "[{name: new}]"} {
t.Run(value, func(t *testing.T) {
lower := "configkit:\n sequences: [{destination: [items], key: id}]\nitems: [{id: old}]\n"
main := "configkit:\n sequences: [{destination: [items], key: name, mode: replace}]\nitems: " + value + "\n"
lower := "config:\n sequences: [{destination: [items], key: id}]\nitems: [{id: old}]\n"
main := "config:\n sequences: [{destination: [items], key: name, mode: replace}]\nitems: " + value + "\n"
loaded, err := testGroupedLoad(loadRequest[map[string]any]{
main: loadInput{source: Bytes("main", []byte(main))},
underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte(lower))}}}},
@@ -36,7 +36,7 @@ func TestAncestorRemovalCanEstablishPendingReplacementIdentity(t *testing.T) {
var loaded *testResult[map[string]any]
var err error
if inheritance {
loaded, err = testLoad[map[string]any](Bytes("source", []byte(`configkit:
loaded, err = testLoad[map[string]any](Bytes("source", []byte(`config:
sequences:
- {destination: [base, group, items], key: id}
- {destination: [child, group, items], key: name}
@@ -52,8 +52,8 @@ grandchild:
loaded, err = testGroupedLoad(loadRequest[map[string]any]{
main: loadInput{source: Bytes("main", []byte("group: {items: [{name: new}]}\n"))},
underlays: []loadGroup{
{inputs: []loadInput{{source: Bytes("base", []byte("configkit:\n sequences: [{destination: [group, items], key: id}]\ngroup: {items: [{id: old}]}\n"))}}},
{inputs: []loadInput{{source: Bytes("remove", []byte("configkit:\n sequences: [{destination: [group, items], key: name}]\ngroup: null\n"))}}},
{inputs: []loadInput{{source: Bytes("base", []byte("config:\n sequences: [{destination: [group, items], key: id}]\ngroup: {items: [{id: old}]}\n"))}}},
{inputs: []loadInput{{source: Bytes("remove", []byte("config:\n sequences: [{destination: [group, items], key: name}]\ngroup: null\n"))}}},
},
})
}
@@ -74,7 +74,7 @@ grandchild:
func TestInheritanceAncestorRetirementDoesNotResurrectOldIdentity(t *testing.T) {
t.Parallel()
_, err := testLoad[map[string]any](Bytes("source", []byte(`configkit:
_, err := testLoad[map[string]any](Bytes("source", []byte(`config:
sequences: [{destination: [base, group, items], key: id}]
base: {group: {items: [{id: old}]}}
child:
@@ -92,7 +92,7 @@ grandchild:
func TestReplacementRetirementPreservesApplicationConstraintsAndRetainedKeys(t *testing.T) {
t.Parallel()
for _, withValue := range []bool{false, true} {
main := "configkit:\n sequences: [{destination: [items], key: name, mode: replace}]\n"
main := "config:\n sequences: [{destination: [items], key: name, mode: replace}]\n"
if withValue {
main += "items: null\n"
}
@@ -104,7 +104,7 @@ func TestReplacementRetirementPreservesApplicationConstraintsAndRetainedKeys(t *
}
_, err := testGroupedLoad(loadRequest[map[string]any]{
main: loadInput{source: Bytes("main", []byte(main))}, definition: definition,
underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte("configkit:\n sequences: [{destination: [items], key: id}]\nitems: [{id: old}]\n"))}}}},
underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte("config:\n sequences: [{destination: [items], key: id}]\nitems: [{id: old}]\n"))}}}},
})
var loadErr *LoadError
if !errors.As(err, &loadErr) || len(loadErr.Diagnostics()) == 0 || loadErr.Diagnostics()[0].Code != CodeSequenceConflict {

View File

@@ -24,7 +24,7 @@ type explanationConcurrency struct {
func TestExplainShowsDefaultBaseAndEffectiveOverlayValue(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "config.yml", "configkit:\n imports: [{path: transcription.yml}]\n")
mainPath := writeImportFile(t, directory, "config.yml", "config:\n imports: [{path: transcription.yml}]\n")
writeImportFile(t, directory, "transcription.yml", "transcription:\n concurrency: 8\n")
writeImportFile(t, directory, filepath.Join("overlays", "testing.yml"), "transcription:\n concurrency: 1\n")
result, err := testLoad[explanationConcurrency](File(mainPath), Options[explanationConcurrency]{

View File

@@ -235,7 +235,7 @@ func TestLayeredUpsertExplanationFollowsStructuralOrder(t *testing.T) {
Records []map[string]any `yaml:"records"`
}
loaded, err := testGroupedLoad(loadRequest[config]{
main: loadInput{source: Bytes("main", []byte("configkit:\n sequences: [{destination: [records], key: id}]\nrecords: [{id: b, value: new-b}, {id: c, value: new-c}, {id: a, value: new-a}]\n"))},
main: loadInput{source: Bytes("main", []byte("config:\n sequences: [{destination: [records], key: id}]\nrecords: [{id: b, value: new-b}, {id: c, value: new-c}, {id: a, value: new-a}]\n"))},
underlays: []loadGroup{{inputs: []loadInput{{source: Bytes("lower", []byte("records: [{id: a, value: old-a}, {id: b, value: old-b, removed: old}]\n"))}}}},
})
if err != nil {

View File

@@ -8,6 +8,27 @@ import (
"testing"
)
func TestControlEvidenceAndApplicationValuesSharePathText(t *testing.T) {
directory := t.TempDir()
main := writeImportFile(t, directory, "main.yml", "config: {imports: [{path: missing.yml}, {path: data.yml, destination: [config]}]}\n")
writeImportFile(t, directory, "data.yml", "imports: [ordinary]\n")
loaded, err := testLoad[map[string]any](File(main), Options[map[string]any]{Imports: true})
if err != nil {
t.Fatal(err)
}
sources := loaded.Sources()
if len(sources) != 2 || len(sources[0].importSelections) != 1 || sources[0].importSelections[0].path.String() != "/config/imports/0" || sources[0].importSelections[0].location.SourceID != sources[0].ID {
t.Fatalf("source control evidence = %#v", sources)
}
info, ok := loaded.Metadata().Lookup(Keys("config", "imports").Index(0))
if !ok || len(info.Origin.Locations) == 0 || info.Origin.Locations[0].SourceID != sources[1].ID {
t.Fatalf("application value attribution = %#v, %t", info, ok)
}
if got := loaded.Config["config"].(map[string]any)["imports"].([]any)[0]; got != "ordinary" {
t.Fatalf("application value = %#v", got)
}
}
func TestBroadEmptyImportSelectionsKeepDeclaringSourceOwnership(t *testing.T) {
t.Parallel()

View File

@@ -76,7 +76,7 @@ func TestInheritanceReplacementResetsOnlyReplacedYamlIdentity(t *testing.T) {
func TestLoadInheritanceReplacementUsesNewYamlKey(t *testing.T) {
t.Parallel()
loaded, err := testLoad[map[string]any](Bytes("replacement", []byte(`configkit:
loaded, err := testLoad[map[string]any](Bytes("replacement", []byte(`config:
sequences:
- {destination: [base, items], key: id}
- {destination: [child, items], key: name, mode: replace}
@@ -150,7 +150,7 @@ child:
}
func TestLoadAppliesYamlCollectionIdentityDuringSiblingInheritance(t *testing.T) {
input := `configkit:
input := `config:
sequences:
- destination: [base, items]
key: id

View File

@@ -16,7 +16,7 @@ func TestRecordInheritanceHistoryPreservesChildIdentity(t *testing.T) {
key, reference = "[id, tenant]", "{id: base, tenant: parent-team}"
identities["tenant"] = "child-team"
}
body := "configkit:\n sequences: [{destination: [items], key: " + key + "}]\nitems:\n - id: base\n tenant: parent-team\n value: inherited\n - id: child\n tenant: child-team\n extends: " + reference + "\n"
body := "config:\n sequences: [{destination: [items], key: " + key + "}]\nitems:\n - id: base\n tenant: parent-team\n value: inherited\n - id: child\n tenant: child-team\n extends: " + reference + "\n"
loaded, err := testLoad(Bytes("records", []byte(body)), Options[map[string]any]{})
if err != nil {
t.Fatal(err)

View File

@@ -76,7 +76,7 @@ func TestInheritancePolicyIndexReplacesOnlyTheChangedScope(t *testing.T) {
func TestInheritanceWideIndependentYamlCollectionScopes(t *testing.T) {
var body strings.Builder
body.WriteString("configkit:\n sequences:\n")
body.WriteString("config:\n sequences:\n")
for index := range 64 {
fmt.Fprintf(&body, " - {destination: [groups, group-%03d, base, items], key: id, mode: append}\n", index)
}

View File

@@ -71,6 +71,21 @@ func TestSiblingInheritanceResolvesReferencesAndKeepsInputs(t *testing.T) {
}
}
func TestSiblingInheritancePreservesNestedConfigData(t *testing.T) {
input := parseCompositionDocument(t, "source-1", "parent: {config: {enabled: false, count: 0, empty: '', optional: null}}\nchild: {extends: parent}\n")
result, diagnostics := expandSiblingDefinitions(input, Path{})
if len(diagnostics) != 0 {
t.Fatal(diagnostics)
}
for _, key := range []string{"enabled", "count", "empty", "optional"} {
parent, ok := documentAtPath(result.root, Keys("parent", "config", key))
child, found := documentAtPath(result.root, Keys("child", "config", key))
if !ok || !found || parent.Kind != child.Kind || parent.Text != child.Text {
t.Fatalf("inherited %s = %#v, parent = %#v", key, child, parent)
}
}
}
func TestSiblingInheritanceTracksTransferAndIndependentChildren(t *testing.T) {
input := parseCompositionDocument(t, "source-1", "first: {value: parent}\nsecond: {extends: first}\nthird: {extends: first}\n")
result, diagnostics := expandSiblingDefinitions(input, Keys("catalog"))

View File

@@ -584,7 +584,7 @@ func TestLoadRejectsUnavailableOptionsAndDisabledDirectives(t *testing.T) {
})
}
result, err := testLoad[config](Bytes("directive.yml", []byte("nested:\n configkit: {}\n")))
result, err := testLoad[config](Bytes("directive.yml", []byte("config: {imports: []}\n")))
if result != nil || err == nil {
t.Fatalf("result, err = %#v, %v", result, err)
}
@@ -593,7 +593,7 @@ func TestLoadRejectsUnavailableOptionsAndDisabledDirectives(t *testing.T) {
t.Fatal(err)
}
diagnostic := loadError.Diagnostics()[0]
if diagnostic.Code != CodeImportDisabled || diagnostic.Path.String() != "/nested/configkit" || diagnostic.Primary == nil || diagnostic.Primary.Line != 2 {
if diagnostic.Code != CodeImportDisabled || diagnostic.Path.String() != "/config" || diagnostic.Primary == nil || diagnostic.Primary.Line != 1 {
t.Fatalf("diagnostic = %#v", diagnostic)
}
}

View File

@@ -129,7 +129,7 @@ func TestHistoricalPathsExposeHiddenBaseOwnershipWithoutResolvingBaseSecrets(t *
for _, credential := range []string{"CONFIGKIT_HISTORY_TEST_KEY", "null"} {
t.Run(credential, func(t *testing.T) {
dir := t.TempDir()
root := writeImportFile(t, dir, "main.yml", "configkit:\n imports: [{path: first.yml}, {path: second.yml}]\ncredential: CONFIGKIT_HISTORY_UNUSED_PROD_KEY\n")
root := writeImportFile(t, dir, "main.yml", "config:\n imports: [{path: first.yml}, {path: second.yml}]\ncredential: CONFIGKIT_HISTORY_UNUSED_PROD_KEY\n")
writeImportFile(t, dir, "first.yml", "definitions: {recap: {prompt: recap}}\n")
writeImportFile(t, dir, "second.yml", "definitions: {recap: {output: recap.md}}\n")
writeImportFile(t, dir, "overlays/testing.yml", "definitions: null\ncredential: "+credential+"\n")

View File

@@ -25,7 +25,7 @@ type ordinarySequenceConfig struct {
}
func TestOrdinaryLoadAcceptsDeclarationsWithoutImports(t *testing.T) {
result, err := testLoad[ordinarySequenceConfig](Bytes("catalog.yml", []byte(`configkit:
result, err := testLoad[ordinarySequenceConfig](Bytes("catalog.yml", []byte(`config:
sequences:
- destination: [items]
key: id
@@ -48,7 +48,7 @@ items:
}
func TestOrdinaryLoadKeepsDisabledImportRejection(t *testing.T) {
_, err := testLoad[ordinarySequenceConfig](Bytes("disabled.yml", []byte(`configkit:
_, err := testLoad[ordinarySequenceConfig](Bytes("disabled.yml", []byte(`config:
imports: [{path: fragment.yml}]
sequences: [{destination: [items], key: id}]
`)))
@@ -62,7 +62,7 @@ func TestOrdinaryLoadKeepsDisabledImportRejection(t *testing.T) {
}
func TestOrdinaryLoadValidatesSingletonCollectionIdentity(t *testing.T) {
_, err := testLoad[ordinarySequenceConfig](Bytes("missing-identity.yml", []byte(`configkit:
_, err := testLoad[ordinarySequenceConfig](Bytes("missing-identity.yml", []byte(`config:
sequences:
- destination: [items]
key: id
@@ -90,7 +90,7 @@ func TestOrdinaryLoadCombinesImportedCollectionPeers(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
mainPath := writeImportFile(t, directory, "main.yml", `config:
imports:
- path: first.yml
- path: second.yml
@@ -123,7 +123,7 @@ func TestOrdinaryLoadCombinesImportedCollectionPeers(t *testing.T) {
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:
inline, err := testLoad[ordinarySequenceConfig](Bytes("inline.yml", []byte(`config:
sequences:
- destination: [items]
key: id
@@ -142,14 +142,14 @@ items: [{id: first}, {id: second}]
func TestImportedCollectionDeclarationsUseRelativePlacement(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
mainPath := writeImportFile(t, directory, "main.yml", `config:
imports:
- path: first.yml
destination: [catalog]
- path: second.yml
destination: [catalog]
`)
fragment := `configkit:
fragment := `config:
sequences:
- destination: [items]
key: id
@@ -171,13 +171,13 @@ items: [{id: ITEM}]
func TestSequenceItemSourceAppliesItsCollectionDeclarations(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
mainPath := writeImportFile(t, directory, "main.yml", `config:
imports:
- path: item.yml
destination: [records]
placement: sequence-item
`)
writeImportFile(t, directory, "item.yml", `configkit:
writeImportFile(t, directory, "item.yml", `config:
sequences:
- destination: [children]
key: id
@@ -196,7 +196,7 @@ children: [{id: child}]
}
func TestOrdinaryLoadAllowsDeclarationsWithoutValues(t *testing.T) {
result, err := testLoad[ordinarySequenceConfig](Bytes("policy.yml", []byte(`configkit:
result, err := testLoad[ordinarySequenceConfig](Bytes("policy.yml", []byte(`config:
sequences:
- destination: [items]
key: id

View File

@@ -22,7 +22,7 @@ func TestCLIHistoryFollowsInheritanceAndPrecedesResolution(t *testing.T) {
}
override := Override{Path: query, Value: cliValue, Flag: "--value"}
if test.records {
body = "configkit:\n sequences: [{destination: [items], key: id}]\nitems: [{id: base, value: parent}, {id: child, extends: {id: base}}]\n"
body = "config:\n sequences: [{destination: [items], key: id}]\nitems: [{id: base, value: parent}, {id: child, extends: {id: base}}]\n"
query, parent = Keys("items").Index(1).Key("value"), Keys("items").Index(0).Key("value")
if test.equal {
cliValue = "parent"

View File

@@ -178,7 +178,7 @@ func TestCLISequenceAssignmentReplacesAppendAndUpsertResults(t *testing.T) {
Items []item `yaml:"items"`
}
declaration := func(mode string, id string) Source {
return Bytes(id, []byte("configkit:\n sequences:\n - destination: [items]\n key: id\n mode: "+mode+"\nitems: [{id: "+id+"}]\n"))
return Bytes(id, []byte("config:\n sequences:\n - destination: [items]\n key: id\n mode: "+mode+"\nitems: [{id: "+id+"}]\n"))
}
result, err := testGroupedLoad(loadRequest[config]{
main: loadInput{source: declaration("upsert", "main")},

View File

@@ -30,7 +30,7 @@ type overrideLifecycleConfig struct {
func TestOverridesRunAtHighestInputPrecedenceBeforeResolutionAndValidation(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "configkit:\n imports: [{path: base.yml}]\nversion: v1\n")
mainPath := writeImportFile(t, directory, "main.yml", "config:\n imports: [{path: base.yml}]\nversion: v1\n")
writeImportFile(t, directory, "base.yml", `settings:
target: base
count: 4

View File

@@ -101,7 +101,7 @@ func TestNoSourceSelectedOverlayUsesCompleteNativeContext(t *testing.T) {
func TestLoadAppliesSelectedOverlayAfterBaseAndBeforeDefaults(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
mainPath := writeImportFile(t, directory, "main.yml", `config:
imports: [{path: base.yml}]
settings:
kept: main
@@ -191,7 +191,7 @@ func TestLoadSelectedOverlayAppliesCollectionOperationsAndEvidence(t *testing.T)
{
name: "append",
mode: "append",
overlay: `configkit:
overlay: `config:
sequences:
- destination: [items]
key: id
@@ -203,7 +203,7 @@ items: [{id: c, value: added}]
{
name: "upsert replaces complete records at retained positions",
mode: "upsert",
overlay: `configkit:
overlay: `config:
sequences:
- destination: [items]
key: id
@@ -215,7 +215,7 @@ items: [{id: a, value: new}]
{
name: "replace",
mode: "replace",
overlay: `configkit:
overlay: `config:
sequences:
- destination: [items]
key: id
@@ -283,7 +283,7 @@ func TestLoadSelectedOverlayReconcilesPendingAndRetainedCollectionPolicy(t *test
}
}
t.Run("key only validates retained records", func(t *testing.T) {
requireCode(t, "items: [{id: duplicate}, {id: duplicate}]\n", `configkit:
requireCode(t, "items: [{id: duplicate}, {id: duplicate}]\n", `config:
sequences:
- destination: [items]
key: id
@@ -292,14 +292,14 @@ func TestLoadSelectedOverlayReconcilesPendingAndRetainedCollectionPolicy(t *test
t.Run("mode only retains lower identity", func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
mainPath := writeImportFile(t, directory, "main.yml", `config:
sequences:
- destination: [items]
key: id
mode: append
items: [{id: base}]
`)
writeImportFile(t, directory, "overlays/testing.yml", `configkit:
writeImportFile(t, directory, "overlays/testing.yml", `config:
sequences:
- destination: [items]
mode: append
@@ -316,7 +316,7 @@ items: [{id: overlay}]
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:
mainPath := writeImportFile(t, directory, "main.yml", `config:
sequences:
- destination: [items]
key: id
@@ -331,12 +331,12 @@ items: [{id: base}]
})
t.Run("overlay identity must agree with retained identity", func(t *testing.T) {
requireCode(t, `configkit:
requireCode(t, `config:
sequences:
- destination: [items]
key: id
items: [{id: base}]
`, `configkit:
`, `config:
sequences:
- destination: [items]
key: slug
@@ -352,7 +352,7 @@ items: [{id: overlay}]
{mode: "replace"},
{mode: "append", want: CodeSequenceInvalid},
} {
overlay := "configkit:\n sequences:\n - destination: [items]\n key: id\n mode: " + test.mode + "\nitems: null\n"
overlay := "config:\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
@@ -379,7 +379,7 @@ items: [{id: overlay}]
{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"
overlay := "config:\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
@@ -399,7 +399,7 @@ items: [{id: overlay}]
func TestLoadSelectedOverlayKeepsCollectionPoliciesIndependent(t *testing.T) {
t.Parallel()
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", `configkit:
mainPath := writeImportFile(t, directory, "main.yml", `config:
sequences:
- destination: [items]
key: id
@@ -410,7 +410,7 @@ func TestLoadSelectedOverlayKeepsCollectionPoliciesIndependent(t *testing.T) {
items: [{id: base}]
catalog: {items: [{id: catalog-base}]}
`)
writeImportFile(t, directory, "overlays/testing.yml", `configkit:
writeImportFile(t, directory, "overlays/testing.yml", `config:
sequences:
- destination: [items]
mode: upsert
@@ -477,8 +477,7 @@ func TestLoadOverlayRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
overlay string
code Code
}{
{name: "root directive", overlay: "configkit: {}\n", code: CodeOverlayDirective},
{name: "nested directive", overlay: "settings: {configkit: {}}\n", code: CodeOverlayDirective},
{name: "root directive", overlay: "config: {}\n", code: CodeOverlayDirective},
{name: "equal integer version", overlay: "schema: {version: 4}\n", code: CodeOverlayVersion},
{name: "ancestor scalar", overlay: "schema: null\n", code: CodeOverlayVersion},
} {
@@ -502,7 +501,7 @@ func TestLoadOverlayRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
t.Run("overlay aliases imported base", func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: overlays/testing.yml}]}\n")
mainPath := writeImportFile(t, directory, "main.yml", "config: {imports: [{path: overlays/testing.yml}]}\n")
writeImportFile(t, directory, "overlays/testing.yml", "settings: {changed: value}\n")
requireOverlayLoadCode(t, File(mainPath), Options[overlayTestConfig]{Imports: true, Overlay: "testing"}, CodeOverlayRepeated)
})

View File

@@ -177,7 +177,7 @@ func TestCombineSequenceReplacementResetsLowerIdentity(t *testing.T) {
}
func TestCombineSequenceDifferentIdentityDeclarationsConflict(t *testing.T) {
declarations := extractSequenceDeclarationsYAML(t, `configkit:
declarations := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [items]
key: id
@@ -256,7 +256,7 @@ func TestCombineSequenceClonesInputsAndDoesNotMutatePolicy(t *testing.T) {
func TestCombineSequenceEquivalentIdentityFieldSets(t *testing.T) {
lower := sequenceFromYAML(t, "items: [{id: a, namespace: one}]\n")
higher := sequenceFromYAML(t, "items: [{namespace: one, id: b}]\n")
policy, diagnostics := resolveCollectionPolicy(Keys("items"), extractSequenceDeclarationsYAML(t, `configkit:
policy, diagnostics := resolveCollectionPolicy(Keys("items"), extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [items]
key: [namespace, id, id]

View File

@@ -23,12 +23,12 @@ func TestParseSequenceDeclarationPresenceAndRelativeDestination(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
extracted := extractSequenceDeclarationsYAML(t, "configkit:\n sequences:\n - "+test.declaration+"\n", rootDirectiveContextSource)
extracted := extractSequenceDeclarationsYAML(t, "config:\n sequences:\n - "+test.declaration+"\n", rootDirectiveContextSource)
if len(extracted.sequences) != 1 {
t.Fatalf("extraction = %#v", extracted)
}
declaration := extracted.sequences[0]
if declaration.destination.String() != "/prompts" || declaration.path.String() != "/configkit/sequences/0" {
if declaration.destination.String() != "/prompts" || declaration.path.String() != "/config/sequences/0" {
t.Fatalf("relative destination or declaration path changed: %#v", declaration)
}
if declaration.location.SourceID != "sequence-declarations" || declaration.location.Line == 0 {
@@ -51,7 +51,7 @@ func TestParseSequenceDeclarationPresenceAndRelativeDestination(t *testing.T) {
}
func TestParseSequenceDeclarationNormalizesEquivalentFieldSets(t *testing.T) {
extracted := extractSequenceDeclarationsYAML(t, `configkit:
extracted := extractSequenceDeclarationsYAML(t, `config:
sequences:
- destination: [prompts]
key: [namespace, id, id]
@@ -100,17 +100,17 @@ func TestParseSequenceDeclarationRejectsMalformedForms(t *testing.T) {
body string
path string
}{
{name: "sequences wrong type", body: "configkit: {sequences: nope}\n", path: "/configkit/sequences"},
{name: "declaration wrong type", body: "configkit: {sequences: [nope]}\n", path: "/configkit/sequences/0"},
{name: "missing destination", body: "configkit: {sequences: [{key: id}]}\n", path: "/configkit/sequences/0/destination"},
{name: "destination wrong type", body: "configkit: {sequences: [{destination: nope}]}\n", path: "/configkit/sequences/0/destination"},
{name: "reserved destination", body: "configkit: {sequences: [{destination: [configkit]}]}\n", path: "/configkit/sequences/0/destination/0"},
{name: "key wrong type", body: "configkit: {sequences: [{destination: [prompts], key: true}]}\n", path: "/configkit/sequences/0/key"},
{name: "empty key set", body: "configkit: {sequences: [{destination: [prompts], key: []}]}\n", path: "/configkit/sequences/0/key"},
{name: "key member wrong type", body: "configkit: {sequences: [{destination: [prompts], key: [id, null]}]}\n", path: "/configkit/sequences/0/key/1"},
{name: "mode wrong type", body: "configkit: {sequences: [{destination: [prompts], mode: true}]}\n", path: "/configkit/sequences/0/mode"},
{name: "unknown mode", body: "configkit: {sequences: [{destination: [prompts], mode: merge}]}\n", path: "/configkit/sequences/0/mode"},
{name: "unknown declaration field", body: "configkit: {sequences: [{destination: [prompts], append: true}]}\n", path: "/configkit/sequences/0/append"},
{name: "sequences wrong type", body: "config: {sequences: nope}\n", path: "/config/sequences"},
{name: "declaration wrong type", body: "config: {sequences: [nope]}\n", path: "/config/sequences/0"},
{name: "missing destination", body: "config: {sequences: [{key: id}]}\n", path: "/config/sequences/0/destination"},
{name: "destination wrong type", body: "config: {sequences: [{destination: nope}]}\n", path: "/config/sequences/0/destination"},
{name: "reserved destination", body: "config: {sequences: [{destination: [extends]}]}\n", path: "/config/sequences/0/destination/0"},
{name: "key wrong type", body: "config: {sequences: [{destination: [prompts], key: true}]}\n", path: "/config/sequences/0/key"},
{name: "empty key set", body: "config: {sequences: [{destination: [prompts], key: []}]}\n", path: "/config/sequences/0/key"},
{name: "key member wrong type", body: "config: {sequences: [{destination: [prompts], key: [id, null]}]}\n", path: "/config/sequences/0/key/1"},
{name: "mode wrong type", body: "config: {sequences: [{destination: [prompts], mode: true}]}\n", path: "/config/sequences/0/mode"},
{name: "unknown mode", body: "config: {sequences: [{destination: [prompts], mode: merge}]}\n", path: "/config/sequences/0/mode"},
{name: "unknown declaration field", body: "config: {sequences: [{destination: [prompts], append: true}]}\n", path: "/config/sequences/0/append"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {

View File

@@ -17,10 +17,10 @@ func TestLayeredSequenceProjectsNestedSourceDestinations(t *testing.T) {
t.Run(fmt.Sprintf("%s/underlay=%t", mode, underlay), func(t *testing.T) {
files := fstest.MapFS{}
for _, id := range []string{"a", "b", "c"} {
files[id+"/record.yml"] = &fstest.MapFile{Data: []byte("configkit: {imports: [{path: helper.yml}]}\nid: " + id + "\n")}
files[id+"/record.yml"] = &fstest.MapFile{Data: []byte("config: {imports: [{path: helper.yml}]}\nid: " + id + "\n")}
files[id+"/helper.yml"] = &fstest.MapFile{Data: []byte("value: " + id + "\n")}
}
files["main.yml"] = &fstest.MapFile{Data: []byte("configkit:\n sequences: [{destination: [items], key: id, mode: " + mode + "}]\n imports: [{glob: '*/record.yml', destination: [items], placement: sequence-item}]\n")}
files["main.yml"] = &fstest.MapFile{Data: []byte("config:\n sequences: [{destination: [items], key: id, mode: " + mode + "}]\n imports: [{glob: '*/record.yml', destination: [items], placement: sequence-item}]\n")}
lower := "items: [{id: b}, {id: a}, {id: keep}]\n"
want := map[string]int{"a": 1, "b": 0, "c": 3}
if mode == "append" {

View File

@@ -34,11 +34,11 @@ func TestSequenceUpsertProjectsIncomingOrigins(t *testing.T) {
t.Run(name, func(t *testing.T) {
files := fstest.MapFS{}
for _, id := range []string{"a", "b", "c"} {
files[id+"/record.yml"] = &fstest.MapFile{Data: []byte("configkit: {imports: [{path: helper.yml}]}\nid: " + id + "\ndetails: {label: " + id + "}\n")}
files[id+"/record.yml"] = &fstest.MapFile{Data: []byte("config: {imports: [{path: helper.yml}]}\nid: " + id + "\ndetails: {label: " + id + "}\n")}
files[id+"/helper.yml"] = &fstest.MapFile{Data: []byte("details: {resource: asset.txt}\n")}
files[id+"/asset.txt"] = &fstest.MapFile{Data: []byte(id + " asset")}
}
main := "configkit:\n sequences:\n - destination: " + destination + "\n key: id\n imports:\n - glob: '*/record.yml'\n destination: " + destination + "\n placement: sequence-item\n"
main := "config:\n sequences:\n - destination: " + destination + "\n key: id\n imports:\n - glob: '*/record.yml'\n destination: " + destination + "\n placement: sequence-item\n"
if inheritance {
main += "base:\n items: [{id: b}, {id: a}, {id: keep}]\nchild:\n extends: base\n"
}

View File

@@ -349,7 +349,7 @@ func TestRegisteredSequencePolicyAcrossLoadingLifecycle(t *testing.T) {
t.Run("ordinary imports retain registered composition", func(t *testing.T) {
directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "configkit: {imports: [{path: fragment.yml}]}\nitems: [{id: authored}]\n")
mainPath := writeImportFile(t, directory, "main.yml", "config: {imports: [{path: fragment.yml}]}\nitems: [{id: authored}]\n")
writeImportFile(t, directory, "fragment.yml", "items: [{id: imported}]\n")
result, err := testLoad[config](File(mainPath), Options[config]{Imports: true, Definition: definition})
if err != nil || len(result.Config.Items) != 2 || result.Config.Items[0].ID != "imported" || result.Config.Items[1].ID != "authored" {