Implement structured run reporting
This commit is contained in:
@@ -718,6 +718,99 @@ func TestRunJSONIncludesGeneratedOutputMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRunReportIncludesStructuredDryRunResults(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
destinationRoot := t.TempDir()
|
||||
writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
|
||||
configPath := testutil.WriteLocalConfigWithLinks(t, sourceRoot, destinationRoot, config.PathMappingFixed, "https://reports.example.com/latest", config.LinkPrimaryAuto, false, true, config.TransformModeIndex)
|
||||
cfg, err := config.LoadFile(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
report, err := buildRunReportWithBackendFactory(context.Background(), cfg, RunOptions{DryRun: true}, newBackendFactoryWithEnvironment)
|
||||
if err != nil {
|
||||
t.Fatalf("buildRunReportWithBackendFactory() error = %v", err)
|
||||
}
|
||||
if !report.DryRun || report.Summary.Status != "ok" || !report.Summary.DryRun {
|
||||
t.Fatalf("report dry-run/status = dry_run:%t summary:%#v, want ok dry-run", report.DryRun, report.Summary)
|
||||
}
|
||||
if got, want := len(report.Pipelines), 1; got != want {
|
||||
t.Fatalf("pipeline count = %d, want %d", got, want)
|
||||
}
|
||||
pipeline := report.Pipelines[0]
|
||||
if pipeline.ID != "reports" || pipeline.SourceBackend != config.BackendLocal || pipeline.BundleCount != 1 || strings.Join(pipeline.Destinations, ",") != "archive" {
|
||||
t.Fatalf("pipeline summary = %#v, want reports/local bundle summary", pipeline)
|
||||
}
|
||||
if got, want := len(report.Warnings), 1; got != want {
|
||||
t.Fatalf("warning count = %d, want %d", got, want)
|
||||
}
|
||||
if !strings.Contains(report.Warnings[0].Message, "path_mapping=fixed candidates=1 selected_bundle=.") {
|
||||
t.Fatalf("warning = %#v, want fixed path selection", report.Warnings[0])
|
||||
}
|
||||
if got, want := len(report.Actions), 1; got != want {
|
||||
t.Fatalf("action count = %d, want %d", got, want)
|
||||
}
|
||||
action := report.Actions[0]
|
||||
if action.PipelineID != "reports" || action.DestinationID != "archive" || action.Action != "publish_new" || action.PrimaryURL != "https://reports.example.com/latest/" {
|
||||
t.Fatalf("action = %#v, want publish_new with primary URL", action)
|
||||
}
|
||||
if action.PathMapping != config.PathMappingFixed || action.DestinationPath != "." {
|
||||
t.Fatalf("action path mapping = %q destination path = %q, want fixed root", action.PathMapping, action.DestinationPath)
|
||||
}
|
||||
if got, want := len(action.Outputs), 1; got != want {
|
||||
t.Fatalf("output count = %d, want %d", got, want)
|
||||
}
|
||||
output := action.Outputs[0]
|
||||
if output.Path != "index.html" || output.Kind != state.OutputKindGenerated || output.SourcePath != "report.md" || output.Transform != "markdown_to_html" || output.URL != "https://reports.example.com/latest/" {
|
||||
t.Fatalf("output = %#v, want generated index metadata", output)
|
||||
}
|
||||
if report.Summary.Planned != 1 || report.Summary.PublishNew != 1 || report.Summary.FixedPath != 1 || report.Summary.Failed != 0 {
|
||||
t.Fatalf("summary = %#v, want publish_new fixed path counters", report.Summary)
|
||||
}
|
||||
if len(report.OutputErrors) != 0 {
|
||||
t.Fatalf("output errors = %#v, want none", report.OutputErrors)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRunReportIncludesPartialFailures(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
firstDestination := t.TempDir()
|
||||
secondDestination := t.TempDir()
|
||||
writeSourceBundle(t, sourceRoot, "", testBundleOptions{})
|
||||
if err := os.WriteFile(filepath.Join(firstDestination, "unmanaged.txt"), []byte("data"), 0o600); err != nil {
|
||||
t.Fatalf("write unmanaged file: %v", err)
|
||||
}
|
||||
cfg, err := config.LoadFile(writeFanoutConfig(t, sourceRoot, firstDestination, secondDestination))
|
||||
if err != nil {
|
||||
t.Fatalf("load config: %v", err)
|
||||
}
|
||||
|
||||
report, err := buildRunReportWithBackendFactory(context.Background(), cfg, RunOptions{}, newBackendFactoryWithEnvironment)
|
||||
if err == nil || !IsPartialResultError(err) {
|
||||
t.Fatalf("buildRunReportWithBackendFactory() error = %v, want partial result error", err)
|
||||
}
|
||||
if report.Summary.Status != "failed" || report.Summary.Planned != 1 || report.Summary.PublishNew != 1 || report.Summary.Failed != 1 {
|
||||
t.Fatalf("summary = %#v, want one planned publish and one failure", report.Summary)
|
||||
}
|
||||
if got, want := len(report.Actions), 2; got != want {
|
||||
t.Fatalf("action count = %d, want %d", got, want)
|
||||
}
|
||||
if report.Actions[0].DestinationID != "archive-one" || report.Actions[0].Action != "error" || !strings.Contains(report.Actions[0].Reason, "fail_unmanaged") {
|
||||
t.Fatalf("first action = %#v, want archive-one error", report.Actions[0])
|
||||
}
|
||||
if report.Actions[1].DestinationID != "archive-two" || report.Actions[1].Action != "publish_new" {
|
||||
t.Fatalf("second action = %#v, want archive-two publish_new", report.Actions[1])
|
||||
}
|
||||
if got, want := len(report.OutputErrors), 1; got != want {
|
||||
t.Fatalf("output error count = %d, want %d", got, want)
|
||||
}
|
||||
outputError := report.OutputErrors[0]
|
||||
if outputError.PipelineID != "reports" || outputError.DestinationID != "archive-one" || outputError.Backend != config.BackendLocal || outputError.BundlePath != "." || !strings.Contains(outputError.Message, "fail_unmanaged") {
|
||||
t.Fatalf("output error = %#v, want archive-one unmanaged failure", outputError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunDoesNotNotifyForSkippedDestination(t *testing.T) {
|
||||
sourceRoot := t.TempDir()
|
||||
destinationRoot := t.TempDir()
|
||||
|
||||
Reference in New Issue
Block a user