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 entryCountExplicit := 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 entryCountExplicit = r.IsExplicit(configkit.Keys("config", "count")) 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 || entryCountExplicit || 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 info, ok := result.Metadata().Lookup(configkit.Keys("config", "count")); !ok || info.Origin.Layer != configkit.LayerResolution { t.Fatalf("final count info = %#v, %t", info, ok) } if _, ok := result.Explain(configkit.Keys("config", "count")); !ok { t.Fatal("missing resolution explanation") } }