package engine import ( "reflect" "testing" ) func TestNestedCLIOverridePreservesRetainedInputPresence(t *testing.T) { t.Parallel() type nested struct { Changed int `yaml:"changed"` Count int `yaml:"count"` Zero int `yaml:"zero"` Enabled bool `yaml:"enabled"` } type settings struct { Nested nested `yaml:"nested"` } type config struct { Settings settings `yaml:"settings"` } for _, layered := range []bool{false, true} { name := "ordinary" if layered { name = "layered" } t.Run(name, func(t *testing.T) { source := Bytes("input", []byte("settings:\n nested:\n changed: 1\n count: 2\n zero: 0\n enabled: false\n")) definition := Definition[config]{Resolve: func(value *config, resolution *Resolution) error { for _, key := range []string{"count", "zero", "enabled"} { info, ok := resolution.InputInfo(Keys("settings", "nested", key)) if !ok || !info.Explicit || info.Origin.Layer != LayerBase { t.Errorf("retained %s input info = %#v, %v", key, info, ok) } } Default(resolution, Keys("settings", "nested", "count"), &value.Settings.Nested.Count, 99, "fill missing count") Default(resolution, Keys("settings", "nested", "zero"), &value.Settings.Nested.Zero, 99, "fill missing zero") Default(resolution, Keys("settings", "nested", "enabled"), &value.Settings.Nested.Enabled, true, "fill missing enabled") return nil }} overrides := []Override{{Path: Keys("settings"), Value: map[string]any{"nested": map[string]any{"changed": 3}}, Flag: "--settings"}} var result *testResult[config] var err error if layered { result, err = testGroupedLoad(loadRequest[config]{main: loadInput{source: source}, definition: definition, overrides: overrides}) } else { result, err = testLoad(source, Options[config]{Definition: definition, Overrides: overrides}) } if err != nil { t.Fatal(err) } if got, want := result.Config.Settings.Nested, (nested{Changed: 3, Count: 2}); got != want { t.Fatalf("resolved nested values = %#v, want %#v", got, want) } for _, key := range []string{"count", "zero", "enabled"} { info, ok := result.Metadata().Lookup(Keys("settings", "nested", key)) if !ok || !info.Explicit || info.Origin.Layer != LayerBase || len(info.Origin.Locations) != 1 || info.Origin.Locations[0].SourceID != "source-1" { t.Errorf("retained %s metadata = %#v, %v", key, info, ok) } } info, ok := result.Metadata().Lookup(Keys("settings", "nested", "changed")) if !ok || !info.Explicit || info.Origin.Layer != LayerCLI { t.Errorf("changed metadata = %#v, %v", info, ok) } }) } } func TestPostDecodeOverrideLifecycleMatchesOrdinaryAndLayeredLoads(t *testing.T) { t.Parallel() type config struct { Count int `yaml:"count"` Derived int `yaml:"derived"` } input := Bytes("settings.yml", []byte("count: 4\n")) defaultsCalls := 0 definition := Definition[config]{ Defaults: func() config { defaultsCalls++ return config{Count: 9, Derived: 1} }, Resolve: func(value *config, resolution *Resolution) error { info, ok := resolution.InputInfo(Keys("count")) if !ok || !info.Explicit || info.Origin.Layer != LayerCLI { t.Errorf("resolution count info = %#v, %v", info, ok) } if source, ok := resolution.Source("source-2"); !ok || source.Layer != LayerCLI { t.Errorf("resolution CLI source = %#v, %v", source, ok) } Default(resolution, Keys("count"), &value.Count, 99, "preserve explicit count") Default(resolution, Keys("derived"), &value.Derived, value.Count*2, "derive from CLI count", Keys("count")) Normalize(resolution, Keys("count"), &value.Count, value.Count+1, "normalize after CLI") return nil }, } options := Options[config]{Definition: definition, Overrides: []Override{{Path: Keys("count"), Value: 4, Flag: "--count"}}} ordinary, err := testLoad(input, options) if err != nil { t.Fatal(err) } layered, err := testGroupedLoad(loadRequest[config]{ main: loadInput{source: input}, definition: definition, overrides: []Override{{Path: Keys("count"), Value: 4, Flag: "--count"}}, }) if err != nil { t.Fatal(err) } if defaultsCalls != 2 { t.Fatalf("defaults factory calls = %d, want once per load", defaultsCalls) } for name, result := range map[string]*testResult[config]{"ordinary": ordinary, "layered": layered} { if result.Config.Count != 5 || result.Config.Derived != 8 { t.Errorf("%s resolved configuration = %#v", name, result.Config) } if got := historyLayers(result.Metadata().History(Keys("count"))); !reflect.DeepEqual(got, []Layer{LayerDefault, LayerBase, LayerCLI, LayerResolution}) { t.Errorf("%s count history layers = %#v", name, got) } info, ok := result.Metadata().Lookup(Keys("count")) if !ok || info.Origin.Layer != LayerResolution { t.Errorf("%s final count origin = %#v, %v", name, info, ok) } } if !reflect.DeepEqual(ordinary.Config, layered.Config) || !reflect.DeepEqual(ordinary.Metadata().History(Keys("count")), layered.Metadata().History(Keys("count"))) { t.Fatalf("ordinary and layered results differ: %#v / %#v", ordinary.Config, layered.Config) } } func TestNoSourceOverridesReachResolutionAfterDefaults(t *testing.T) { t.Parallel() type config struct { Count int `yaml:"count"` Derived int `yaml:"derived"` } calls := 0 result, err := testLoad(NoSource(), Options[config]{ Definition: Definition[config]{ Defaults: func() config { calls++ return config{Count: 2} }, Resolve: func(value *config, resolution *Resolution) error { info, ok := resolution.InputInfo(Keys("count")) if !ok || !info.Explicit || info.Origin.Layer != LayerCLI { t.Errorf("resolution count info = %#v, %v", info, ok) } Default(resolution, Keys("count"), &value.Count, 10, "preserve CLI value") Default(resolution, Keys("derived"), &value.Derived, value.Count*3, "derive from CLI count") return nil }, }, Overrides: []Override{{Path: Keys("count"), Value: 4, Flag: "--count"}}, }) if err != nil { t.Fatal(err) } if calls != 1 || result.Config.Count != 4 || result.Config.Derived != 12 { t.Fatalf("defaults-only CLI result = %#v, defaults calls %d", result.Config, calls) } if sources := result.Sources(); len(sources) != 1 || sources[0].Layer != LayerCLI { t.Fatalf("defaults-only CLI sources = %#v", sources) } } func TestCLISequenceAssignmentReplacesAppendAndUpsertResults(t *testing.T) { t.Parallel() type item struct { ID string `yaml:"id"` } type config struct { 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")) } result, err := testGroupedLoad(loadRequest[config]{ main: loadInput{source: declaration("upsert", "main")}, underlays: []loadGroup{{inputs: []loadInput{{source: declaration("append", "underlay")}}}}, overrides: []Override{{Path: Keys("items"), Value: []any{map[string]any{"id": "cli"}}, Flag: "--items"}}, }) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(result.Config.Items, []item{{ID: "cli"}}) { t.Fatalf("CLI sequence assignment = %#v, want complete replacement", result.Config.Items) } } func TestCLISecretReferenceUsesEffectiveLookupAndLocation(t *testing.T) { t.Parallel() type config struct { APIKey Secret `yaml:"api_key"` } options := Options[config]{ Definition: Definition[config]{Defaults: func() config { return config{APIKey: SecretEnv("DEFAULT_SECRET")} }}, Overrides: []Override{{Path: Keys("api_key"), Value: "CLI_SECRET", Flag: "--api-key"}}, } source := Bytes("secret-input", []byte("api_key: BASE_SECRET\n")) t.Run("effective reference is looked up", func(t *testing.T) { calls := 0 result, err := testLoadWithSecretLookup(source, func(name string) (string, bool) { calls++ if name != "CLI_SECRET" { t.Errorf("secret lookup name = %q", name) } return "effective-credential", true }, options) if err != nil || calls != 1 || result.Config.APIKey.Value() != "effective-credential" { t.Fatalf("CLI secret result = %#v, calls %d, error %v", result, calls, err) } info, ok := result.Metadata().Lookup(Keys("api_key")) if !ok || info.Origin.Layer != LayerCLI { t.Fatalf("CLI secret origin = %#v, %v", info, ok) } }) t.Run("missing effective reference uses CLI location", func(t *testing.T) { result, err := testLoadWithSecretLookup(source, func(name string) (string, bool) { if name != "CLI_SECRET" { t.Errorf("secret lookup name = %q", name) } return "", false }, options) if result != nil { t.Fatalf("missing secret returned a result: %#v", result) } loadErr, ok := err.(*LoadError) if !ok || len(loadErr.Diagnostics()) != 1 { t.Fatalf("missing secret error = %v", err) } diagnostic := loadErr.Diagnostics()[0] if diagnostic.Code != CodeSecretUnset || diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-2" { t.Fatalf("missing secret diagnostic = %#v", diagnostic) } }) } func TestCLIOverridesCannotHideEarlierFailuresOrAddInheritance(t *testing.T) { t.Parallel() type config struct { Count int `yaml:"count"` Child map[string]any `yaml:"child"` Parent map[string]any `yaml:"parent"` } t.Run("decode error", func(t *testing.T) { _, err := testLoad(Bytes("bad-count", []byte("count: invalid\n")), Options[config]{ Overrides: []Override{{Path: Keys("count"), Value: 3, Flag: "--count"}}, }) requireSecretLoadCode(t, err, CodeDecodeType) }) t.Run("inheritance error", func(t *testing.T) { _, err := testLoad(Bytes("missing-parent", []byte("child: {extends: missing, value: source}\n")), Options[config]{ Overrides: []Override{{Path: Keys("child"), Value: map[string]any{"value": "cli"}, Flag: "--child"}}, }) requireSecretLoadCode(t, err, CodeInheritanceMissing) }) t.Run("cannot add relationship", func(t *testing.T) { _, err := testLoad(Bytes("new-relationship", []byte("child: {value: source}\nparent: {value: parent}\n")), Options[config]{ Overrides: []Override{{Path: Keys("child"), Value: map[string]any{"extends": "parent"}, Flag: "--child"}}, }) requireSecretLoadCode(t, err, CodeOverrideDirective) }) t.Run("cannot cancel relationship", func(t *testing.T) { _, err := testLoad(Bytes("cancel-relationship", []byte("child: {extends: parent}\nparent: {value: parent}\n")), Options[config]{ Overrides: []Override{{Path: Keys("child", "extends"), Value: nil, Flag: "--extends"}}, }) requireSecretLoadCode(t, err, CodeOverrideDirective) }) }