275 lines
6.9 KiB
Go
275 lines
6.9 KiB
Go
package cli
|
||
|
||
import (
|
||
"bytes"
|
||
"os"
|
||
"strings"
|
||
"testing"
|
||
|
||
"gitea.maximumdirect.net/eric/seriatim/internal/config"
|
||
)
|
||
|
||
func TestRenderCommandIsRecognized(t *testing.T) {
|
||
cmd := NewRootCommand()
|
||
cmd.SetArgs([]string{"render", "--help"})
|
||
if err := cmd.Execute(); err != nil {
|
||
t.Fatalf("render command should be recognized: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestRootHelpIncludesRender(t *testing.T) {
|
||
cmd := NewRootCommand()
|
||
var out bytes.Buffer
|
||
cmd.SetOut(&out)
|
||
cmd.SetErr(&out)
|
||
cmd.SetArgs([]string{"--help"})
|
||
if err := cmd.Execute(); err != nil {
|
||
t.Fatalf("help failed: %v", err)
|
||
}
|
||
if !strings.Contains(out.String(), "render") {
|
||
t.Fatalf("root help missing render command:\n%s", out.String())
|
||
}
|
||
}
|
||
|
||
func TestRenderEndToEndMarkdownOutput(t *testing.T) {
|
||
dir := t.TempDir()
|
||
input := writeJSONFile(t, dir, "input.json", `{
|
||
"metadata": {
|
||
"application": "seriatim",
|
||
"version": "v-test",
|
||
"output_schema": "seriatim-intermediate"
|
||
},
|
||
"segments": [
|
||
{"id": 1, "start": 1, "end": 4, "speaker": "Eric", "text": "Hello there."},
|
||
{"id": 2, "start": 5, "end": 8, "speaker": "Mike", "text": "Yeah.", "categories": ["backchannel"]}
|
||
]
|
||
}`)
|
||
output := writeJSONFile(t, dir, "output.md", "")
|
||
|
||
err := executeRender(
|
||
"--input-file", input,
|
||
"--output-file", output,
|
||
"--format", config.RenderFormatMarkdown,
|
||
"--title", "Transcript",
|
||
)
|
||
if err != nil {
|
||
t.Fatalf("render failed: %v", err)
|
||
}
|
||
|
||
data := readFile(t, output)
|
||
if !strings.Contains(data, "# Transcript") {
|
||
t.Fatalf("missing title:\n%s", data)
|
||
}
|
||
if !strings.Contains(data, "[00:00:01–00:00:04] **Eric:** Hello there.") {
|
||
t.Fatalf("missing first segment:\n%s", data)
|
||
}
|
||
if !strings.Contains(data, "[00:00:05–00:00:08] **Mike:** *Yeah.*") {
|
||
t.Fatalf("missing italicized backchannel segment:\n%s", data)
|
||
}
|
||
}
|
||
|
||
func TestRenderWorksWithRequiredFlagsOnly(t *testing.T) {
|
||
dir := t.TempDir()
|
||
input := writeJSONFile(t, dir, "input.json", `{
|
||
"metadata": {
|
||
"application": "seriatim",
|
||
"version": "v-test",
|
||
"output_schema": "seriatim-minimal"
|
||
},
|
||
"segments": [
|
||
{"id": 1, "start": 1, "end": 2, "speaker": "Eric", "text": "Hello there."}
|
||
]
|
||
}`)
|
||
output := writeJSONFile(t, dir, "output.md", "")
|
||
|
||
err := executeRender(
|
||
"--input-file", input,
|
||
"--output-file", output,
|
||
"--format", config.RenderFormatMarkdown,
|
||
)
|
||
if err != nil {
|
||
t.Fatalf("render with required flags failed: %v", err)
|
||
}
|
||
|
||
data := readFile(t, output)
|
||
if !strings.Contains(data, "# Transcript") {
|
||
t.Fatalf("missing default title:\n%s", data)
|
||
}
|
||
if !strings.Contains(data, "[00:00:01–00:00:02] **Eric:** Hello there.") {
|
||
t.Fatalf("missing rendered segment:\n%s", data)
|
||
}
|
||
}
|
||
|
||
func TestRenderRejectsUnsupportedFormat(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", "")
|
||
|
||
err := executeRender(
|
||
"--input-file", input,
|
||
"--output-file", output,
|
||
"--format", "txt",
|
||
)
|
||
if err == nil {
|
||
t.Fatal("expected format error")
|
||
}
|
||
if !strings.Contains(err.Error(), "--format must be") {
|
||
t.Fatalf("unexpected error: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestRenderRejectsMalformedAndRawInput(t *testing.T) {
|
||
dir := t.TempDir()
|
||
output := writeJSONFile(t, dir, "output.md", "")
|
||
|
||
malformed := writeJSONFile(t, dir, "malformed.json", `{"metadata":`)
|
||
err := executeRender(
|
||
"--input-file", malformed,
|
||
"--output-file", output,
|
||
"--format", config.RenderFormatMarkdown,
|
||
)
|
||
if err == nil {
|
||
t.Fatal("expected malformed input error")
|
||
}
|
||
if !strings.Contains(err.Error(), "input JSON is malformed") {
|
||
t.Fatalf("unexpected malformed input error: %v", err)
|
||
}
|
||
|
||
raw := writeJSONFile(t, dir, "raw.json", `{"segments":[{"id":0,"start":0.1,"end":1.1,"text":"hello","words":[{"word":"hello"}]}]}`)
|
||
err = executeRender(
|
||
"--input-file", raw,
|
||
"--output-file", output,
|
||
"--format", config.RenderFormatMarkdown,
|
||
)
|
||
if err == nil {
|
||
t.Fatal("expected artifact validation error")
|
||
}
|
||
if !strings.Contains(err.Error(), "not a valid seriatim output artifact") {
|
||
t.Fatalf("unexpected raw input error: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestRenderSupportsMinimalIntermediateAndFullInputs(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
content string
|
||
}{
|
||
{
|
||
name: "minimal",
|
||
content: `{
|
||
"metadata": {
|
||
"application": "seriatim",
|
||
"version": "v-test",
|
||
"output_schema": "seriatim-minimal"
|
||
},
|
||
"segments": [{"id":1,"start":1,"end":2,"speaker":"A","text":"one"}]
|
||
}`,
|
||
},
|
||
{
|
||
name: "intermediate",
|
||
content: `{
|
||
"metadata": {
|
||
"application": "seriatim",
|
||
"version": "v-test",
|
||
"output_schema": "seriatim-intermediate"
|
||
},
|
||
"segments": [{"id":1,"start":1,"end":2,"speaker":"A","text":"one","categories":["filler"]}]
|
||
}`,
|
||
},
|
||
{
|
||
name: "full",
|
||
content: `{
|
||
"metadata": {
|
||
"application": "seriatim",
|
||
"version": "v-test",
|
||
"input_reader": "json-files",
|
||
"input_files": ["input.json"],
|
||
"preprocessing_modules": [],
|
||
"postprocessing_modules": [],
|
||
"output_modules": ["json"]
|
||
},
|
||
"segments": [{
|
||
"id":1,
|
||
"source":"input.json",
|
||
"source_segment_index":0,
|
||
"speaker":"A",
|
||
"start":1,
|
||
"end":2,
|
||
"text":"one"
|
||
}],
|
||
"overlap_groups": []
|
||
}`,
|
||
},
|
||
}
|
||
|
||
for _, test := range tests {
|
||
t.Run(test.name, func(t *testing.T) {
|
||
dir := t.TempDir()
|
||
input := writeJSONFile(t, dir, "input.json", test.content)
|
||
output := writeJSONFile(t, dir, "output.md", "")
|
||
|
||
err := executeRender(
|
||
"--input-file", input,
|
||
"--output-file", output,
|
||
"--format", config.RenderFormatMarkdown,
|
||
)
|
||
if err != nil {
|
||
t.Fatalf("render failed: %v", err)
|
||
}
|
||
data := readFile(t, output)
|
||
if !strings.Contains(data, "**A:**") {
|
||
t.Fatalf("missing rendered segment for %s input:\n%s", test.name, data)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
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...))
|
||
return cmd.Execute()
|
||
}
|
||
|
||
func readFile(t *testing.T, path string) string {
|
||
t.Helper()
|
||
data, err := os.ReadFile(path)
|
||
if err != nil {
|
||
t.Fatalf("read %s: %v", path, err)
|
||
}
|
||
return string(data)
|
||
}
|