Wire schema validation into process command
This commit is contained in:
@@ -341,49 +341,71 @@ func TestRunProcessReportJSONBestEffortOnFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessCLIOverridesEnvironment(t *testing.T) {
|
||||
func TestRunProcessInvalidTranscriptSchema(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
t.Setenv("AUDITA_MODEL", "env-model")
|
||||
t.Setenv("AUDITA_VALIDATION_LLM_CONCURRENCY", "2")
|
||||
|
||||
transcriptPath := fixturePath("tiny_transcript.json")
|
||||
glossaryPath := fixturePath("tiny_glossary.yaml")
|
||||
|
||||
var captured processInvocation
|
||||
originalRunner := processRunner
|
||||
processRunner = func(inv processInvocation, output io.Writer) error {
|
||||
_ = output
|
||||
captured = inv
|
||||
return nil
|
||||
exitCode := Run([]string{"process", fixturePath("transcript_empty_speaker.json"), "--glossary", fixturePath("tiny_glossary.yaml")}, &stdout, &stderr)
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("expected nonzero exit code for invalid transcript schema")
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
processRunner = originalRunner
|
||||
})
|
||||
if stdout.Len() != 0 {
|
||||
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "invalid transcript:") {
|
||||
t.Fatalf("expected invalid transcript error, got %q", stderr.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "speaker") {
|
||||
t.Fatalf("expected speaker validation error, got %q", stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
exitCode := Run([]string{
|
||||
"process",
|
||||
transcriptPath,
|
||||
"--glossary",
|
||||
glossaryPath,
|
||||
"--model",
|
||||
"cli-model",
|
||||
"--validation-llm-concurrency",
|
||||
"5",
|
||||
}, &stdout, &stderr)
|
||||
func TestRunProcessValidBareArrayTranscript(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
exitCode := Run([]string{"process", "../core/schema/testdata/transcript_bare_array.json", "--glossary", fixturePath("tiny_glossary.yaml")}, &stdout, &stderr)
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("expected exit code 0, got %d", exitCode)
|
||||
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
|
||||
}
|
||||
if captured.Config.PrimaryLLM.Model != "cli-model" {
|
||||
t.Fatalf("expected CLI model override, got %q", captured.Config.PrimaryLLM.Model)
|
||||
if stderr.Len() != 0 {
|
||||
t.Fatalf("expected empty stderr on success, got %q", stderr.String())
|
||||
}
|
||||
if captured.Config.ValidationLLM.Concurrency == nil || *captured.Config.ValidationLLM.Concurrency != 5 {
|
||||
t.Fatalf("expected CLI validation concurrency override, got %#v", captured.Config.ValidationLLM.Concurrency)
|
||||
|
||||
// Should produce valid JSON output
|
||||
if !json.Valid(stdout.Bytes()) {
|
||||
t.Fatalf("expected valid JSON output, got %q", stdout.String())
|
||||
}
|
||||
if captured.GlossaryPath != glossaryPath {
|
||||
t.Fatalf("unexpected glossary path: %q", captured.GlossaryPath)
|
||||
|
||||
// Parse the output to verify it's a proper transcript
|
||||
var output any
|
||||
if err := json.Unmarshal(stdout.Bytes(), &output); err != nil {
|
||||
t.Fatalf("failed to unmarshal output JSON: %v", err)
|
||||
}
|
||||
|
||||
// Should be an array at the top level (canonical object form would be {"segments": [...]})
|
||||
outputArray, ok := output.([]any)
|
||||
if !ok {
|
||||
t.Fatalf("expected output to be JSON array, got %T", output)
|
||||
}
|
||||
if len(outputArray) != 2 {
|
||||
t.Fatalf("expected 2 segments in output, got %d", len(outputArray))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessInvalidGlossarySchema(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
exitCode := Run([]string{"process", fixturePath("tiny_transcript.json"), "--glossary", "../core/schema/testdata/glossary_missing_fields.yaml"}, &stdout, &stderr)
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("expected nonzero exit code for invalid glossary schema")
|
||||
}
|
||||
if stdout.Len() != 0 {
|
||||
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "invalid glossary:") {
|
||||
t.Fatalf("expected invalid glossary error, got %q", stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,5 +460,82 @@ func readProcessReport(t *testing.T, path string) minimalProcessReport {
|
||||
if err := json.Unmarshal(raw, &report); err != nil {
|
||||
t.Fatalf("failed to unmarshal process report JSON: %v (raw: %q)", err, string(raw))
|
||||
}
|
||||
return report
|
||||
func TestRunProcessStdoutBehaviorWithoutOutput(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
exitCode := Run([]string{"process", "../core/schema/testdata/transcript_bare_array.json", "--glossary", fixturePath("tiny_glossary.yaml")}, &stdout, &stderr)
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
|
||||
}
|
||||
if stderr.Len() != 0 {
|
||||
t.Fatalf("expected empty stderr on success, got %q", stderr.String())
|
||||
}
|
||||
|
||||
// Should have transcript JSON in stdout
|
||||
if stdout.Len() == 0 {
|
||||
t.Fatalf("expected transcript JSON in stdout, got empty stdout")
|
||||
}
|
||||
if !json.Valid(stdout.Bytes()) {
|
||||
t.Fatalf("expected valid JSON in stdout, got %q", stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessEmptyStdoutWithOutput(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
transcriptPath := "../core/schema/testdata/transcript_bare_array.json"
|
||||
glossaryPath := fixturePath("tiny_glossary.yaml")
|
||||
outputPath := filepath.Join(t.TempDir(), "corrected.json")
|
||||
|
||||
exitCode := Run([]string{"process", transcriptPath, "--glossary", glossaryPath, "--output", outputPath}, &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 used, got %q", stdout.String())
|
||||
}
|
||||
if stderr.Len() != 0 {
|
||||
t.Fatalf("expected empty stderr on success, got %q", stderr.String())
|
||||
}
|
||||
|
||||
// Verify output file was created and contains valid JSON
|
||||
outputBytes := readFile(t, outputPath)
|
||||
if !json.Valid(outputBytes) {
|
||||
t.Fatalf("expected valid JSON in output file, got %q", string(outputBytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunProcessReportJSONOnParseFailure(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 parse failure")
|
||||
}
|
||||
if stdout.Len() != 0 {
|
||||
t.Fatalf("expected empty stdout on failure, got %q", stdout.String())
|
||||
}
|
||||
|
||||
// Report should still be written on parse failure
|
||||
report := readProcessReport(t, reportPath)
|
||||
if report.Status != "failed" {
|
||||
t.Fatalf("expected failed report status, got %q", report.Status)
|
||||
}
|
||||
if report.ErrorMessage == "" {
|
||||
t.Fatalf("expected error message in failed report")
|
||||
}
|
||||
if !strings.Contains(report.ErrorMessage, "invalid transcript:") {
|
||||
t.Fatalf("expected invalid transcript error in report, got %q", report.ErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user