package processreport import ( "path/filepath" "testing" "time" "gitea.maximumdirect.net/eric/audita/internal/core/chunking" "gitea.maximumdirect.net/eric/audita/internal/core/diagnostics" "gitea.maximumdirect.net/eric/audita/internal/core/normalization" "gitea.maximumdirect.net/eric/audita/internal/core/reporting" "gitea.maximumdirect.net/eric/audita/internal/framework/proposals" "gitea.maximumdirect.net/eric/audita/internal/framework/runner" ) func TestBuildSuccessReportMapsExecutionFacts(t *testing.T) { startedAt := time.Date(2026, 5, 23, 10, 0, 0, 0, time.UTC) completedAt := startedAt.Add(time.Second) configVersion := 4 targetSections := 2 inputSegments := 5 outputSegments := 4 merges := 1 reassigned := 2 differentSpeakers := 3 report := Build(BuildInput{ Status: "success", TranscriptPath: "transcript.json", GlossaryPath: "glossary.yaml", OutputPath: "out.json", Modules: []string{"grammar"}, OutputSchema: "default", ConfigVersion: &configVersion, StartedAt: startedAt, CompletedAt: completedAt, RunDirectoryPath: filepath.Join( "tmp", "audita-run", ), NormalizationSummary: &normalization.NormalizationSummary{ InputSegmentCount: inputSegments, OutputSegmentCount: outputSegments, MergesPerformed: merges, IDsReassigned: reassigned, SkippedMerges: struct { DifferentSpeakers int `json:"different_speakers"` GapTooLarge int `json:"gap_too_large"` DurationExceeded int `json:"duration_exceeded"` TokenLimitExceeded int `json:"token_limit_exceeded"` }{ DifferentSpeakers: differentSpeakers, }, }, ChunkingSummary: &chunking.Summary{ ChunkCount: 3, MinEstimatedTokens: 10, MaxEstimatedTokens: 20, TotalEstimatedTokens: 45, TargetSections: &targetSections, MaxSectionTokens: 200, MinSectionTokens: 50, }, RunOutput: &runner.RunOutput{ ModuleResults: []runner.ModuleResult{ { ModuleKey: "grammar", ModuleInstance: "grammar", ReplacementPolicy: proposals.ReplacementPolicyRequireUnique, Status: runner.ModuleStatusSuccess, ProposalCount: 2, ValidatorDecisions: []runner.ValidatorDecisionRecord{ { ValidatorName: "proposal_shape", ProposalIndex: 1, Approved: true, ReasonCode: "approved", Message: "ok", DiagnosticArtifactPath: "diagnostics/validator.json", }, }, ValidatorRejected: []runner.ValidatorRejectedChange{ { ValidatorName: "protected_term", ProposalIndex: 2, ModuleKey: "grammar", ModuleInstance: "grammar", TargetSegmentID: 7, OriginalText: "old", CorrectedText: "new", ReasonCode: "protected_term", Message: "blocked", }, }, AppliedChanges: []proposals.AppliedChange{ {ProposalIndex: 1, TargetSegmentID: 7, OriginalText: "old", CorrectedText: "new"}, }, SkippedChanges: []proposals.SkippedChange{ {ProposalIndex: 3, TargetSegmentID: 8, SkipReason: proposals.SkipReasonMissingSegment}, }, StartedAt: startedAt, CompletedAt: completedAt, }, }, }, }) if report.ReportMetadata.ReportSchemaName != reporting.DefaultProcessReportSchemaName || report.ReportMetadata.ReportSchemaVersion != reporting.DefaultProcessReportSchemaVersion || report.ReportMetadata.OutputSchema != "default" || report.ReportMetadata.ConfigVersion == nil || *report.ReportMetadata.ConfigVersion != configVersion { t.Fatalf("unexpected report metadata: %+v", report.ReportMetadata) } if report.Phase != "default_pipeline" || report.Operation != "process" || report.Status != "success" { t.Fatalf("unexpected process identity fields: phase=%q operation=%q status=%q", report.Phase, report.Operation, report.Status) } if report.Diagnostics == nil || report.Diagnostics.CorrectionLedgerPath != filepath.Join("tmp", "audita-run", diagnostics.ArtifactCorrectionLedger) { t.Fatalf("unexpected diagnostics metadata: %+v", report.Diagnostics) } if report.InputSegmentCount == nil || *report.InputSegmentCount != inputSegments || report.NormalizationSkipped.DifferentSpeakers == nil || *report.NormalizationSkipped.DifferentSpeakers != differentSpeakers { t.Fatalf("unexpected normalization summary: %+v", report) } if report.Chunking == nil || report.Chunking.ChunkCount != 3 || report.Chunking.TargetSections == nil || *report.Chunking.TargetSections != targetSections { t.Fatalf("unexpected chunking summary: %+v", report.Chunking) } if report.ModulesSummary == nil || report.ModulesSummary.ModuleCount != 1 || report.ModulesSummary.TotalAppliedChanges != 1 || report.ModulesSummary.TotalSkippedChanges != 2 { t.Fatalf("unexpected modules summary: %+v", report.ModulesSummary) } if len(report.ModuleResults) != 1 || len(report.ModuleResults[0].ValidatorDecisions) != 1 || len(report.ModuleResults[0].ValidatorRejected) != 1 { t.Fatalf("unexpected module reports: %+v", report.ModuleResults) } } func TestBuildFailedReportPreservesErrorAndFailureSummary(t *testing.T) { startedAt := time.Date(2026, 5, 23, 10, 0, 0, 0, time.UTC) completedAt := startedAt.Add(time.Second) report := Build(BuildInput{ Status: "failed", TranscriptPath: "transcript.json", GlossaryPath: "glossary.yaml", Modules: []string{"grammar"}, OutputSchema: "default", StartedAt: startedAt, CompletedAt: completedAt, ErrorPhase: "module", ErrorMessage: "module failed", RunDirectoryPath: "run-dir", RunOutput: &runner.RunOutput{ ModuleResults: []runner.ModuleResult{ { ModuleKey: "grammar", ModuleInstance: "grammar", Status: runner.ModuleStatusFailed, ErrorMessage: "module failed", StartedAt: startedAt, CompletedAt: completedAt, }, }, }, }) if report.Status != "failed" || report.ErrorPhase != "module" || report.ErrorMessage != "module failed" { t.Fatalf("unexpected failure fields: %+v", report) } if report.Diagnostics == nil || report.Diagnostics.ErrorLogPath == "" { t.Fatalf("expected failure diagnostics metadata, got %+v", report.Diagnostics) } if report.ModulesSummary == nil || report.ModulesSummary.FailedModuleInstance != "grammar" { t.Fatalf("unexpected failed module summary: %+v", report.ModulesSummary) } }