package configkit_test import ( "reflect" "testing" "testing/fstest" configkit "gitea.maximumdirect.net/eric/ConfigKit" ) func TestUnderlaySequenceDescendantOrigins(t *testing.T) { for _, later := range []bool{false, true} { name := "first tier" if later { name = "later tier" } 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")}, "items.yml": &fstest.MapFile{Data: []byte("items: [{name: first, nested: [false]}]\n")}, }) tiers := []configkit.Underlay{} if later { tiers = append(tiers, configkit.Underlay{Inputs: []configkit.Input{{Source: configkit.Mapping("lower", map[string]any{"other": true})}}}) } tiers = append(tiers, configkit.Underlay{Inputs: []configkit.Input{{Source: bundle.File("main.yml"), Imports: true}}}) paths := []string{"/items", "/items/0", "/items/0/name", "/items/0/nested", "/items/0/nested/0"} entry := map[string]configkit.Origin{} result, err := configkit.LoadLayered[map[string]any](configkit.Input{Source: configkit.Mapping("main", map[string]any{"main": true})}, configkit.LayeredOptions[map[string]any]{ Underlays: tiers, Definition: configkit.Definition[map[string]any]{Resolve: func(_ *map[string]any, resolution *configkit.Resolution) error { for _, pointer := range paths { path, _ := configkit.ParsePath(pointer) info, ok := resolution.InputInfo(path) if !ok { t.Errorf("missing resolver input %s", pointer) } entry[pointer] = info.Origin } return nil }}, }) if err != nil { t.Fatal(err) } var sourceID string for _, source := range result.Sources() { if source.Path == "items.yml" { sourceID = source.ID } } if sourceID == "" { t.Fatal("missing imported source") } for _, pointer := range paths { path, _ := configkit.ParsePath(pointer) info, ok := result.Metadata().Lookup(path) if !ok || info.Origin.Layer != configkit.LayerUnderlay || len(info.Origin.Locations) != 1 || info.Origin.Locations[0].SourceID != sourceID { t.Errorf("%s origin = %+v", pointer, info.Origin) } if !reflect.DeepEqual(entry[pointer], info.Origin) { t.Errorf("%s resolver origin differs", pointer) } explanation, ok := result.Explain(path) if !ok || !reflect.DeepEqual(explanation.Origin, info.Origin) { t.Errorf("%s explanation origin differs", pointer) } history := result.Metadata().History(path) if len(history) == 0 || !reflect.DeepEqual(history[len(history)-1].Origin, info.Origin) { t.Errorf("%s history = %+v", pointer, history) } } }) } } func TestUnderlayHistoryRetainsLaterMappingContributors(t *testing.T) { result, err := configkit.LoadLayered[map[string]any](configkit.Input{Source: configkit.NoSource()}, configkit.LayeredOptions[map[string]any]{Underlays: []configkit.Underlay{ {Inputs: []configkit.Input{{Source: configkit.Mapping("one", map[string]any{"x": 1})}}}, {Inputs: []configkit.Input{{Source: configkit.Mapping("two", map[string]any{"x": map[string]any{}})}}}, {Inputs: []configkit.Input{{Source: configkit.Mapping("three", map[string]any{"x": map[string]any{"y": 2}})}}}, }}) if err != nil { t.Fatal(err) } path := configkit.Keys("x") info, _ := result.Metadata().Lookup(path) wantOrigin := configkit.Origin{Layer: configkit.LayerUnderlay, Locations: []configkit.Location{{SourceID: "source-2"}, {SourceID: "source-3"}}} if !reflect.DeepEqual(info.Origin, wantOrigin) { t.Fatalf("effective origin = %+v, want %+v", info.Origin, wantOrigin) } history := result.Metadata().History(path) if len(history) != 3 || !reflect.DeepEqual(history[len(history)-1].Origin, info.Origin) { t.Fatalf("history = %+v, effective origin = %+v", history, info.Origin) } explanation, ok := result.Explain(path) if !ok || !reflect.DeepEqual(explanation.Origin, info.Origin) { t.Fatal("explanation and metadata disagree") } if len(explanation.History) != 3 { t.Fatalf("explanation history = %+v", explanation.History) } last := explanation.History[len(explanation.History)-1].Origin if last.Layer != configkit.LayerUnderlay || len(last.Locations) != 1 || last.Locations[0].SourceID != "source-3" { t.Fatalf("last explanation origin = %+v", last) } }