Exercise document-root controls through the public API
This commit is contained in:
@@ -230,6 +230,8 @@ stage for the complete repository check.
|
||||
|
||||
## Stage 7 — Update facade fixtures and public integration coverage
|
||||
|
||||
**Status:** Complete. The root package suite passes, including external public integration regressions.
|
||||
|
||||
**Scope:** root facade tests and executable package examples.
|
||||
|
||||
1. Migrate root-level test and example inputs that intentionally use controls,
|
||||
|
||||
@@ -26,7 +26,7 @@ func ExampleLoad_sequenceDeclaration() {
|
||||
type Config struct {
|
||||
Items []Item `yaml:"items"`
|
||||
}
|
||||
result, err := configkit.Load[Config](configkit.Bytes("catalog.yml", []byte(`configkit:
|
||||
result, err := configkit.Load[Config](configkit.Bytes("catalog.yml", []byte(`config:
|
||||
sequences:
|
||||
- destination: [items]
|
||||
key: id
|
||||
|
||||
103
facade_config_directives_test.go
Normal file
103
facade_config_directives_test.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package configkit_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
configkit "go.maximumdirect.net/configkit"
|
||||
)
|
||||
|
||||
func TestDocumentRootControlsAndNestedApplicationConfig(t *testing.T) {
|
||||
type settings struct {
|
||||
Imports string `yaml:"imports"`
|
||||
}
|
||||
type app struct {
|
||||
Service struct {
|
||||
Config settings `yaml:"config"`
|
||||
} `yaml:"service"`
|
||||
}
|
||||
result, err := configkit.Load[app](configkit.Bytes("main", []byte("config: {sequences: []}\nservice: {config: {imports: ordinary}}\n")))
|
||||
if err != nil || result.Config.Service.Config.Imports != "ordinary" {
|
||||
t.Fatalf("result = %#v, error = %v", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMountedConfigDataKeepsControlEvidenceAndHiding(t *testing.T) {
|
||||
type settings struct {
|
||||
Imports string `yaml:"imports"`
|
||||
Token string `yaml:"token" configkit:"secret"`
|
||||
}
|
||||
type app struct {
|
||||
Config settings `yaml:"config"`
|
||||
}
|
||||
fs := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"main.yml": {Data: []byte("config: {imports: [{path: missing.yml}, {path: data.yml, destination: [config]}]}\n")},
|
||||
"data.yml": {Data: []byte("imports: ordinary\ntoken: disposable-canary\n")},
|
||||
})
|
||||
result, err := configkit.Load[app](fs.File("main.yml"), configkit.Options[app]{Imports: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.Config.Config.Imports != "ordinary" {
|
||||
t.Fatalf("config = %#v", result.Config)
|
||||
}
|
||||
sources := result.Sources()
|
||||
if len(sources) != 2 || len(sources[0].EmptyImportSelections) != 1 || sources[0].EmptyImportSelections[0].Path.String() != "/config/imports/0" {
|
||||
t.Fatalf("sources = %#v", sources)
|
||||
}
|
||||
if _, ok := result.Metadata().Lookup(configkit.Keys("config", "imports")); !ok {
|
||||
t.Fatal("missing application value")
|
||||
}
|
||||
if _, ok := result.Explain(configkit.Keys("config", "imports")); !ok {
|
||||
t.Fatal("missing explanation")
|
||||
}
|
||||
var output strings.Builder
|
||||
if err := result.WriteEffective(&output); err != nil || strings.Contains(output.String(), "disposable-canary") || !strings.Contains(output.String(), "ordinary") {
|
||||
t.Fatalf("effective output = %q, %v", output.String(), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldControlSpellingsAreOrdinaryData(t *testing.T) {
|
||||
body := "configkit: {imports: [{path: nonexistent.yml}]}\n_configkit: ordinary\n"
|
||||
result, err := configkit.Load[map[string]any](configkit.Bytes("old", []byte(body)))
|
||||
if err != nil || result.Config["configkit"] == nil || result.Config["_configkit"] != "ordinary" {
|
||||
t.Fatalf("result = %#v, error = %v", result, err)
|
||||
}
|
||||
if _, err := configkit.Load[struct{}](configkit.Bytes("strict", []byte(body))); err == nil {
|
||||
t.Fatal("strict schema accepted unknown data")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultsOverridesAndResolutionAtConfigRoot(t *testing.T) {
|
||||
type settings struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Count int `yaml:"count"`
|
||||
}
|
||||
type app struct {
|
||||
Config settings `yaml:"config"`
|
||||
}
|
||||
entryExplicit := false
|
||||
result, err := configkit.Load[app](configkit.NoSource(), configkit.Options[app]{
|
||||
Definition: configkit.Definition[app]{
|
||||
Defaults: func() app { return app{Config: settings{Enabled: true, Count: 2}} },
|
||||
Resolve: func(value *app, r *configkit.Resolution) error {
|
||||
info, ok := r.EntryInfo(configkit.Keys("config", "enabled"))
|
||||
entryExplicit = ok && info.Explicit && info.Origin.Layer == configkit.LayerCLI
|
||||
value.Config.Count = 7
|
||||
r.RecordNormalization(configkit.Keys("config", "count"), "normalize count")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
Overrides: []configkit.Override{{Path: configkit.Keys("config", "enabled"), Value: false, Flag: "--enabled"}},
|
||||
})
|
||||
if err != nil || result == nil || !entryExplicit || result.Config.Config.Enabled || result.Config.Config.Count != 7 {
|
||||
t.Fatalf("result = %#v, entry explicit = %t, error = %v", result, entryExplicit, err)
|
||||
}
|
||||
if info, ok := result.Metadata().Lookup(configkit.Keys("config", "enabled")); !ok || !info.Explicit || info.Origin.Layer != configkit.LayerCLI {
|
||||
t.Fatalf("final enabled info = %#v, %t", info, ok)
|
||||
}
|
||||
if _, ok := result.Explain(configkit.Keys("config", "count")); !ok {
|
||||
t.Fatal("missing resolution explanation")
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func TestFacadeDestinationRootEmptyMappingNoSourceAndInvalidPaths(t *testing.T)
|
||||
wantReads int
|
||||
}{
|
||||
{name: "index segment", source: configkit.Reader("unread", reader), destination: configkit.Keys("catalog").Index(0)},
|
||||
{name: "reserved configkit key", source: configkit.NoSource(), destination: configkit.Keys("configkit")},
|
||||
{name: "reserved extends key", source: configkit.NoSource(), destination: configkit.Keys("extends")},
|
||||
{name: "reserved extends key", source: configkit.NoSource(), destination: configkit.Keys("catalog", "extends")},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
@@ -129,7 +129,7 @@ func TestFacadeImportedSourcesRetainInputDestination(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
mainPath := filepath.Join(directory, "main.yml")
|
||||
fragmentPath := filepath.Join(directory, "fragment.yml")
|
||||
if err := os.WriteFile(mainPath, []byte("configkit: {imports: [{path: fragment.yml}]}\nmain: present\n"), 0o600); err != nil {
|
||||
if err := os.WriteFile(mainPath, []byte("config: {imports: [{path: fragment.yml}]}\nmain: present\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(fragmentPath, []byte("imported: present\n"), 0o600); err != nil {
|
||||
@@ -169,7 +169,7 @@ func TestFacadeRelativeImportDestinationsForRootAndMountedLoads(t *testing.T) {
|
||||
return filename
|
||||
}
|
||||
|
||||
mainPath := write("main.yml", `configkit:
|
||||
mainPath := write("main.yml", `config:
|
||||
imports:
|
||||
- path: metadata.yml
|
||||
destination: [metadata]
|
||||
@@ -184,7 +184,7 @@ root: main
|
||||
write("metadata.yml", "name: imported\n")
|
||||
write("prompts/a.yml", "a: {name: alpha}\n")
|
||||
write("prompts/b.yml", "b: {name: beta}\n")
|
||||
write("entries/main.yml", `configkit:
|
||||
write("entries/main.yml", `config:
|
||||
imports:
|
||||
- path: nested/details.yml
|
||||
destination: [details]
|
||||
@@ -505,7 +505,7 @@ func TestFacadeRelativeImportFailuresRetainSourceRecordsAndCoordinates(t *testin
|
||||
}
|
||||
|
||||
t.Run("imported parse failure retains effective source destination", func(t *testing.T) {
|
||||
mainPath := write("parse/main.yml", "configkit:\n imports:\n - path: child.yml\n destination: [tenant]\n")
|
||||
mainPath := write("parse/main.yml", "config:\n imports:\n - path: child.yml\n destination: [tenant]\n")
|
||||
childPath := write("parse/child.yml", "count: [unterminated\n")
|
||||
_, err := configkit.LoadLayered[map[string]any](configkit.Input{
|
||||
Source: configkit.File(mainPath), Imports: true, Destination: configkit.Keys("mount"),
|
||||
@@ -525,8 +525,8 @@ func TestFacadeRelativeImportFailuresRetainSourceRecordsAndCoordinates(t *testin
|
||||
})
|
||||
|
||||
t.Run("directive failure stays source local", func(t *testing.T) {
|
||||
mainPath := write("directive/main.yml", "configkit:\n imports:\n - path: parent.yml\n destination: [tenant]\n")
|
||||
parentPath := write("directive/parent.yml", "configkit:\n imports:\n - path: details.yml\n destination: [3]\n")
|
||||
mainPath := write("directive/main.yml", "config:\n imports:\n - path: parent.yml\n destination: [tenant]\n")
|
||||
parentPath := write("directive/parent.yml", "config:\n imports:\n - path: details.yml\n destination: [3]\n")
|
||||
_, err := configkit.LoadLayered[map[string]any](configkit.Input{
|
||||
Source: configkit.File(mainPath), Imports: true, Destination: configkit.Keys("mount"),
|
||||
})
|
||||
@@ -535,7 +535,7 @@ func TestFacadeRelativeImportFailuresRetainSourceRecordsAndCoordinates(t *testin
|
||||
t.Fatalf("load error = %T %v", err, err)
|
||||
}
|
||||
diagnostics := loadError.Diagnostics()
|
||||
wantPath := configkit.Keys("configkit", "imports").Index(0).Key("destination").Index(0)
|
||||
wantPath := configkit.Keys("config", "imports").Index(0).Key("destination").Index(0)
|
||||
if len(diagnostics) != 1 || diagnostics[0].Code != configkit.CodeImportInvalid || !reflect.DeepEqual(diagnostics[0].Path, wantPath) || diagnostics[0].Primary == nil || diagnostics[0].Primary.SourceID != "source-2" {
|
||||
t.Fatalf("import directive diagnostic = %#v", diagnostics)
|
||||
}
|
||||
@@ -546,7 +546,7 @@ func TestFacadeRelativeImportFailuresRetainSourceRecordsAndCoordinates(t *testin
|
||||
})
|
||||
|
||||
t.Run("composition failure reports effective path and all sources", func(t *testing.T) {
|
||||
mainPath := write("compose/main.yml", "configkit:\n imports:\n - path: left.yml\n destination: [tenant]\n - path: right.yml\n destination: [tenant]\n")
|
||||
mainPath := write("compose/main.yml", "config:\n imports:\n - path: left.yml\n destination: [tenant]\n - path: right.yml\n destination: [tenant]\n")
|
||||
write("compose/left.yml", "value: same\n")
|
||||
write("compose/right.yml", "value: same\n")
|
||||
_, err := configkit.LoadLayered[map[string]any](configkit.Input{
|
||||
@@ -567,7 +567,7 @@ func TestFacadeRelativeImportFailuresRetainSourceRecordsAndCoordinates(t *testin
|
||||
})
|
||||
|
||||
t.Run("version failure reports effective path and imported source", func(t *testing.T) {
|
||||
mainPath := write("version/main.yml", "configkit:\n imports:\n - path: child.yml\n destination: [tenant]\n")
|
||||
mainPath := write("version/main.yml", "config:\n imports:\n - path: child.yml\n destination: [tenant]\n")
|
||||
childPath := write("version/child.yml", "version: forbidden\n")
|
||||
_, err := configkit.LoadLayered[map[string]any](configkit.Input{
|
||||
Source: configkit.File(mainPath), Imports: true, Destination: configkit.Keys("mount"),
|
||||
@@ -599,7 +599,7 @@ func TestFacadeRelativeImportFailuresRetainSourceRecordsAndCoordinates(t *testin
|
||||
} `yaml:"tenant"`
|
||||
} `yaml:"mount"`
|
||||
}
|
||||
mainPath := write("typed/main.yml", "configkit:\n imports:\n - path: child.yml\n destination: [tenant]\n")
|
||||
mainPath := write("typed/main.yml", "config:\n imports:\n - path: child.yml\n destination: [tenant]\n")
|
||||
childPath := write("typed/child.yml", "count: invalid\n")
|
||||
_, err := configkit.LoadLayered[configuration](configkit.Input{
|
||||
Source: configkit.File(mainPath), Imports: true, Destination: configkit.Keys("mount"),
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func TestFacadeCompositionConflictKeepsClaimantOrder(t *testing.T) {
|
||||
filesystem := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"a.yml": &fstest.MapFile{Data: []byte(`configkit:
|
||||
"a.yml": &fstest.MapFile{Data: []byte(`config:
|
||||
imports:
|
||||
- path: z.yml
|
||||
`)},
|
||||
@@ -21,7 +21,7 @@ func TestFacadeCompositionConflictKeepsClaimantOrder(t *testing.T) {
|
||||
"z.yml": &fstest.MapFile{Data: []byte("x: z\n")},
|
||||
})
|
||||
main := configkit.Mapping("main", map[string]any{
|
||||
"configkit": map[string]any{"imports": []any{
|
||||
"config": map[string]any{"imports": []any{
|
||||
map[string]any{"path": "a.yml"},
|
||||
map[string]any{"path": "b.yml"},
|
||||
map[string]any{"path": "c.yml"},
|
||||
|
||||
@@ -87,8 +87,8 @@ func TestEquivalentSourceFormsPreserveInputSemantics(t *testing.T) {
|
||||
|
||||
func TestRepeatedImportsAcrossTiersAndReaderConsumption(t *testing.T) {
|
||||
namespace := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"left.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: shared.yml}]}\n")},
|
||||
"right.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: shared.yml}]}\n")},
|
||||
"left.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: shared.yml}]}\n")},
|
||||
"right.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: shared.yml}]}\n")},
|
||||
"shared.yml": &fstest.MapFile{Data: []byte("name: retained\n")},
|
||||
})
|
||||
type config struct {
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestFacadeFilesystemSourceImportsOverlayAndReuse(t *testing.T) {
|
||||
|
||||
func TestFacadeFilesystemContextsAndIdentity(t *testing.T) {
|
||||
first := configkit.NewFilesystem("same", fstest.MapFS{
|
||||
"root/config/main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: ../shared.yml}]}\n")},
|
||||
"root/config/main.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: ../shared.yml}]}\n")},
|
||||
"root/shared.yml": &fstest.MapFile{Data: []byte("items: {value: first}\n")},
|
||||
"root/overlays/testing.yml": &fstest.MapFile{Data: []byte("items: {overlay: first}\n")},
|
||||
})
|
||||
@@ -82,8 +82,8 @@ func TestFacadeFilesystemContextsAndIdentity(t *testing.T) {
|
||||
source configkit.Source
|
||||
kind configkit.SourceKind
|
||||
}{
|
||||
{"bytes", configkit.Bytes("memory", []byte("configkit: {imports: [{path: shared.yml}]}\n")), configkit.SourceKindBytes},
|
||||
{"reader", configkit.Reader("stream", strings.NewReader("configkit: {imports: [{path: shared.yml}]}\n")), configkit.SourceKindReader},
|
||||
{"bytes", configkit.Bytes("memory", []byte("config: {imports: [{path: shared.yml}]}\n")), configkit.SourceKindBytes},
|
||||
{"reader", configkit.Reader("stream", strings.NewReader("config: {imports: [{path: shared.yml}]}\n")), configkit.SourceKindReader},
|
||||
} {
|
||||
t.Run(input.name, func(t *testing.T) {
|
||||
result, err := configkit.Load(input.source, configkit.Options[filesystemFacadeConfig]{Imports: true, Filesystem: &configkit.FilesystemContext{Root: "root", BaseDir: "root", FS: &first}})
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
func TestFacadeNativeDiscoveryPreservesAliasIdentity(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
for name, contents := range map[string]string{
|
||||
"data/item.yml": "configkit:\n imports:\n - path: helper.yml\nvalue: item\n",
|
||||
"data/item.yml": "config:\n imports:\n - path: helper.yml\nvalue: item\n",
|
||||
"data/helper.yml": "owner: target\nresource: resource.txt\n",
|
||||
"aliases/helper.yml": "owner: alias\nresource: resource.txt\n",
|
||||
"data/resource.txt": "target resource",
|
||||
@@ -50,7 +50,7 @@ func TestFacadeNativeDiscoveryPreservesAliasIdentity(t *testing.T) {
|
||||
{"directory/glob", "glob: 'lin*/ite*.yml'", "linked/item.yml", "linked/helper.yml", "target", "target resource"},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result, err := configkit.Load(configkit.Bytes("main", []byte("configkit:\n imports:\n - "+test.declaration+"\n")), configkit.Options[configuration]{
|
||||
result, err := configkit.Load(configkit.Bytes("main", []byte("config:\n imports:\n - "+test.declaration+"\n")), configkit.Options[configuration]{
|
||||
Imports: true, Filesystem: &configkit.FilesystemContext{FS: &filesystem, Root: ".", BaseDir: "."},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -82,14 +82,14 @@ func TestFacadeNativeDiscoveryPreservesAliasIdentity(t *testing.T) {
|
||||
// The same native path is still a repeat when separately included. An alias
|
||||
// to a leaf file remains a different native namespace path, as for path imports.
|
||||
for _, selector := range []string{"path: aliases/item.yml", "dir: aliases\n pattern: item.yml", "glob: 'aliases/ite*.yml'"} {
|
||||
_, err := configkit.Load(configkit.Bytes("main", []byte("configkit:\n imports:\n - "+selector+"\n - path: aliases/item.yml\n")), configkit.Options[configuration]{
|
||||
_, err := configkit.Load(configkit.Bytes("main", []byte("config:\n imports:\n - "+selector+"\n - path: aliases/item.yml\n")), configkit.Options[configuration]{
|
||||
Imports: true, Filesystem: &configkit.FilesystemContext{FS: &filesystem, Root: ".", BaseDir: "."},
|
||||
})
|
||||
var loadError *configkit.LoadError
|
||||
if !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != configkit.CodeImportRepeated {
|
||||
t.Fatalf("repeated source: %v", err)
|
||||
}
|
||||
_, err = configkit.Load(configkit.Bytes("main", []byte("configkit:\n imports:\n - "+selector+"\n - path: data/item.yml\n")), configkit.Options[configuration]{
|
||||
_, err = configkit.Load(configkit.Bytes("main", []byte("config:\n imports:\n - "+selector+"\n - path: data/item.yml\n")), configkit.Options[configuration]{
|
||||
Imports: true, Filesystem: &configkit.FilesystemContext{FS: &filesystem, Root: ".", BaseDir: "."},
|
||||
})
|
||||
if !errors.As(err, &loadError) || len(loadError.Diagnostics()) == 0 || loadError.Diagnostics()[0].Code != configkit.CodeComposeConflict {
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestImportDepthChecksSuffixAfterSaturation(t *testing.T) {
|
||||
{"-0", true},
|
||||
} {
|
||||
t.Run(test.depth, func(t *testing.T) {
|
||||
main := writeFacadeImportFile(t, t.TempDir(), "main.yml", "value: true\nconfigkit:\n imports:\n - dir: absent\n recursive: true\n depth: "+test.depth+"\n")
|
||||
main := writeFacadeImportFile(t, t.TempDir(), "main.yml", "value: true\nconfig:\n imports:\n - dir: absent\n recursive: true\n depth: "+test.depth+"\n")
|
||||
result, err := Load[map[string]any](File(main), Options[map[string]any]{Imports: true})
|
||||
if test.valid {
|
||||
if err != nil || result.Config["value"] != true {
|
||||
@@ -31,7 +31,7 @@ func TestImportDepthChecksSuffixAfterSaturation(t *testing.T) {
|
||||
if !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 {
|
||||
t.Fatalf("invalid depth accepted: %v", err)
|
||||
}
|
||||
if diagnostic := loadError.Diagnostics()[0]; diagnostic.Code != CodeImportInvalid || diagnostic.Path.String() != "/configkit/imports/0/depth" {
|
||||
if diagnostic := loadError.Diagnostics()[0]; diagnostic.Code != CodeImportInvalid || diagnostic.Path.String() != "/config/imports/0/depth" {
|
||||
t.Fatalf("invalid depth diagnostic = %#v", diagnostic)
|
||||
}
|
||||
})
|
||||
@@ -47,12 +47,12 @@ func TestImportDefaultsApplyToTransitiveAndUnderlayImports(t *testing.T) {
|
||||
IgnoredDeep string `yaml:"ignored_deep"`
|
||||
}
|
||||
directory := t.TempDir()
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `configkit:
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `config:
|
||||
imports:
|
||||
- path: parent/parent.yml
|
||||
main: main
|
||||
`)
|
||||
writeFacadeImportFile(t, directory, "parent/parent.yml", `configkit:
|
||||
writeFacadeImportFile(t, directory, "parent/parent.yml", `config:
|
||||
imports:
|
||||
- dir: generated
|
||||
- dir: explicit
|
||||
@@ -64,7 +64,7 @@ main: main
|
||||
explicitPath := writeFacadeImportFile(t, directory, "parent/explicit/exception.yaml", "exception: declaration\n")
|
||||
writeFacadeImportFile(t, directory, "parent/explicit/deep/ignored.yaml", "ignored_deep: wrong-depth\n")
|
||||
writeFacadeImportFile(t, directory, "parent/explicit/ignored.cfg", "ignored_deep: wrong-explicit-pattern\n")
|
||||
underlayPath := writeFacadeImportFile(t, directory, "underlay.yml", `configkit:
|
||||
underlayPath := writeFacadeImportFile(t, directory, "underlay.yml", `config:
|
||||
imports:
|
||||
- dir: underlay-files
|
||||
`)
|
||||
@@ -97,7 +97,7 @@ func TestImportDefaultsPreserveEmptyPatternAndRequiredness(t *testing.T) {
|
||||
Hidden string `yaml:"hidden"`
|
||||
}
|
||||
directory := t.TempDir()
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `configkit:
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `config:
|
||||
imports:
|
||||
- dir: snippets
|
||||
`)
|
||||
@@ -118,7 +118,7 @@ func TestImportDefaultsPreserveEmptyPatternAndRequiredness(t *testing.T) {
|
||||
t.Fatalf("empty pattern = %#v, %v", empty, err)
|
||||
}
|
||||
emptySources := empty.Sources()
|
||||
if len(emptySources) != 1 || len(emptySources[0].EmptyImportSelections) != 1 || emptySources[0].EmptyImportSelections[0].Path.String() != "/configkit/imports/0" || emptySources[0].EmptyImportSelections[0].Selector != ImportSelectorDirectory {
|
||||
if len(emptySources) != 1 || len(emptySources[0].EmptyImportSelections) != 1 || emptySources[0].EmptyImportSelections[0].Path.String() != "/config/imports/0" || emptySources[0].EmptyImportSelections[0].Selector != ImportSelectorDirectory {
|
||||
t.Fatalf("empty-pattern evidence = %#v", emptySources)
|
||||
}
|
||||
required := true
|
||||
@@ -141,7 +141,7 @@ func TestImportDefaultsAreSnapshottedBeforeOrdinaryAndLayeredCallbacks(t *testin
|
||||
t.Run(map[bool]string{false: "ordinary", true: "layered"}[layered], func(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `version: v1
|
||||
configkit:
|
||||
config:
|
||||
imports:
|
||||
- dir: parts
|
||||
`)
|
||||
@@ -229,7 +229,7 @@ func TestPublicImportSelectionSnapshotsAreDetachedAcrossResultErrorAndResolution
|
||||
Value string `yaml:"value"`
|
||||
}
|
||||
directory := t.TempDir()
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `configkit:
|
||||
mainPath := writeFacadeImportFile(t, directory, "main.yml", `config:
|
||||
imports:
|
||||
- dir: absent
|
||||
value: loaded
|
||||
@@ -242,7 +242,7 @@ value: loaded
|
||||
}
|
||||
first.EmptyImportSelections[0].Path = Keys("callback mutation")
|
||||
second, ok := resolution.Source("source-1")
|
||||
if !ok || second.EmptyImportSelections[0].Path.String() != "/configkit/imports/0" {
|
||||
if !ok || second.EmptyImportSelections[0].Path.String() != "/config/imports/0" {
|
||||
t.Fatalf("resolution snapshot changed after mutation: %#v", second)
|
||||
}
|
||||
callbackPath = second.EmptyImportSelections[0].Path.String()
|
||||
@@ -251,7 +251,7 @@ value: loaded
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if callbackPath != "/configkit/imports/0" {
|
||||
if callbackPath != "/config/imports/0" {
|
||||
t.Fatalf("callback path = %q", callbackPath)
|
||||
}
|
||||
sources := loaded.Sources()
|
||||
@@ -259,11 +259,11 @@ value: loaded
|
||||
t.Fatalf("result source evidence = %#v", sources)
|
||||
}
|
||||
sources[0].EmptyImportSelections[0].Path = Keys("result mutation")
|
||||
if got := loaded.Sources()[0].EmptyImportSelections[0].Path.String(); got != "/configkit/imports/0" {
|
||||
if got := loaded.Sources()[0].EmptyImportSelections[0].Path.String(); got != "/config/imports/0" {
|
||||
t.Fatalf("result snapshot changed after mutation: %q", got)
|
||||
}
|
||||
|
||||
failurePath := writeFacadeImportFile(t, directory, "failure.yml", `configkit:
|
||||
failurePath := writeFacadeImportFile(t, directory, "failure.yml", `config:
|
||||
imports:
|
||||
- dir: absent
|
||||
- path: required-missing.yml
|
||||
@@ -275,11 +275,11 @@ value: loaded
|
||||
t.Fatalf("failed load error = %T %v", err, err)
|
||||
}
|
||||
errorSources := loadError.Sources()
|
||||
if len(errorSources) != 1 || len(errorSources[0].EmptyImportSelections) != 1 || errorSources[0].EmptyImportSelections[0].Path.String() != "/configkit/imports/0" {
|
||||
if len(errorSources) != 1 || len(errorSources[0].EmptyImportSelections) != 1 || errorSources[0].EmptyImportSelections[0].Path.String() != "/config/imports/0" {
|
||||
t.Fatalf("error source evidence = %#v", errorSources)
|
||||
}
|
||||
errorSources[0].EmptyImportSelections[0].Path = Keys("error mutation")
|
||||
if got := loadError.Sources()[0].EmptyImportSelections[0].Path.String(); got != "/configkit/imports/0" {
|
||||
if got := loadError.Sources()[0].EmptyImportSelections[0].Path.String(); got != "/config/imports/0" {
|
||||
t.Fatalf("error snapshot changed after mutation: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func TestDiscoveredItemHelpersPreserveNestedInheritedResourceEvidence(t *testing.T) {
|
||||
const canary = "PRIVATE-CONSUMED-HEADER-CANARY"
|
||||
filesystem := NewFilesystem("bundle", fstest.MapFS{
|
||||
"bundle/config/main.yml": {Data: []byte(`configkit:
|
||||
"bundle/config/main.yml": {Data: []byte(`config:
|
||||
imports:
|
||||
- dir: records
|
||||
pattern: '*-item.yml'
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
- id: authored
|
||||
version: authored
|
||||
`)},
|
||||
"bundle/config/records/a-item.yml": {Data: []byte(`configkit:
|
||||
"bundle/config/records/a-item.yml": {Data: []byte(`config:
|
||||
imports:
|
||||
- path: helper.yml
|
||||
version: ` + canary + `
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestFacadeDirectoryRootBelowContainedAbsoluteSymlink(t *testing.T) {
|
||||
for _, follow := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("follow=%t", follow), func(t *testing.T) {
|
||||
mainPath := filepath.Join(root, "main.yml")
|
||||
contents := fmt.Sprintf("configkit:\n imports:\n - dir: absolute/parts\n pattern: '*.yml'\n follow: %t\n", follow)
|
||||
contents := fmt.Sprintf("config:\n imports:\n - dir: absolute/parts\n pattern: '*.yml'\n follow: %t\n", follow)
|
||||
if err := os.WriteFile(mainPath, []byte(contents), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func TestFacadeNativeDiscoveryWithNestedRoot(t *testing.T) {
|
||||
"dir: parts", "glob: 'parts/*.yml'", "dir: linked", "glob: 'lin*/*.yml'",
|
||||
} {
|
||||
t.Run(selector, func(t *testing.T) {
|
||||
result, err := configkit.Load(configkit.Bytes("main", []byte("configkit:\n imports:\n - "+selector+"\n")), configkit.Options[map[string]any]{Imports: true, Filesystem: context})
|
||||
result, err := configkit.Load(configkit.Bytes("main", []byte("config:\n imports:\n - "+selector+"\n")), configkit.Options[map[string]any]{Imports: true, Filesystem: context})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func TestFacadeNativeDiscoveryWithNestedRoot(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, selector := range []string{"dir: parts", "glob: 'parts/*.yml'"} {
|
||||
_, err := configkit.Load(configkit.Bytes("main", []byte("configkit:\n imports:\n - "+selector+"\n")), configkit.Options[map[string]any]{Imports: true, Filesystem: context})
|
||||
_, err := configkit.Load(configkit.Bytes("main", []byte("config:\n imports:\n - "+selector+"\n")), configkit.Options[map[string]any]{Imports: true, Filesystem: context})
|
||||
var loadError *configkit.LoadError
|
||||
if !errors.As(err, &loadError) || len(loadError.Diagnostics()) != 1 || loadError.Diagnostics()[0].Code != configkit.CodeImportOutsideRoot {
|
||||
t.Fatalf("escaping %s: %v", selector, err)
|
||||
@@ -107,7 +107,7 @@ func TestFacadeDiscoveryAllowsFiniteLinkReuse(t *testing.T) {
|
||||
if native {
|
||||
context = &configkit.FilesystemContext{FS: &filesystem, Root: ".", BaseDir: "."}
|
||||
}
|
||||
result, err := configkit.Load(configkit.Bytes("main", []byte("configkit:\n imports:\n - "+selector+"\n")), configkit.Options[map[string]any]{Imports: true, Filesystem: context})
|
||||
result, err := configkit.Load(configkit.Bytes("main", []byte("config:\n imports:\n - "+selector+"\n")), configkit.Options[map[string]any]{Imports: true, Filesystem: context})
|
||||
if err != nil {
|
||||
t.Fatalf("native=%v selector=%s: %v", native, selector, err)
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ func TestFacadeIdentifiedInheritanceFailureDoesNotRevealSecretCanary(t *testing.
|
||||
|
||||
func TestFacadeLayeredIdentifiedInheritanceUsesSurvivingRecords(t *testing.T) {
|
||||
policy := func(mode string) string {
|
||||
return "configkit:\n sequences:\n - destination: [jobs]\n key: id\n mode: " + mode + "\n"
|
||||
return "config:\n sequences:\n - destination: [jobs]\n key: id\n mode: " + mode + "\n"
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -416,7 +416,7 @@ func TestFacadeInheritanceFailureStopsLaterCallbacks(t *testing.T) {
|
||||
|
||||
func TestFacadeCLIOverridesCannotChangeInheritanceRelationship(t *testing.T) {
|
||||
filesystem := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: parent.yml}]}\nentries: {child: {extends: first}}\n")},
|
||||
"main.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: parent.yml}]}\nentries: {child: {extends: first}}\n")},
|
||||
"parent.yml": &fstest.MapFile{Data: []byte("entries: {first: {count: 1}, second: {count: 2}}\n")},
|
||||
})
|
||||
_, err := configkit.Load(filesystem.File("main.yml"), configkit.Options[inheritedConfiguration]{
|
||||
@@ -427,7 +427,7 @@ func TestFacadeCLIOverridesCannotChangeInheritanceRelationship(t *testing.T) {
|
||||
t.Fatalf("CLI inheritance relationship change = %v", err)
|
||||
}
|
||||
conflicting := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: parent.yml}]}\nentries: {child: {extends: first}, first: {count: 3}}\n")},
|
||||
"main.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: parent.yml}]}\nentries: {child: {extends: first}, first: {count: 3}}\n")},
|
||||
"parent.yml": &fstest.MapFile{Data: []byte("entries: {first: {count: 1}}\n")},
|
||||
})
|
||||
_, err = configkit.Load[inheritedConfiguration](conflicting.File("main.yml"), configkit.Options[inheritedConfiguration]{Imports: true})
|
||||
|
||||
@@ -22,14 +22,14 @@ func TestFacadeItemScopesDoNotReinterpretGlobalRegistrations(t *testing.T) {
|
||||
configkit.SequenceLiteralKey("catalog"), configkit.SequenceAnyMappingKey(), configkit.SequenceLiteralKey("prompts"),
|
||||
}, "id")
|
||||
}
|
||||
item := "configkit: {imports: [{path: helper.yml}, {path: child.yml, destination: [children], placement: sequence-item}]}\n"
|
||||
item := "config: {imports: [{path: helper.yml}, {path: child.yml, destination: [children], placement: sequence-item}]}\n"
|
||||
if !missingOuterID {
|
||||
item += "id: outer\n"
|
||||
}
|
||||
files := configkit.NewFilesystem("items", fstest.MapFS{
|
||||
"main.yml": {Data: []byte("configkit: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||
"main.yml": {Data: []byte("config: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||
"item.yml": {Data: []byte(item)}, "helper.yml": {Data: []byte(payload)},
|
||||
"child.yml": {Data: []byte("id: child\nconfigkit: {imports: [{path: child-helper.yml}]}\n")},
|
||||
"child.yml": {Data: []byte("id: child\nconfig: {imports: [{path: child-helper.yml}]}\n")},
|
||||
"child-helper.yml": {Data: []byte(payload)},
|
||||
})
|
||||
definition := configkit.Definition[map[string]any]{SequenceIdentities: []configkit.SequenceIdentity{
|
||||
|
||||
@@ -13,18 +13,18 @@ func TestFacadeItemDomainVersionsKeepEffectiveScope(t *testing.T) {
|
||||
underlay, mainItem bool
|
||||
}{
|
||||
{name: "item", item: "id: item\nversion: domain\n"},
|
||||
{name: "helper", item: "id: item\nconfigkit: {imports: [{path: helper.yml}]}\n", helper: "version: domain\n"},
|
||||
{name: "nested", item: "id: parent\nconfigkit: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\n", child: "id: child\nversion: domain\n"},
|
||||
{name: "helper", item: "id: item\nconfig: {imports: [{path: helper.yml}]}\n", helper: "version: domain\n"},
|
||||
{name: "nested", item: "id: parent\nconfig: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\n", child: "id: child\nversion: domain\n"},
|
||||
{name: "underlay", item: "id: item\nversion: domain\n", underlay: true},
|
||||
{name: "main-item", item: "id: parent\nconfigkit: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\n", child: "id: child\nversion: domain\n", mainItem: true},
|
||||
{name: "main-item", item: "id: parent\nconfig: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\n", child: "id: child\nversion: domain\n", mainItem: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
files := fstest.MapFS{
|
||||
"main.yml": {Data: []byte("version: 1\nconfigkit: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||
"main.yml": {Data: []byte("version: 1\nconfig: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||
"item.yml": {Data: []byte(test.item)},
|
||||
"child.yml": {Data: []byte(test.child)},
|
||||
"helper.yml": {Data: []byte(test.helper)},
|
||||
"underlay.yml": {Data: []byte("configkit: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||
"underlay.yml": {Data: []byte("config: {imports: [{path: item.yml, destination: [items], placement: sequence-item}]}\n")},
|
||||
}
|
||||
filesystem := configkit.NewFilesystem("items", files)
|
||||
definition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestFacadeLayeredMixedSourcesAndInspection(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lower := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: defaults.yml}]}\nsettings: {shared: lower, native: retained, secret: old-canary}\nlist: [lower]\n")},
|
||||
"main.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: defaults.yml}]}\nsettings: {shared: lower, native: retained, secret: old-canary}\nlist: [lower]\n")},
|
||||
"defaults.yml": &fstest.MapFile{Data: []byte("settings: {imported: retained}\n")},
|
||||
"overlays/testing.yml": &fstest.MapFile{Data: []byte("settings: {from_lower_namespace: retained}\n")},
|
||||
})
|
||||
|
||||
@@ -80,7 +80,7 @@ func TestFacadeMappingImportsUseExplicitContext(t *testing.T) {
|
||||
"root/shared.yml": &fstest.MapFile{Data: []byte("name: imported\n")},
|
||||
})
|
||||
source := configkit.Mapping("programmatic", map[string]any{
|
||||
"configkit": map[string]any{"imports": []any{map[string]any{"path": "shared.yml"}}},
|
||||
"config": map[string]any{"imports": []any{map[string]any{"path": "shared.yml"}}},
|
||||
"enabled": true,
|
||||
})
|
||||
result, err := configkit.Load(source, configkit.Options[mappingFacadeConfig]{
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestFacadeSourceContextsFollowOriginsWithoutQueryIO(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
borrowed := &observedResourceFS{files: fstest.MapFS{
|
||||
"bundle/config/defaults.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: ../shared.yml}]}\nnative: icon.txt\n")},
|
||||
"bundle/config/defaults.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: ../shared.yml}]}\nnative: icon.txt\n")},
|
||||
"bundle/shared.yml": &fstest.MapFile{Data: []byte("shared: shared.txt\n")},
|
||||
"bundle/overlays/test.yml": &fstest.MapFile{Data: []byte("selected: overlay.txt\n")},
|
||||
"bundle/config/icon.txt": &fstest.MapFile{Data: []byte("icon-v1")},
|
||||
@@ -224,8 +224,8 @@ func TestFacadeSyntheticSourceContextsAndContextlessSources(t *testing.T) {
|
||||
|
||||
func TestFacadeRelativeImportDestinationsPreserveNativeResourceContexts(t *testing.T) {
|
||||
borrowed := &observedResourceFS{files: fstest.MapFS{
|
||||
"bundle/config/main.yml": &fstest.MapFile{Data: []byte("configkit:\n imports:\n - path: entries/parent.yml\n destination: [logical]\n")},
|
||||
"bundle/config/entries/parent.yml": &fstest.MapFile{Data: []byte("configkit:\n imports:\n - path: nested/details.yml\n destination: [details]\nresource: icon.txt\n")},
|
||||
"bundle/config/main.yml": &fstest.MapFile{Data: []byte("config:\n imports:\n - path: entries/parent.yml\n destination: [logical]\n")},
|
||||
"bundle/config/entries/parent.yml": &fstest.MapFile{Data: []byte("config:\n imports:\n - path: nested/details.yml\n destination: [details]\nresource: icon.txt\n")},
|
||||
"bundle/config/entries/nested/details.yml": &fstest.MapFile{Data: []byte("resource: detail.txt\n")},
|
||||
"bundle/config/entries/icon.txt": &fstest.MapFile{Data: []byte("parent-resource")},
|
||||
"bundle/config/entries/nested/detail.txt": &fstest.MapFile{Data: []byte("nested-resource")},
|
||||
|
||||
@@ -54,12 +54,12 @@ func TestFacadeNoSourcePreservesAbsenceWithSequencePolicy(t *testing.T) {
|
||||
func TestFacadeItemHelperHistoryPreservesScheduledPlacement(t *testing.T) {
|
||||
t.Parallel()
|
||||
filesystem := configkit.NewFilesystem("history", fstest.MapFS{
|
||||
"main.yml": {Data: []byte("records: [{id: authored}]\nconfigkit: {imports: [{path: one.yml, destination: [records], placement: sequence-item}, {path: two.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"one.yml": {Data: []byte("id: one\nconfigkit: {imports: [{path: helper-one.yml}, {path: child.yml, destination: [children], placement: sequence-item}]}\n")},
|
||||
"two.yml": {Data: []byte("id: two\nconfigkit: {imports: [{path: helper-two.yml}]}\n")},
|
||||
"main.yml": {Data: []byte("records: [{id: authored}]\nconfig: {imports: [{path: one.yml, destination: [records], placement: sequence-item}, {path: two.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"one.yml": {Data: []byte("id: one\nconfig: {imports: [{path: helper-one.yml}, {path: child.yml, destination: [children], placement: sequence-item}]}\n")},
|
||||
"two.yml": {Data: []byte("id: two\nconfig: {imports: [{path: helper-two.yml}]}\n")},
|
||||
"helper-one.yml": {Data: []byte("text: first helper\n")},
|
||||
"helper-two.yml": {Data: []byte("text: second helper\n")},
|
||||
"child.yml": {Data: []byte("id: child\nconfigkit: {imports: [{path: child-helper.yml}]}\n")},
|
||||
"child.yml": {Data: []byte("id: child\nconfig: {imports: [{path: child-helper.yml}]}\n")},
|
||||
"child-helper.yml": {Data: []byte("text: nested helper\n")},
|
||||
})
|
||||
result, err := configkit.Load(filesystem.File("main.yml"), configkit.Options[map[string]any]{
|
||||
@@ -107,7 +107,7 @@ func TestFacadeRegisteredReplacementExplainsRemovedDescendants(t *testing.T) {
|
||||
{"removed field", []any{map[string]any{"id": "main"}}, configkit.Keys("records").Index(0).Key("text")},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
mainYAML := "configkit:\n sequences:\n - destination: [records]\n key: id\n mode: replace\n"
|
||||
mainYAML := "config:\n sequences:\n - destination: [records]\n key: id\n mode: replace\n"
|
||||
if len(test.upper) == 0 {
|
||||
mainYAML += "records: []\n"
|
||||
} else {
|
||||
@@ -135,12 +135,12 @@ func TestFacadeRegisteredReplacementExplainsRemovedDescendants(t *testing.T) {
|
||||
func TestFacadeItemConflictDiagnosticsFollowEnclosingIndexes(t *testing.T) {
|
||||
t.Parallel()
|
||||
filesystem := configkit.NewFilesystem("conflicts", fstest.MapFS{
|
||||
"main.yml": {Data: []byte("records: [{id: authored}]\nconfigkit: {imports: [{path: good.yml, destination: [records], placement: sequence-item}, {path: bad.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"group.yml": {Data: []byte("records: [{id: authored}]\nconfigkit: {imports: [{path: bad.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"nested.yml": {Data: []byte("records: [{id: authored}]\nconfigkit: {imports: [{path: parent.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"parent.yml": {Data: []byte("id: parent\nchildren: [{id: authored-child}]\nconfigkit: {imports: [{path: bad.yml, destination: [children], placement: sequence-item}]}\n")},
|
||||
"main.yml": {Data: []byte("records: [{id: authored}]\nconfig: {imports: [{path: good.yml, destination: [records], placement: sequence-item}, {path: bad.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"group.yml": {Data: []byte("records: [{id: authored}]\nconfig: {imports: [{path: bad.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"nested.yml": {Data: []byte("records: [{id: authored}]\nconfig: {imports: [{path: parent.yml, destination: [records], placement: sequence-item}]}\n")},
|
||||
"parent.yml": {Data: []byte("id: parent\nchildren: [{id: authored-child}]\nconfig: {imports: [{path: bad.yml, destination: [children], placement: sequence-item}]}\n")},
|
||||
"good.yml": {Data: []byte("id: good\n")},
|
||||
"bad.yml": {Data: []byte("id: bad\nconfigkit: {imports: [{path: helper.yml}]}\n")},
|
||||
"bad.yml": {Data: []byte("id: bad\nconfig: {imports: [{path: helper.yml}]}\n")},
|
||||
"helper.yml": {Data: []byte("id: duplicate-field\n")},
|
||||
})
|
||||
definition := configkit.Definition[map[string]any]{SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("records"), "id")}}
|
||||
@@ -277,7 +277,7 @@ func TestFacadeSequencePlacementControlsAcrossLoadEntryPoints(t *testing.T) {
|
||||
func TestFacadeItemImportGlobComposesWithAuthoredSequence(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
mainPath := filepath.Join(directory, "main.yml")
|
||||
if err := os.WriteFile(mainPath, []byte(`configkit:
|
||||
if err := os.WriteFile(mainPath, []byte(`config:
|
||||
imports:
|
||||
- glob: records/*.yml
|
||||
destination: [items]
|
||||
@@ -358,7 +358,7 @@ func TestFacadeItemDestinationFallbackEvidenceSurvivesFailures(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
mainPath := filepath.Join(directory, "main.yml")
|
||||
helperPath := filepath.Join(directory, "helper.yml")
|
||||
if err := os.WriteFile(mainPath, []byte("configkit: {imports: [{path: helper.yml, destination: [metadata]}]}\nid: main\n"), 0o600); err != nil {
|
||||
if err := os.WriteFile(mainPath, []byte("config: {imports: [{path: helper.yml, destination: [metadata]}]}\nid: main\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(helperPath, []byte("value: [unterminated\n"), 0o600); err != nil {
|
||||
@@ -430,10 +430,10 @@ func TestFacadeNestedItemFallbackUsesNearestResolvedContainer(t *testing.T) {
|
||||
mainPath := filepath.Join(directory, "main.yml")
|
||||
childPath := filepath.Join(directory, "child.yml")
|
||||
helperPath := filepath.Join(directory, "helper.yml")
|
||||
if err := os.WriteFile(mainPath, []byte("configkit: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\nid: parent\n"), 0o600); err != nil {
|
||||
if err := os.WriteFile(mainPath, []byte("config: {imports: [{path: child.yml, destination: [children], placement: sequence-item}]}\nid: parent\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(childPath, []byte("configkit: {imports: [{path: helper.yml, destination: [metadata]}]}\nid: child\n"), 0o600); err != nil {
|
||||
if err := os.WriteFile(childPath, []byte("config: {imports: [{path: helper.yml, destination: [metadata]}]}\nid: child\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(helperPath, []byte("value: [unterminated\n"), 0o600); err != nil {
|
||||
@@ -556,7 +556,7 @@ func TestFacadeSequenceReplacementKeepsTypedAndHistoricalEvidenceAligned(t *test
|
||||
directory := t.TempDir()
|
||||
mainPath := filepath.Join(directory, "main.yml")
|
||||
underlayPath := filepath.Join(directory, "underlay.yml")
|
||||
if err := os.WriteFile(mainPath, []byte(`configkit:
|
||||
if err := os.WriteFile(mainPath, []byte(`config:
|
||||
sequences:
|
||||
- destination: [entries]
|
||||
key: id
|
||||
@@ -571,7 +571,7 @@ entries:
|
||||
`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(underlayPath, []byte(`configkit:
|
||||
if err := os.WriteFile(underlayPath, []byte(`config:
|
||||
imports:
|
||||
- glob: records/*.yml
|
||||
destination: [entries]
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestUnderlayVersionProtectionUsesMainRecordPosition(t *testing.T) {
|
||||
want := configkit.CodeUnderlayVersion
|
||||
if imported {
|
||||
files := configkit.NewFilesystem("lower", fstest.MapFS{
|
||||
"main.yml": {Data: []byte("configkit: {imports: [{path: fragment.yml}]}\n")},
|
||||
"main.yml": {Data: []byte("config: {imports: [{path: fragment.yml}]}\n")},
|
||||
"fragment.yml": {Data: []byte(payload)},
|
||||
})
|
||||
lower.Source, lower.Imports = files.File("main.yml"), true
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestFacadeSourceVersionEvidenceFollowsAssembledSequenceRanges(t *testing.T)
|
||||
})
|
||||
} else {
|
||||
filesystem := configkit.NewFilesystem("versions", fstest.MapFS{
|
||||
"main.yml": {Data: []byte("configkit: {imports: [{path: child.yml}]}\n" + first)},
|
||||
"main.yml": {Data: []byte("config: {imports: [{path: child.yml}]}\n" + first)},
|
||||
"child.yml": {Data: []byte(child)},
|
||||
})
|
||||
result, err = configkit.Load(filesystem.File("main.yml"), configkit.Options[map[string]any]{
|
||||
@@ -98,7 +98,7 @@ func TestFacadeSourceVersionOwnershipAcceptsContextualIndexes(t *testing.T) {
|
||||
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.Bytes("lower", []byte(child))}}}},
|
||||
})
|
||||
} else {
|
||||
filesystem := configkit.NewFilesystem("versions", fstest.MapFS{"main.yml": {Data: []byte("configkit: {imports: [{path: child.yml}]}\n" + first)}, "child.yml": {Data: []byte(child)}})
|
||||
filesystem := configkit.NewFilesystem("versions", fstest.MapFS{"main.yml": {Data: []byte("config: {imports: [{path: child.yml}]}\n" + first)}, "child.yml": {Data: []byte(child)}})
|
||||
result, err = configkit.Load(filesystem.File("main.yml"), configkit.Options[map[string]any]{Definition: definition, SourceVersionMode: configkit.SourceVersionModeRequired, Imports: true})
|
||||
}
|
||||
if err != nil || result == nil {
|
||||
@@ -121,7 +121,7 @@ func TestFacadeItemMainKeepsDefaultVersionOwnership(t *testing.T) {
|
||||
helper = "other: {version: permitted}\n"
|
||||
}
|
||||
filesystem := configkit.NewFilesystem("item-main", fstest.MapFS{
|
||||
"main.yml": {Data: []byte(body + "configkit: {imports: [{path: helper.yml}]}\n")}, "helper.yml": {Data: []byte(helper)},
|
||||
"main.yml": {Data: []byte(body + "config: {imports: [{path: helper.yml}]}\n")}, "helper.yml": {Data: []byte(helper)},
|
||||
})
|
||||
checks := 0
|
||||
definition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
|
||||
@@ -223,7 +223,7 @@ func TestFacadeSourceVersionModesPreserveImportOptIn(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, mode := range []configkit.SourceVersionMode{configkit.SourceVersionModeIfPresent, configkit.SourceVersionModeRequired} {
|
||||
for _, underlay := range []bool{false, true} {
|
||||
bad := configkit.Bytes("disabled-imports", []byte("version: accepted\nconfigkit: {imports: [{path: missing.yml}]}\nvalue: visible\n"))
|
||||
bad := configkit.Bytes("disabled-imports", []byte("version: accepted\nconfig: {imports: [{path: missing.yml}]}\nvalue: visible\n"))
|
||||
definition := configkit.Definition[map[string]any]{Version: &configkit.VersionRule{
|
||||
Path: configkit.Keys("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil },
|
||||
}}
|
||||
@@ -246,7 +246,7 @@ func TestFacadeSourceVersionModesPreserveImportOptIn(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
diagnostics := failure.Diagnostics()
|
||||
if len(diagnostics) != 1 || diagnostics[0].Code != configkit.CodeImportDisabled || diagnostics[0].Path.String() != "/configkit" ||
|
||||
if len(diagnostics) != 1 || diagnostics[0].Code != configkit.CodeImportDisabled || diagnostics[0].Path.String() != "/config" ||
|
||||
diagnostics[0].Primary == nil || diagnostics[0].Primary.SourceID != wantSource {
|
||||
t.Errorf("mode=%v underlay=%v: diagnostics=%#v", mode, underlay, diagnostics)
|
||||
}
|
||||
@@ -636,7 +636,7 @@ func TestFacadeSourceVersionEvidenceOnFailedLoadsAndSyntheticSources(t *testing.
|
||||
Path: configkit.Keys("version"), Check: func(configkit.Version, *configkit.Validation) error { checked++; return nil },
|
||||
}}
|
||||
path := filepath.Join(t.TempDir(), "main.yml")
|
||||
contents := "version: " + canary + "\nconfigkit:\n imports:\n - path: missing.yml\n required: true\n"
|
||||
contents := "version: " + canary + "\nconfig:\n imports:\n - path: missing.yml\n required: true\n"
|
||||
if err := os.WriteFile(path, []byte(contents), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestUnderlaySequenceDescendantOrigins(t *testing.T) {
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
bundle := configkit.NewFilesystem("bundle", fstest.MapFS{
|
||||
"main.yml": &fstest.MapFile{Data: []byte("configkit: {imports: [{path: items.yml}]}\n")},
|
||||
"main.yml": &fstest.MapFile{Data: []byte("config: {imports: [{path: items.yml}]}\n")},
|
||||
"items.yml": &fstest.MapFile{Data: []byte("items: [{name: first, nested: [false]}]\n")},
|
||||
})
|
||||
tiers := []configkit.Underlay{}
|
||||
|
||||
@@ -74,10 +74,10 @@ func TestSourceVersionEvidenceConversion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImportSelectionConversion(t *testing.T) {
|
||||
file := writeRecordConfig(t, "configkit:\n imports:\n - glob: absent/*.yml\nvalue: main\n")
|
||||
file := writeRecordConfig(t, "config:\n imports:\n - glob: absent/*.yml\nvalue: main\n")
|
||||
internal := engineSourceInfoFromLoad(t, file, "source-1", engine.Options[recordConfig]{Imports: true})
|
||||
converted := fromEngineSourceInfo(internal)
|
||||
if len(converted.EmptyImportSelections) != 1 || converted.EmptyImportSelections[0].Path.String() != "/configkit/imports/0" ||
|
||||
if len(converted.EmptyImportSelections) != 1 || converted.EmptyImportSelections[0].Path.String() != "/config/imports/0" ||
|
||||
converted.EmptyImportSelections[0].Location.SourceID != internal.ID || converted.EmptyImportSelections[0].Location.Line != 3 ||
|
||||
converted.EmptyImportSelections[0].Selector != ImportSelectorGlob {
|
||||
t.Fatalf("converted selection evidence = %#v", converted.EmptyImportSelections)
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestResultSourceLookup(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mainPath := writeSourceLookupFile(t, directory, "main.yml", `version: MAIN-SOURCE-CANARY
|
||||
configkit:
|
||||
config:
|
||||
imports:
|
||||
- path: imported.yml
|
||||
- dir: absent
|
||||
@@ -122,20 +122,20 @@ imported_value: imported
|
||||
|
||||
func TestLoadErrorSourceLookup(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
mainPath := writeSourceLookupFile(t, directory, "main.yml", `configkit:
|
||||
mainPath := writeSourceLookupFile(t, directory, "main.yml", `config:
|
||||
imports:
|
||||
- path: item.yml
|
||||
destination: [items]
|
||||
placement: sequence-item
|
||||
`)
|
||||
writeSourceLookupFile(t, directory, "item.yml", `configkit:
|
||||
writeSourceLookupFile(t, directory, "item.yml", `config:
|
||||
imports:
|
||||
- path: helper.yml
|
||||
destination: [metadata]
|
||||
`)
|
||||
const canary = "FAILED-SOURCE-LOOKUP-CANARY"
|
||||
helperPath := writeSourceLookupFile(t, directory, "helper.yml", `version: `+canary+`
|
||||
configkit:
|
||||
config:
|
||||
imports:
|
||||
- dir: absent
|
||||
- path: missing.yml
|
||||
|
||||
2
testdata/filesystem/config/main.yml
vendored
2
testdata/filesystem/config/main.yml
vendored
@@ -1,4 +1,4 @@
|
||||
configkit:
|
||||
config:
|
||||
imports:
|
||||
- path: ../shared.yml
|
||||
items:
|
||||
|
||||
Reference in New Issue
Block a user