package ckconfig import ( "bytes" "context" "io" "reflect" "strings" "testing" commandkit "gitea.maximumdirect.net/eric/CommandKit" configkit "gitea.maximumdirect.net/eric/ConfigKit" "go.yaml.in/yaml/v4" ) func TestSourcesProjectNativeEvidenceInTextAndYAML(t *testing.T) { servicePath, err := configkit.ParsePath("/services") if err != nil { t.Fatal(err) } otherPath, err := configkit.ParsePath("/other") if err != nil { t.Fatal(err) } versionPath, err := configkit.ParsePath("/version") if err != nil { t.Fatal(err) } nullVersionPath, err := configkit.ParsePath("/metadata/version") if err != nil { t.Fatal(err) } reader := &sourceInventory{values: []configkit.SourceInfo{ { ID: "root", Label: "root source", Path: "root.yml", PlacementMode: configkit.PlacementModeMapping, }, { ID: "item", Label: "sequence item", Path: "item.yml", Destination: servicePath, PlacementMode: configkit.PlacementModeSequenceItem, DestinationUnresolved: true, VersionCheck: &configkit.SourceVersionCheck{ Path: versionPath, Version: configkit.Version{Present: true, Text: "v2"}, Location: &configkit.Location{SourceID: "item.yml", Line: 4, Column: 2}, }, }, { ID: "null", Label: "null version", Path: "null.yml", Destination: otherPath, PlacementMode: configkit.PlacementModeMapping, VersionCheck: &configkit.SourceVersionCheck{ Path: nullVersionPath, Version: configkit.Version{Present: true, Null: true}, Redacted: true, }, }, }} app := newConfigTestApp(t, Command(Suite[struct{}, struct{}]{Scopes: []Scope[struct{}, struct{}]{ {Name: "app", Operations: []Operation{Sources}, Acquire: func(context.Context, Request, struct{}, struct{}) (commandkit.Acquired[View], error) { return commandkit.Acquired[View]{Value: View{Sources: reader}}, nil }}, }})) var yamlOutput bytes.Buffer outcome := app.Execute(context.Background(), []string{"config", "sources", "--format", "yaml"}, commandkit.Streams{Out: &yamlOutput, Err: io.Discard}) if outcome.Kind != commandkit.OutcomeSuccess || reader.calls != 1 { t.Fatalf("YAML source report outcome=%#v calls=%d", outcome, reader.calls) } var report struct { Scope string `yaml:"scope"` Sources []struct { ID string `yaml:"id"` Destination string `yaml:"destination"` Placement string `yaml:"placement"` DestinationUnresolved bool `yaml:"destination_unresolved"` VersionCheck *struct { Path string `yaml:"path"` Version struct { Present bool `yaml:"present"` Null bool `yaml:"null"` Text string `yaml:"text"` } `yaml:"version"` Location *struct { SourceID string `yaml:"source_id"` Line int `yaml:"line"` Column int `yaml:"column"` } `yaml:"location"` Redacted bool `yaml:"redacted"` } `yaml:"version_check"` } `yaml:"sources"` } if err := yaml.Unmarshal(yamlOutput.Bytes(), &report); err != nil { t.Fatalf("decode source report: %v\n%s", err, yamlOutput.String()) } if report.Scope != "app" || len(report.Sources) != 3 { t.Fatalf("source report scope/order = %q, %#v", report.Scope, report.Sources) } if got := []string{report.Sources[0].ID, report.Sources[1].ID, report.Sources[2].ID}; !reflect.DeepEqual(got, []string{"root", "item", "null"}) { t.Fatalf("source order = %v", got) } if root := report.Sources[0]; root.Destination != "" || root.Placement != "mapping" || root.DestinationUnresolved || root.VersionCheck != nil { t.Errorf("root source projection = %#v", root) } item := report.Sources[1] if item.Destination != "/services" || item.Placement != "sequence_item" || !item.DestinationUnresolved { t.Errorf("sequence item projection = %#v", item) } if check := item.VersionCheck; check == nil || check.Path != "/version" || !check.Version.Present || check.Version.Null || check.Version.Text != "v2" || check.Location == nil || check.Location.SourceID != "item.yml" || check.Location.Line != 4 || check.Location.Column != 2 || check.Redacted { t.Errorf("text version evidence projection = %#v", check) } nullSource := report.Sources[2] if nullSource.Destination != "/other" || nullSource.Placement != "mapping" || nullSource.DestinationUnresolved { t.Errorf("mapping projection = %#v", nullSource) } if check := nullSource.VersionCheck; check == nil || check.Path != "/metadata/version" || !check.Version.Present || !check.Version.Null || check.Version.Text != "" || check.Location != nil || !check.Redacted { t.Errorf("null/redacted version evidence projection = %#v", check) } var textOutput bytes.Buffer outcome = app.Execute(context.Background(), []string{"config", "sources", "--format", "text"}, commandkit.Streams{Out: &textOutput, Err: io.Discard}) if outcome.Kind != commandkit.OutcomeSuccess || reader.calls != 2 { t.Fatalf("text source report outcome=%#v calls=%d", outcome, reader.calls) } for _, fragment := range []string{ `id: "root"`, `destination: ""`, `placement: "mapping"`, "destination_unresolved: false", "version_check: null", `destination: "/services"`, `placement: "sequence_item"`, "destination_unresolved: true", `path: "/version"`, `text: "v2"`, `destination: "/other"`, `path: "/metadata/version"`, "null: true", `text: ""`, "location: null", "redacted: true", } { if !strings.Contains(textOutput.String(), fragment) { t.Errorf("text report missing %q:\n%s", fragment, textOutput.String()) } } }