package cli import ( "bytes" "errors" "strings" "testing" ) func TestRunRootHelp(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"--help"}, &stdout, &stderr) if exitCode != 0 { t.Fatalf("expected exit code 0, got %d", exitCode) } if !strings.Contains(stdout.String(), "audita [options]") { t.Fatalf("expected root usage in stdout, got %q", stdout.String()) } if !strings.Contains(stdout.String(), "process") { t.Fatalf("expected process command in root help, got %q", stdout.String()) } if stderr.Len() != 0 { t.Fatalf("expected empty stderr, got %q", stderr.String()) } } func TestRunProcessHelpListsExpectedFlags(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"process", "--help"}, &stdout, &stderr) if exitCode != 0 { t.Fatalf("expected exit code 0, got %d", exitCode) } for _, expectedFlag := range []string{ "--glossary", "--output", "--report-json", "--modules", "--llm-api-key", "--validation-llm-api-key", "--model", "--validation-model", "--base-url", "--validation-base-url", "--llm-timeout-seconds", "--validation-llm-timeout-seconds", "--validation-max-prompt-tokens", "--target-sections", "--max-retries", "--validation-max-retries", "--validation-llm-concurrency", "--max-section-tokens", "--min-section-tokens", "--glossary-confidence-threshold", "--grammar-confidence-threshold", "--homophones-confidence-threshold", "--spoken-word-confidence-threshold", "--normalize-max-segment-gap", "--normalize-ellipsis-gap", "--normalize-max-segment-duration", "--normalize-max-segment-tokens", "--work-dir", "--work-dir-retention", } { if !strings.Contains(stdout.String(), expectedFlag) { t.Fatalf("expected process help to include %q, got %q", expectedFlag, stdout.String()) } } if stderr.Len() != 0 { t.Fatalf("expected empty stderr, got %q", stderr.String()) } } func TestRunUnknownCommand(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"unknown"}, &stdout, &stderr) if exitCode != 2 { t.Fatalf("expected exit code 2, got %d", exitCode) } if stdout.Len() != 0 { t.Fatalf("expected empty stdout, got %q", stdout.String()) } if !strings.Contains(stderr.String(), "unknown command") { t.Fatalf("expected unknown command error in stderr, got %q", stderr.String()) } } func TestRunProcessMissingTranscriptPath(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"process", "--glossary", "glossary.yaml"}, &stdout, &stderr) if exitCode == 0 { t.Fatalf("expected nonzero exit code") } if stdout.Len() != 0 { t.Fatalf("expected empty stdout, got %q", stdout.String()) } if !strings.Contains(stderr.String(), "expected exactly 1 transcript JSON path argument") { t.Fatalf("expected missing transcript error, got %q", stderr.String()) } } func TestRunProcessMissingGlossary(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"process", "transcript.json"}, &stdout, &stderr) if exitCode == 0 { t.Fatalf("expected nonzero exit code") } if stdout.Len() != 0 { t.Fatalf("expected empty stdout, got %q", stdout.String()) } if !strings.Contains(stderr.String(), "--glossary is required") { t.Fatalf("expected missing glossary error, got %q", stderr.String()) } } func TestRunProcessInvalidCLIConfig(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"process", "transcript.json", "--glossary", "glossary.yaml", "--work-dir-retention", "invalid"}, &stdout, &stderr) if exitCode != 2 { t.Fatalf("expected exit code 2, got %d", exitCode) } if stdout.Len() != 0 { t.Fatalf("expected empty stdout, got %q", stdout.String()) } if !strings.Contains(stderr.String(), "invalid CLI configuration") { t.Fatalf("expected invalid config error, got %q", stderr.String()) } } func TestRunProcessNotImplemented(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer exitCode := Run([]string{"process", "transcript.json", "--glossary", "glossary.yaml"}, &stdout, &stderr) if exitCode != 1 { t.Fatalf("expected exit code 1 for not-implemented process, got %d", exitCode) } if stdout.Len() != 0 { t.Fatalf("expected empty stdout, got %q", stdout.String()) } if !strings.Contains(stderr.String(), processNotImplementedMessage) { t.Fatalf("expected not-implemented message in stderr, got %q", stderr.String()) } } func TestRunProcessCLIOverridesEnvironment(t *testing.T) { var stdout bytes.Buffer var stderr bytes.Buffer t.Setenv("AUDITA_MODEL", "env-model") t.Setenv("AUDITA_VALIDATION_LLM_CONCURRENCY", "2") var captured processInvocation originalRunner := processRunner processRunner = func(inv processInvocation) error { captured = inv return errors.New(processNotImplementedMessage) } t.Cleanup(func() { processRunner = originalRunner }) exitCode := Run([]string{ "process", "transcript.json", "--glossary", "glossary.yaml", "--model", "cli-model", "--validation-llm-concurrency", "5", }, &stdout, &stderr) if exitCode != 1 { t.Fatalf("expected exit code 1 for not implemented, got %d", exitCode) } if captured.Config.PrimaryLLM.Model != "cli-model" { t.Fatalf("expected CLI model override, got %q", captured.Config.PrimaryLLM.Model) } if captured.Config.ValidationLLM.Concurrency == nil || *captured.Config.ValidationLLM.Concurrency != 5 { t.Fatalf("expected CLI validation concurrency override, got %#v", captured.Config.ValidationLLM.Concurrency) } if captured.GlossaryPath != "glossary.yaml" { t.Fatalf("unexpected glossary path: %q", captured.GlossaryPath) } }