Add render artifact model, config, and Seriatim adapter contracts
This commit is contained in:
@@ -569,6 +569,156 @@ func TestSubprocessRunnerNormalizeInvalidReportJSONFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerRenderSuccessInvocationAndProvenance(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
}
|
||||
|
||||
t.Setenv("GO_WANT_SERIATIM_HELPER", "1")
|
||||
t.Setenv("SERIATIM_HELPER_MODE", "render_success")
|
||||
recordPath := filepath.Join(t.TempDir(), "record.json")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", recordPath)
|
||||
|
||||
wrapper := writeHelperWrapper(t)
|
||||
runner := mustRunner(t, wrapper, false)
|
||||
req := renderReqForTest(t)
|
||||
|
||||
res, err := runner.Render(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("Render() error = %v", err)
|
||||
}
|
||||
if res.OutputRenderedPath != req.OutputRenderedPath {
|
||||
t.Fatalf("OutputRenderedPath = %q, want %q", res.OutputRenderedPath, req.OutputRenderedPath)
|
||||
}
|
||||
if res.Format != req.Format {
|
||||
t.Fatalf("Format = %q, want %q", res.Format, req.Format)
|
||||
}
|
||||
if res.Title != req.Title {
|
||||
t.Fatalf("Title = %q, want %q", res.Title, req.Title)
|
||||
}
|
||||
if res.InvokedBinary != wrapper {
|
||||
t.Fatalf("InvokedBinary = %q, want %q", res.InvokedBinary, wrapper)
|
||||
}
|
||||
if res.ExitCode != 0 {
|
||||
t.Fatalf("ExitCode = %d, want 0", res.ExitCode)
|
||||
}
|
||||
if res.Duration <= 0 {
|
||||
t.Fatalf("Duration = %s, want >0", res.Duration)
|
||||
}
|
||||
if res.Metadata == nil || res.Metadata["adapter"] != "seriatim_subprocess" {
|
||||
t.Fatalf("Metadata = %#v, want adapter marker", res.Metadata)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(req.OutputRenderedPath); err != nil {
|
||||
t.Fatalf("rendered output missing: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(req.StdoutLogPath); err != nil {
|
||||
t.Fatalf("stdout log missing: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(req.StderrLogPath); err != nil {
|
||||
t.Fatalf("stderr log missing: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(req.GeneratedConfigPath); err != nil {
|
||||
t.Fatalf("generated config missing: %v", err)
|
||||
}
|
||||
|
||||
rec := readHelperRecord(t, recordPath)
|
||||
wantArgs := []string{
|
||||
"render",
|
||||
"--input-file", req.InputTranscriptPath,
|
||||
"--output-file", req.OutputRenderedPath,
|
||||
"--format", req.Format,
|
||||
"--include-timestamps", "true",
|
||||
"--include-segment-ids", "false",
|
||||
"--include-metadata", "true",
|
||||
"--title", req.Title,
|
||||
}
|
||||
if strings.Join(rec.Args, "\n") != strings.Join(wantArgs, "\n") {
|
||||
t.Fatalf("args = %#v, want %#v", rec.Args, wantArgs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerRenderWithoutTitleOmitsTitleArg(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
}
|
||||
t.Setenv("GO_WANT_SERIATIM_HELPER", "1")
|
||||
t.Setenv("SERIATIM_HELPER_MODE", "render_success")
|
||||
recordPath := filepath.Join(t.TempDir(), "record.json")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", recordPath)
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := renderReqForTest(t)
|
||||
req.Title = ""
|
||||
if _, err := runner.Render(context.Background(), req); err != nil {
|
||||
t.Fatalf("Render() error = %v", err)
|
||||
}
|
||||
|
||||
rec := readHelperRecord(t, recordPath)
|
||||
for i := 0; i < len(rec.Args); i++ {
|
||||
if rec.Args[i] == "--title" {
|
||||
t.Fatalf("args = %#v, did not expect --title", rec.Args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerRenderSubprocessFailure(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
}
|
||||
t.Setenv("GO_WANT_SERIATIM_HELPER", "1")
|
||||
t.Setenv("SERIATIM_HELPER_MODE", "fail")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := renderReqForTest(t)
|
||||
_, err := runner.Render(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Render() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "run seriatim render") {
|
||||
t.Fatalf("error = %q, want subprocess context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerRenderMissingOutputFails(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
}
|
||||
t.Setenv("GO_WANT_SERIATIM_HELPER", "1")
|
||||
t.Setenv("SERIATIM_HELPER_MODE", "missing_output")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := renderReqForTest(t)
|
||||
_, err := runner.Render(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Render() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "validate seriatim rendered output") {
|
||||
t.Fatalf("error = %q, want output validation context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerRenderEmptyOutputFails(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("helper wrapper script uses /bin/sh")
|
||||
}
|
||||
t.Setenv("GO_WANT_SERIATIM_HELPER", "1")
|
||||
t.Setenv("SERIATIM_HELPER_MODE", "render_empty_output")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := renderReqForTest(t)
|
||||
_, err := runner.Render(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Render() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "file is empty") {
|
||||
t.Fatalf("error = %q, want empty-file validation", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerConstructorValidation(t *testing.T) {
|
||||
_, err := NewSubprocessRunnerFromConfigValues("", "10m", "seriatim-intermediate", nil, true, EnvConfig{})
|
||||
if err == nil {
|
||||
@@ -702,6 +852,14 @@ func TestSeriatimSubprocessHelper(t *testing.T) {
|
||||
case "normalize_report_missing":
|
||||
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
|
||||
os.Exit(0)
|
||||
case "render_success":
|
||||
writeSeriatimHelperFile(outputPath, "# Rendered transcript\n\nHello.\n")
|
||||
_, _ = os.Stdout.WriteString("seriatim helper render stdout\n")
|
||||
_, _ = os.Stderr.WriteString("seriatim helper render stderr\n")
|
||||
os.Exit(0)
|
||||
case "render_empty_output":
|
||||
writeSeriatimHelperFile(outputPath, "")
|
||||
os.Exit(0)
|
||||
default:
|
||||
_, _ = os.Stderr.WriteString(fmt.Sprintf("unknown helper mode %q\n", mode))
|
||||
os.Exit(2)
|
||||
@@ -777,6 +935,25 @@ func normalizeReqForTest(t *testing.T, withReport bool) NormalizeRequest {
|
||||
return req
|
||||
}
|
||||
|
||||
func renderReqForTest(t *testing.T) RenderRequest {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
input := filepath.Join(dir, "final.trimmed.json")
|
||||
writeSeriatimFile(t, input, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
|
||||
return RenderRequest{
|
||||
InputTranscriptPath: input,
|
||||
OutputRenderedPath: filepath.Join(dir, "final.trimmed.md"),
|
||||
Format: "markdown",
|
||||
Title: "Session 42",
|
||||
IncludeTimestamps: true,
|
||||
IncludeSegmentIDs: false,
|
||||
IncludeMetadata: true,
|
||||
GeneratedConfigPath: filepath.Join(dir, "seriatim.render.generated.yml"),
|
||||
StdoutLogPath: filepath.Join(dir, "seriatim.render.stdout.log"),
|
||||
StderrLogPath: filepath.Join(dir, "seriatim.render.stderr.log"),
|
||||
}
|
||||
}
|
||||
|
||||
func mustRunner(t *testing.T, binary string, report bool) *SubprocessRunner {
|
||||
t.Helper()
|
||||
coalesce := 3.0
|
||||
|
||||
Reference in New Issue
Block a user