package configkit_test import ( "bytes" "errors" "strings" "testing" "testing/fstest" configkit "gitea.maximumdirect.net/eric/ConfigKit" ) func TestFacadeCompositionConflictKeepsClaimantOrder(t *testing.T) { filesystem := configkit.NewFilesystem("bundle", fstest.MapFS{ "a.yml": &fstest.MapFile{Data: []byte(`configkit: imports: - path: z.yml `)}, "b.yml": &fstest.MapFile{Data: []byte("x: b\n")}, "c.yml": &fstest.MapFile{Data: []byte("x: c\n")}, "z.yml": &fstest.MapFile{Data: []byte("x: z\n")}, }) main := configkit.Mapping("main", map[string]any{ "configkit": map[string]any{"imports": []any{ map[string]any{"path": "a.yml"}, map[string]any{"path": "b.yml"}, map[string]any{"path": "c.yml"}, }}, }) _, err := configkit.Load[map[string]any](main, configkit.Options[map[string]any]{ Imports: true, Filesystem: &configkit.FilesystemContext{ FS: &filesystem, Root: ".", BaseDir: ".", }, }) var loadError *configkit.LoadError if !errors.As(err, &loadError) { t.Fatalf("Load error = %v, want composition LoadError", err) } diagnostics := loadError.Diagnostics() if len(diagnostics) != 1 { t.Fatalf("diagnostics = %#v, want one composition conflict", diagnostics) } diagnostic := diagnostics[0] if diagnostic.Code != configkit.CodeComposeConflict || diagnostic.Path.String() != "/x" || diagnostic.Primary == nil { t.Fatalf("composition diagnostic = %#v", diagnostic) } pathsByID := make(map[string]string) for _, source := range loadError.Sources() { pathsByID[source.ID] = source.Path } claimant := func(location configkit.Location) string { for _, name := range []string{"b.yml", "c.yml", "z.yml"} { if pathsByID[location.SourceID] == name || strings.HasSuffix(pathsByID[location.SourceID], "/"+name) { return strings.TrimSuffix(name, ".yml") } } return "" } if got := claimant(*diagnostic.Primary); got != "b" { t.Fatalf("primary claimant = %q, want b (sources: %#v)", got, pathsByID) } if len(diagnostic.Related) != 2 || claimant(diagnostic.Related[0]) != "c" || claimant(diagnostic.Related[1]) != "z" { t.Fatalf("related claimants = %#v, want c then z", diagnostic.Related) } var output bytes.Buffer if err := configkit.WriteDiagnostics(&output, diagnostics); err != nil { t.Fatal(err) } positions := []int{ strings.Index(output.String(), diagnostic.Primary.SourceID), strings.Index(output.String(), diagnostic.Related[0].SourceID), strings.Index(output.String(), diagnostic.Related[1].SourceID), } if positions[0] < 0 || positions[1] <= positions[0] || positions[2] <= positions[1] { t.Fatalf("rendered claimant order = %v in %q", positions, output.String()) } diagnostics[0].Related[0].SourceID = "mutated" if got := claimant(loadError.Diagnostics()[0].Related[0]); got != "c" { t.Fatalf("LoadError.Diagnostics retained caller mutation; first claimant = %q", got) } }