From 6dfc1ea5274c8027c9438ce7cb7fa0cc4eb26fa7 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sun, 24 May 2026 23:00:10 +0000 Subject: [PATCH] Harden render validation tests for input paths and empty transcript output --- internal/cli/render_test.go | 34 +++++++++++++++++++++++++++++ internal/config/config_test.go | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/internal/cli/render_test.go b/internal/cli/render_test.go index d265548..a26967e 100644 --- a/internal/cli/render_test.go +++ b/internal/cli/render_test.go @@ -224,6 +224,40 @@ func TestRenderSupportsMinimalIntermediateAndFullInputs(t *testing.T) { } } +func TestRenderEmptyTranscriptIsDeterministic(t *testing.T) { + dir := t.TempDir() + input := writeJSONFile(t, dir, "input.json", `{ + "metadata": { + "application": "seriatim", + "version": "v-test", + "output_schema": "seriatim-minimal" + }, + "segments": [] + }`) + output := writeJSONFile(t, dir, "output.md", "") + + run := func() string { + err := executeRender( + "--input-file", input, + "--output-file", output, + "--format", config.RenderFormatMarkdown, + ) + if err != nil { + t.Fatalf("render failed: %v", err) + } + return readFile(t, output) + } + + first := run() + second := run() + if first != second { + t.Fatalf("empty transcript render is not deterministic:\nfirst:\n%s\nsecond:\n%s", first, second) + } + if first != "# Transcript\n" { + t.Fatalf("unexpected empty transcript output:\n%s", first) + } +} + func executeRender(args ...string) error { cmd := NewRootCommand() cmd.SetArgs(append([]string{"render"}, args...)) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index d570d89..ea882b3 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -895,6 +895,46 @@ func TestNewRenderConfigAppliesDefaultsAndFlags(t *testing.T) { } } +func TestNewRenderConfigRejectsMissingAndDirectoryInputFile(t *testing.T) { + dir := t.TempDir() + output := filepath.Join(dir, "rendered.md") + + missingInput := filepath.Join(dir, "missing.json") + _, err := NewRenderConfig(validRenderOptions(missingInput, output)) + if err == nil { + t.Fatal("expected missing input-file error") + } + if !strings.Contains(err.Error(), "--input-file") { + t.Fatalf("unexpected error: %v", err) + } + + inputDir := filepath.Join(dir, "input-dir") + if err := os.MkdirAll(inputDir, 0o700); err != nil { + t.Fatalf("mkdir input dir: %v", err) + } + _, err = NewRenderConfig(validRenderOptions(inputDir, output)) + if err == nil { + t.Fatal("expected directory input-file error") + } + if !strings.Contains(err.Error(), "is a directory, not a file") { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestNewRenderConfigRejectsMissingOutputParent(t *testing.T) { + dir := t.TempDir() + input := writeTempFile(t, dir, "input.json") + output := filepath.Join(dir, "missing-parent", "rendered.md") + + _, err := NewRenderConfig(validRenderOptions(input, output)) + if err == nil { + t.Fatal("expected output parent directory error") + } + if !strings.Contains(err.Error(), "--output-file parent directory") { + t.Fatalf("unexpected error: %v", err) + } +} + func assertPositiveFloatEnvValidation(t *testing.T, envName string) { t.Helper()