Report schema validation and normalization results

This commit is contained in:
2026-05-11 00:16:50 +00:00
parent e2ae7f77d8
commit aeb9c4f062
3 changed files with 234 additions and 35 deletions

View File

@@ -673,11 +673,11 @@ func TestRunProcessNoStdoutWithOutput(t *testing.T) {
}
}
func TestRunProcessNormalizationConfigOverride(t *testing.T) {
func TestRunProcessReportIncludesNormalizationSummary(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
// Create segments that would normally merge but set very small max gap
// Create a test transcript with segments that will be merged
transcriptJSON := `[
{"id": 1, "speaker": "Alice", "start": 0.0, "end": 1.0, "text": "Hello"},
{"id": 2, "speaker": "Alice", "start": 1.5, "end": 2.5, "text": "world"}
@@ -685,24 +685,171 @@ func TestRunProcessNormalizationConfigOverride(t *testing.T) {
transcriptPath := filepath.Join(t.TempDir(), "transcript.json")
glossaryPath := fixturePath("tiny_glossary.yaml")
reportPath := filepath.Join(t.TempDir(), "report.json")
if err := os.WriteFile(transcriptPath, []byte(transcriptJSON), 0644); err != nil {
t.Fatalf("failed to create test transcript: %v", err)
}
// Set max segment gap to 0.1 (smaller than the 0.5 gap between segments)
exitCode := Run([]string{"process", transcriptPath, "--glossary", glossaryPath, "--normalize-max-segment-gap", "0.1"}, &stdout, &stderr)
exitCode := Run([]string{"process", transcriptPath, "--glossary", glossaryPath, "--report-json", reportPath}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
if stdout.Len() != 0 {
t.Fatalf("expected empty stdout when --output is not used but --report-json is provided, got %q", stdout.String())
}
if stderr.Len() != 0 {
t.Fatalf("expected empty stderr on success, got %q", stderr.String())
}
// Read and parse the report
reportBytes := readFile(t, reportPath)
var report reporting.ProcessReport
if err := json.Unmarshal(reportBytes, &report); err != nil {
t.Fatalf("failed to parse report JSON: %v (raw: %s)", err, string(reportBytes))
}
// Verify report includes normalization summary
if report.Status != "success" {
t.Fatalf("expected success status, got %q", report.Status)
}
if report.InputSegmentCount == nil || *report.InputSegmentCount != 2 {
t.Fatalf("expected input segment count 2, got %v", report.InputSegmentCount)
}
if report.NormalizedSegmentCount == nil || *report.NormalizedSegmentCount != 1 {
t.Fatalf("expected normalized segment count 1, got %v", report.NormalizedSegmentCount)
}
if report.NormalizationMerges == nil || *report.NormalizationMerges != 1 {
t.Fatalf("expected 1 merge, got %v", report.NormalizationMerges)
}
if report.NormalizationIDReassignments == nil || *report.NormalizationIDReassignments != 0 {
t.Fatalf("expected 0 ID reassignments (already sequential), got %v", report.NormalizationIDReassignments)
}
}
func TestRunProcessReportFailedTranscriptSchema(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
reportPath := filepath.Join(t.TempDir(), "report.json")
exitCode := Run([]string{
"process",
fixturePath("transcript_empty_speaker.json"),
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--report-json",
reportPath,
}, &stdout, &stderr)
if exitCode == 0 {
t.Fatalf("expected nonzero exit code for invalid transcript")
}
if stdout.Len() != 0 {
t.Fatalf("expected empty stdout on failure, got %q", stdout.String())
}
// Read and parse the report
reportBytes := readFile(t, reportPath)
var report reporting.ProcessReport
if err := json.Unmarshal(reportBytes, &report); err != nil {
t.Fatalf("failed to parse report JSON: %v (raw: %s)", err, string(reportBytes))
}
// Verify failed report structure
if report.Status != "failed" {
t.Fatalf("expected failed status, got %q", report.Status)
}
if report.ErrorPhase != "transcript_schema" {
t.Fatalf("expected error phase 'transcript_schema', got %q", report.ErrorPhase)
}
if report.ErrorMessage == "" {
t.Fatalf("expected error message in failed report")
}
if !strings.Contains(report.ErrorMessage, "speaker") {
t.Fatalf("expected speaker validation error, got %q", report.ErrorMessage)
}
}
func TestRunProcessReportFailedGlossarySchema(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
reportPath := filepath.Join(t.TempDir(), "report.json")
exitCode := Run([]string{
"process",
fixturePath("tiny_transcript.json"),
"--glossary",
"../core/schema/testdata/glossary_missing_fields.yaml",
"--report-json",
reportPath,
}, &stdout, &stderr)
if exitCode == 0 {
t.Fatalf("expected nonzero exit code for invalid glossary")
}
if stdout.Len() != 0 {
t.Fatalf("expected empty stdout on failure, got %q", stdout.String())
}
// Read and parse the report
reportBytes := readFile(t, reportPath)
var report reporting.ProcessReport
if err := json.Unmarshal(reportBytes, &report); err != nil {
t.Fatalf("failed to parse report JSON: %v (raw: %s)", err, string(reportBytes))
}
// Verify failed report structure
if report.Status != "failed" {
t.Fatalf("expected failed status, got %q", report.Status)
}
if report.ErrorPhase != "glossary_schema" {
t.Fatalf("expected error phase 'glossary_schema', got %q", report.ErrorPhase)
}
if report.ErrorMessage == "" {
t.Fatalf("expected error message in failed report")
}
}
func TestRunProcessReportJSONNotInStdout(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
transcriptJSON := `[
{"id": 1, "speaker": "Alice", "start": 0.0, "end": 1.0, "text": "Hello"}
]`
transcriptPath := filepath.Join(t.TempDir(), "transcript.json")
glossaryPath := fixturePath("tiny_glossary.yaml")
reportPath := filepath.Join(t.TempDir(), "report.json")
if err := os.WriteFile(transcriptPath, []byte(transcriptJSON), 0644); err != nil {
t.Fatalf("failed to create test transcript: %v", err)
}
exitCode := Run([]string{"process", transcriptPath, "--glossary", glossaryPath, "--report-json", reportPath}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
// Should NOT merge due to small max gap setting
var outputTranscript schema.Transcript
if err := schema.ParseTranscriptJSON(stdout.Bytes()); err != nil {
t.Fatalf("failed to parse output transcript: %v", err)
// Read the report to get its content
reportBytes := readFile(t, reportPath)
// Verify report JSON is not in stdout
if bytes.Contains(stdout.Bytes(), reportBytes) {
t.Fatalf("stdout was polluted with report JSON")
}
if len(outputTranscript.Segments) != 2 {
t.Fatalf("expected 2 unmerged segments due to small max gap, got %d", len(outputTranscript.Segments))
// Verify stdout only contains transcript JSON
if !json.Valid(stdout.Bytes()) {
t.Fatalf("expected valid JSON in stdout, got %q", stdout.String())
}
// Parse stdout to verify it's a transcript, not a report
var stdoutContent map[string]interface{}
if err := json.Unmarshal(stdout.Bytes(), &stdoutContent); err != nil {
t.Fatalf("failed to parse stdout as JSON: %v", err)
}
// Transcript should be an array at top level
if _, isArray := stdoutContent["segments"]; !isArray && stdoutContent["segments"] != nil {
t.Fatalf("expected transcript in stdout, got report structure")
}
}