Add Seriatim trim adapter support

This commit is contained in:
2026-05-08 16:58:26 +00:00
parent ea39594b33
commit 79a8bcf37b
5 changed files with 495 additions and 13 deletions

View File

@@ -209,6 +209,172 @@ func TestSubprocessRunnerInvalidReportJSONFails(t *testing.T) {
}
}
func TestSubprocessRunnerTrimSuccessInvocationAndProvenance(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", "trim_success")
recordPath := filepath.Join(t.TempDir(), "record.json")
t.Setenv("SERIATIM_HELPER_RECORD_PATH", recordPath)
wrapper := writeHelperWrapper(t)
runner := mustRunner(t, wrapper, false)
req := trimReqForTest(t)
res, err := runner.Trim(context.Background(), req)
if err != nil {
t.Fatalf("Trim() error = %v", err)
}
if res.OutputTrimmedPath != req.OutputTrimmedPath {
t.Fatalf("OutputTrimmedPath = %q, want %q", res.OutputTrimmedPath, req.OutputTrimmedPath)
}
if res.KeepSelector != req.KeepSelector {
t.Fatalf("KeepSelector = %q, want %q", res.KeepSelector, req.KeepSelector)
}
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)
}
assertJSONFile(t, req.OutputTrimmedPath)
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)
}
stdoutData, err := os.ReadFile(req.StdoutLogPath)
if err != nil {
t.Fatalf("read stdout log: %v", err)
}
if !strings.Contains(string(stdoutData), "seriatim helper trim stdout") {
t.Fatalf("stdout log = %q, want trim helper stdout marker", string(stdoutData))
}
stderrData, err := os.ReadFile(req.StderrLogPath)
if err != nil {
t.Fatalf("read stderr log: %v", err)
}
if !strings.Contains(string(stderrData), "seriatim helper trim stderr") {
t.Fatalf("stderr log = %q, want trim helper stderr marker", string(stderrData))
}
cfgData, err := os.ReadFile(req.GeneratedConfigPath)
if err != nil {
t.Fatalf("read generated config: %v", err)
}
cfgText := string(cfgData)
if !strings.Contains(cfgText, "command: trim") {
t.Fatalf("generated config = %q, want command: trim", cfgText)
}
if !strings.Contains(cfgText, "keep_selector: 5-12") {
t.Fatalf("generated config = %q, want keep selector", cfgText)
}
rec := readHelperRecord(t, recordPath)
wantArgs := []string{
"trim",
"--input-file", req.InputTranscriptPath,
"--output-file", req.OutputTrimmedPath,
"--keep", req.KeepSelector,
}
if strings.Join(rec.Args, "\n") != strings.Join(wantArgs, "\n") {
t.Fatalf("args = %#v, want %#v", rec.Args, wantArgs)
}
}
func TestSubprocessRunnerTrimSubprocessFailure(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 := trimReqForTest(t)
_, err := runner.Trim(context.Background(), req)
if err == nil {
t.Fatal("Trim() error = nil, want non-nil")
}
if !strings.Contains(err.Error(), "run seriatim trim") {
t.Fatalf("error = %q, want subprocess context", err.Error())
}
if !strings.Contains(err.Error(), "exit code") {
t.Fatalf("error = %q, want exit code context", err.Error())
}
}
func TestSubprocessRunnerTrimMissingOutputFails(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 := trimReqForTest(t)
_, err := runner.Trim(context.Background(), req)
if err == nil {
t.Fatal("Trim() error = nil, want non-nil")
}
if !strings.Contains(err.Error(), "validate seriatim trimmed output") {
t.Fatalf("error = %q, want output validation context", err.Error())
}
}
func TestSubprocessRunnerTrimInvalidOutputJSONFails(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", "invalid_output")
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
runner := mustRunner(t, writeHelperWrapper(t), false)
req := trimReqForTest(t)
_, err := runner.Trim(context.Background(), req)
if err == nil {
t.Fatal("Trim() error = nil, want non-nil")
}
if !strings.Contains(err.Error(), "parse json") {
t.Fatalf("error = %q, want parse json context", err.Error())
}
}
func TestSubprocessRunnerTrimOutputMissingSegmentsFails(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", "trim_missing_segments")
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
runner := mustRunner(t, writeHelperWrapper(t), false)
req := trimReqForTest(t)
_, err := runner.Trim(context.Background(), req)
if err == nil {
t.Fatal("Trim() error = nil, want non-nil")
}
if !strings.Contains(err.Error(), "top-level segments is required") {
t.Fatalf("error = %q, want missing segments context", err.Error())
}
}
func TestSubprocessRunnerConstructorValidation(t *testing.T) {
_, err := NewSubprocessRunnerFromConfigValues("", "10m", "seriatim-intermediate", nil, true, EnvConfig{})
if err == nil {
@@ -250,14 +416,14 @@ func TestSeriatimSubprocessHelper(t *testing.T) {
_, _ = os.Stderr.WriteString("missing -- args separator\n")
os.Exit(2)
}
mergeArgs := args[start:]
cmdArgs := args[start:]
outputPath := flagValue(mergeArgs, "--output-file")
reportPath := flagValue(mergeArgs, "--report-file")
outputPath := flagValue(cmdArgs, "--output-file")
reportPath := flagValue(cmdArgs, "--report-file")
recordPath := os.Getenv("SERIATIM_HELPER_RECORD_PATH")
if strings.TrimSpace(recordPath) != "" {
rec := helperRecord{
Args: mergeArgs,
Args: cmdArgs,
Env: map[string]string{
"SERIATIM_OVERLAP_WORD_RUN_GAP": os.Getenv("SERIATIM_OVERLAP_WORD_RUN_GAP"),
"SERIATIM_OVERLAP_WORD_RUN_REORDER_WINDOW": os.Getenv("SERIATIM_OVERLAP_WORD_RUN_REORDER_WINDOW"),
@@ -280,6 +446,11 @@ func TestSeriatimSubprocessHelper(t *testing.T) {
_, _ = os.Stdout.WriteString("seriatim helper success stdout\n")
_, _ = os.Stderr.WriteString("seriatim helper success stderr\n")
os.Exit(0)
case "trim_success":
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
_, _ = os.Stdout.WriteString("seriatim helper trim stdout\n")
_, _ = os.Stderr.WriteString("seriatim helper trim stderr\n")
os.Exit(0)
case "fail":
_, _ = os.Stderr.WriteString("seriatim helper failure\n")
os.Exit(9)
@@ -300,6 +471,9 @@ func TestSeriatimSubprocessHelper(t *testing.T) {
writeSeriatimHelperFile(reportPath, `not-json`)
}
os.Exit(0)
case "trim_missing_segments":
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1"}`)
os.Exit(0)
default:
_, _ = os.Stderr.WriteString(fmt.Sprintf("unknown helper mode %q\n", mode))
os.Exit(2)
@@ -340,6 +514,21 @@ func mergeReqForTest(t *testing.T, withReport bool) MergeRequest {
return req
}
func trimReqForTest(t *testing.T) TrimRequest {
t.Helper()
dir := t.TempDir()
input := filepath.Join(dir, "processed.json")
writeSeriatimFile(t, input, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
return TrimRequest{
InputTranscriptPath: input,
OutputTrimmedPath: filepath.Join(dir, "trimmed.json"),
KeepSelector: "5-12",
GeneratedConfigPath: filepath.Join(dir, "seriatim.trim.generated.yml"),
StdoutLogPath: filepath.Join(dir, "seriatim.trim.stdout.log"),
StderrLogPath: filepath.Join(dir, "seriatim.trim.stderr.log"),
}
}
func mustRunner(t *testing.T, binary string, report bool) *SubprocessRunner {
t.Helper()
coalesce := 3.0