package configkit_test import ( "math" "reflect" "strings" "testing" "testing/fstest" configkit "gitea.maximumdirect.net/eric/ConfigKit" ) type mappingFacadeConfig struct { Enabled bool `yaml:"enabled"` Count uint64 `yaml:"count"` Optional *string `yaml:"optional"` Items []string `yaml:"items"` Name string `yaml:"name"` } func TestFacadeMappingSnapshotsAndMatchesYAML(t *testing.T) { items := []any{"one", "two"} values := map[string]any{ "name": "original", "enabled": false, "count": ^uint64(0), "optional": nil, "items": items, } source := configkit.Mapping("programmatic", values) values["name"] = "mutated" items[0] = "mutated" for range 2 { result, err := configkit.Load[mappingFacadeConfig](source) if err != nil { t.Fatal(err) } want := mappingFacadeConfig{Count: ^uint64(0), Items: []string{"one", "two"}, Name: "original"} if !reflect.DeepEqual(result.Config, want) { t.Fatalf("mapping config = %#v, want %#v", result.Config, want) } if sources := result.Sources(); len(sources) != 1 || sources[0].ID != "source-1" || sources[0].Kind != configkit.SourceKindMapping || sources[0].Layer != configkit.LayerBase || sources[0].Path != "" { t.Fatalf("mapping sources = %#v", sources) } info, ok := result.Metadata().Lookup(configkit.Keys("name")) if !ok || len(info.Origin.Locations) != 1 || info.Origin.Locations[0].SourceID != "source-1" || info.Origin.Locations[0].Line != 0 { t.Fatalf("mapping name origin = %#v, %v", info, ok) } fromYAML, err := configkit.Load[mappingFacadeConfig](configkit.Bytes("equivalent", []byte("enabled: false\ncount: 18446744073709551615\noptional: null\nitems: [one, two]\nname: original\n"))) if err != nil || !reflect.DeepEqual(result.Config, fromYAML.Config) { t.Fatalf("equivalent YAML = %#v, %v", fromYAML, err) } } } func TestFacadeMappingNullEmptyAndVersion(t *testing.T) { result, err := configkit.Load[mappingFacadeConfig](configkit.Mapping("empty", map[string]any{})) if err != nil || len(result.Sources()) != 1 { t.Fatalf("empty mapping = %#v, %v", result, err) } var nilSequence []any var nilMap map[string]any var checked string result, err = configkit.Load(configkit.Mapping("nullable", map[string]any{ "items": nilSequence, "optional": nilMap, "enabled": false, "count": uint64(0), }), configkit.Options[mappingFacadeConfig]{Definition: configkit.Definition[mappingFacadeConfig]{ Version: &configkit.VersionRule{Path: configkit.Keys("count"), Check: func(version configkit.Version, validation *configkit.Validation) error { checked = version.Text return nil }}, }}) if err != nil || checked != "0" || result.Config.Items != nil || result.Config.Optional != nil { t.Fatalf("null and zero mapping = %#v, checked %q, %v", result, checked, err) } info, ok := result.Metadata().Lookup(configkit.Keys("items")) if !ok || !info.Null || !info.Explicit { t.Fatalf("null metadata = %#v, %v", info, ok) } } func TestFacadeMappingImportsUseExplicitContext(t *testing.T) { namespace := configkit.NewFilesystem("bundle", fstest.MapFS{ "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"}}}, "enabled": true, }) result, err := configkit.Load(source, configkit.Options[mappingFacadeConfig]{ Imports: true, Filesystem: &configkit.FilesystemContext{Root: "root", BaseDir: "root", FS: &namespace}, }) if err != nil || result.Config.Name != "imported" || !result.Config.Enabled { t.Fatalf("mapping import = %#v, %v", result, err) } if sources := result.Sources(); len(sources) != 2 || sources[0].Kind != configkit.SourceKindMapping || sources[1].Kind != configkit.SourceKindFilesystem || sources[1].Layer != configkit.LayerBase { t.Fatalf("mapping import sources = %#v", sources) } checkMappingCode(t, source, configkit.Options[mappingFacadeConfig]{Imports: true}, configkit.CodeOptionsInvalid) checkMappingCode(t, source, configkit.Options[mappingFacadeConfig]{}, configkit.CodeImportDisabled) } func TestFacadeMappingDeferredValueDiagnostics(t *testing.T) { cycle := map[string]any{} cycle["secret"] = cycle for _, test := range []struct { name string source configkit.Source want configkit.Code path string }{ {"nil root", configkit.Mapping("nil", nil), configkit.CodeSourceInvalid, ""}, {"bad label", configkit.Mapping("bad\nlabel", map[string]any{}), configkit.CodeSourceInvalid, ""}, {"unsupported", configkit.Mapping("bad", map[string]any{"secret": struct{ Value string }{"canary-value"}}), configkit.CodeSourceInvalid, "/secret"}, {"nonfinite", configkit.Mapping("bad", map[string]any{"count": math.NaN()}), configkit.CodeSourceInvalid, "/count"}, {"cycle", configkit.Mapping("bad", cycle), configkit.CodeSourceInvalid, "/secret"}, {"too many bytes", configkit.Mapping("bad", map[string]any{"name": strings.Repeat("x", 8<<20+1)}), configkit.CodeSourceLimitExceeded, "/name"}, } { t.Run(test.name, func(t *testing.T) { loadError := checkMappingCode(t, test.source, configkit.Options[mappingFacadeConfig]{}, test.want) diagnostic := loadError.Diagnostics()[0] if diagnostic.Path.String() != test.path || diagnostic.Primary == nil || diagnostic.Primary.SourceID != "source-1" || strings.Contains(diagnostic.Message, "canary-value") { t.Fatalf("diagnostic = %#v", diagnostic) } }) } } func checkMappingCode(t *testing.T, source configkit.Source, options configkit.Options[mappingFacadeConfig], want configkit.Code) *configkit.LoadError { t.Helper() _, err := configkit.Load(source, options) loadError, ok := err.(*configkit.LoadError) if !ok || len(loadError.Diagnostics()) == 0 || loadError.Diagnostics()[0].Code != want { t.Fatalf("error = %#v, want %s", err, want) } return loadError }