package configkit import ( "errors" "reflect" "sync" "testing" ) type layeredItem struct { Value string `yaml:"value"` } type layeredConfig struct { Shared struct { Equal string `yaml:"equal"` Imported string `yaml:"imported"` Profile string `yaml:"profile"` } `yaml:"shared"` Values []int `yaml:"values"` Items map[string]layeredItem `yaml:"items"` Value any `yaml:"value"` Nullable *string `yaml:"nullable"` Derived int `yaml:"derived"` Empty map[string]string `yaml:"empty"` } func TestLayeredProvenanceAgreesWithResolvedSnapshot(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", `configkit: imports: [{path: imported.yml}] shared: {equal: same} values: [1, 2] items: keep: {value: 'yes'} remove: {value: 'no'} value: {child: base} nullable: base empty: {} `) importPath := writeImportFile(t, directory, "imported.yml", "shared: {imported: base}\n") profilePath := writeImportFile(t, directory, "profiles/testing.yml", `shared: {profile: selected} values: [3, 4] value: selected nullable: null empty: {} `) defaultNullable := "default" definition := Definition[layeredConfig]{ Defaults: func() layeredConfig { defaults := layeredConfig{ Values: []int{0, 0}, Value: map[string]any{"child": "default"}, Nullable: &defaultNullable, Empty: map[string]string{"retained": "default"}, } defaults.Shared.Equal = "same" return defaults }, Resolve: func(configuration *layeredConfig, resolution *Resolution) error { Normalize(resolution, Keys("values"), &configuration.Values, []int{5}, "bounded values", Keys("shared", "imported"), Keys("shared", "imported")) Normalize(resolution, Keys("values"), &configuration.Values, []int{5}, "bounded values", Keys("shared", "imported")) delete(configuration.Items, "remove") resolution.Record(Keys("items"), ChangeNormalization, "removed disabled items") Default(resolution, Keys("derived"), &configuration.Derived, 7, "derived from equal", Keys("shared", "equal"), Keys("shared", "equal")) resolution.Warning(Keys("values"), "values were bounded", Keys("shared", "imported")) return nil }, } result, err := Load[layeredConfig](File(mainPath), Options[layeredConfig]{ Imports: true, Profile: "testing", Definition: definition, }) if err != nil { var loadError *LoadError _ = errors.As(err, &loadError) t.Fatalf("%v: %#v", err, loadError.Diagnostics()) } wantSources := []string{canonicalTestPath(t, mainPath), canonicalTestPath(t, importPath), canonicalTestPath(t, profilePath)} sources := result.Sources() gotSources := make([]string, len(sources)) for index, source := range sources { gotSources[index] = source.Path } if !reflect.DeepEqual(gotSources, wantSources) || sources[2].Layer != LayerProfile { t.Fatalf("sources = %#v", sources) } if result.Config.Shared.Equal != "same" || result.Config.Shared.Imported != "base" || result.Config.Shared.Profile != "selected" || !reflect.DeepEqual(result.Config.Values, []int{5}) || result.Config.Nullable != nil || result.Config.Derived != 7 || result.Config.Empty["retained"] != "default" { t.Fatalf("configuration = %#v", result.Config) } metadata := result.Metadata() equal, _ := metadata.Lookup(Keys("shared", "equal")) derived, _ := metadata.Lookup(Keys("derived")) nullable, _ := metadata.Lookup(Keys("nullable")) empty, _ := metadata.Lookup(Keys("empty")) firstValue, _ := metadata.Lookup(Keys("values").Index(0)) if !equal.Explicit || equal.Origin.Layer != LayerBase || derived.Explicit || derived.Origin.Operation != ChangeDerivedDefault || !nullable.Explicit || !nullable.Null || nullable.Origin.Layer != LayerProfile || !empty.Explicit || empty.Origin.Layer != LayerProfile || len(empty.Origin.Locations) != 2 || !firstValue.Explicit || firstValue.Origin.Operation != ChangeNormalization { t.Fatalf("metadata = equal %#v derived %#v nullable %#v empty %#v value %#v", equal, derived, nullable, empty, firstValue) } if _, ok := metadata.Lookup(Keys("value", "child")); ok { t.Fatal("profile-removed child remains effective") } if _, ok := metadata.Lookup(Keys("values").Index(1)); ok { t.Fatal("resolution-removed sequence index remains effective") } if _, ok := metadata.Lookup(Keys("items", "remove", "value")); ok { t.Fatal("resolution-removed mapping child remains effective") } assertHistoryLayersAndPaths(t, metadata.History(Keys("values").Index(1)), []Layer{LayerDefault, LayerBase, LayerProfile, LayerResolution}, []string{"/values/1", "/values/1", "/values", "/values"}) assertHistoryLayersAndPaths(t, metadata.History(Keys("value", "child")), []Layer{LayerDefault, LayerBase, LayerProfile}, []string{"/value/child", "/value/child", "/value"}) removedHistory := metadata.History(Keys("items", "remove", "value")) assertHistoryLayersAndPaths(t, removedHistory, []Layer{LayerBase, LayerResolution}, []string{"/items/remove/value", "/items"}) if !reflect.DeepEqual(removedHistory[1].Reasons, []string{"removed disabled items"}) { t.Fatalf("removed history = %#v", removedHistory) } valuesHistory := metadata.History(Keys("values").Index(0)) lastValue := valuesHistory[len(valuesHistory)-1] if !reflect.DeepEqual(lastValue.Reasons, []string{"bounded values"}) || len(lastValue.Dependencies) != 1 || lastValue.Dependencies[0].String() != "/shared/imported" { t.Fatalf("canonical resolution history = %#v", lastValue) } schema, issue := buildSchema(reflect.TypeFor[layeredConfig]()) if issue != nil { t.Fatal(issue) } projected, projectionIssue := projectConfiguration(schema, reflect.ValueOf(result.Config), defaultProjectionLimits()) if projectionIssue != nil || !configurationValuesEqual(projected, result.final) { t.Fatalf("typed projection and private snapshot disagree: %#v", projectionIssue) } metadata.history[Keys("values").Index(0).String()][0].Origin.Layer = "mutated" sources[1].ImportedFrom.SourceID = "mutated" diagnostics := result.Diagnostics() diagnostics[0].Related[0].SourceID = "mutated" result.Config.Values[0] = 99 freshHistory := result.Metadata().History(Keys("values").Index(0)) if freshHistory[0].Origin.Layer == "mutated" || result.Sources()[1].ImportedFrom.SourceID == "mutated" || result.Diagnostics()[0].Related[0].SourceID == "mutated" { t.Fatal("returned inspection records alias private state") } snapshotValue, ok := documentAtPath(result.final, Keys("values").Index(0)) if !ok || snapshotValue.Text != "5" { t.Fatal("caller configuration mutation changed the private final snapshot") } } func TestSelectedProfileLoadsRemainIndependentConcurrently(t *testing.T) { directory := t.TempDir() mainPath := writeImportFile(t, directory, "main.yml", "values: [1, 2]\n") writeImportFile(t, directory, "profiles/testing.yml", "values: [3, 4]\n") options := Options[layeredConfig]{Profile: "testing"} const loads = 16 results := make(chan *Result[layeredConfig], loads) errors := make(chan error, loads) var group sync.WaitGroup for index := 0; index < loads; index++ { group.Add(1) go func() { defer group.Done() result, err := Load[layeredConfig](File(mainPath), options) if err != nil { errors <- err return } results <- result }() } group.Wait() close(results) close(errors) for err := range errors { t.Fatal(err) } var collected []*Result[layeredConfig] for result := range results { collected = append(collected, result) } if len(collected) != loads { t.Fatalf("completed loads = %d", len(collected)) } collected[0].Config.Values[0] = 99 collected[0].metadata.fields[Keys("values").String()] = FieldInfo{} for _, result := range collected[1:] { if !reflect.DeepEqual(result.Config.Values, []int{3, 4}) { t.Fatalf("concurrent result contaminated = %#v", result.Config.Values) } if info, ok := result.Metadata().Lookup(Keys("values")); !ok || info.Origin.Layer != LayerProfile { t.Fatalf("concurrent metadata contaminated = %#v, %v", info, ok) } } } func assertHistoryLayersAndPaths(t *testing.T, history []Change, layers []Layer, paths []string) { t.Helper() if len(history) != len(layers) || len(history) != len(paths) { t.Fatalf("history = %#v", history) } for index, change := range history { if change.Origin.Layer != layers[index] || change.Path.String() != paths[index] { t.Fatalf("history[%d] = %#v", index, change) } } }