Add Seriatim normalize adapter support
This commit is contained in:
@@ -375,6 +375,200 @@ func TestSubprocessRunnerTrimOutputMissingSegmentsFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerNormalizeSuccessInvocationAndProvenance(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", "normalize_success")
|
||||
recordPath := filepath.Join(t.TempDir(), "record.json")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", recordPath)
|
||||
|
||||
wrapper := writeHelperWrapper(t)
|
||||
runner := mustRunner(t, wrapper, false)
|
||||
req := normalizeReqForTest(t, true)
|
||||
|
||||
res, err := runner.Normalize(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("Normalize() error = %v", err)
|
||||
}
|
||||
if res.OutputNormalizedPath != req.OutputNormalizedPath {
|
||||
t.Fatalf("OutputNormalizedPath = %q, want %q", res.OutputNormalizedPath, req.OutputNormalizedPath)
|
||||
}
|
||||
if res.OutputSchema != req.OutputSchema {
|
||||
t.Fatalf("OutputSchema = %q, want %q", res.OutputSchema, req.OutputSchema)
|
||||
}
|
||||
if res.ReportPath != req.ReportPath {
|
||||
t.Fatalf("ReportPath = %q, want %q", res.ReportPath, req.ReportPath)
|
||||
}
|
||||
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.OutputNormalizedPath)
|
||||
assertJSONFile(t, req.ReportPath)
|
||||
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)
|
||||
}
|
||||
|
||||
cfgData, err := os.ReadFile(req.GeneratedConfigPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read generated config: %v", err)
|
||||
}
|
||||
cfgText := string(cfgData)
|
||||
if !strings.Contains(cfgText, "command: normalize") {
|
||||
t.Fatalf("generated config = %q, want command: normalize", cfgText)
|
||||
}
|
||||
if !strings.Contains(cfgText, "output_schema: seriatim-intermediate") {
|
||||
t.Fatalf("generated config = %q, want output schema", cfgText)
|
||||
}
|
||||
|
||||
rec := readHelperRecord(t, recordPath)
|
||||
wantArgs := []string{
|
||||
"normalize",
|
||||
"--input-file", req.InputTranscriptPath,
|
||||
"--output-file", req.OutputNormalizedPath,
|
||||
"--output-schema", req.OutputSchema,
|
||||
"--report-file", req.ReportPath,
|
||||
}
|
||||
if strings.Join(rec.Args, "\n") != strings.Join(wantArgs, "\n") {
|
||||
t.Fatalf("args = %#v, want %#v", rec.Args, wantArgs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerNormalizeSubprocessFailure(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 := normalizeReqForTest(t, false)
|
||||
_, err := runner.Normalize(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Normalize() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "run seriatim normalize") {
|
||||
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 TestSubprocessRunnerNormalizeMissingOutputFails(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", "normalize_missing_output")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := normalizeReqForTest(t, false)
|
||||
_, err := runner.Normalize(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Normalize() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "validate seriatim normalized output") {
|
||||
t.Fatalf("error = %q, want output validation context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerNormalizeInvalidOutputJSONFails(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", "normalize_invalid_output")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := normalizeReqForTest(t, false)
|
||||
_, err := runner.Normalize(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Normalize() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "parse json") {
|
||||
t.Fatalf("error = %q, want parse json context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerNormalizeOutputMissingSegmentsFails(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", "normalize_missing_segments")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := normalizeReqForTest(t, false)
|
||||
_, err := runner.Normalize(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Normalize() 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 TestSubprocessRunnerNormalizeReportMissingFails(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", "normalize_report_missing")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := normalizeReqForTest(t, true)
|
||||
_, err := runner.Normalize(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Normalize() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "validate seriatim normalize report output") {
|
||||
t.Fatalf("error = %q, want report validation context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerNormalizeInvalidReportJSONFails(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", "normalize_invalid_report")
|
||||
t.Setenv("SERIATIM_HELPER_RECORD_PATH", filepath.Join(t.TempDir(), "record.json"))
|
||||
|
||||
runner := mustRunner(t, writeHelperWrapper(t), false)
|
||||
req := normalizeReqForTest(t, true)
|
||||
_, err := runner.Normalize(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Normalize() error = nil, want non-nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "validate seriatim normalize report output") {
|
||||
t.Fatalf("error = %q, want report validation context", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessRunnerConstructorValidation(t *testing.T) {
|
||||
_, err := NewSubprocessRunnerFromConfigValues("", "10m", "seriatim-intermediate", nil, true, EnvConfig{})
|
||||
if err == nil {
|
||||
@@ -451,6 +645,14 @@ func TestSeriatimSubprocessHelper(t *testing.T) {
|
||||
_, _ = os.Stdout.WriteString("seriatim helper trim stdout\n")
|
||||
_, _ = os.Stderr.WriteString("seriatim helper trim stderr\n")
|
||||
os.Exit(0)
|
||||
case "normalize_success":
|
||||
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `{"schema":"seriatim.normalize.report.v1","ok":true}`)
|
||||
}
|
||||
_, _ = os.Stdout.WriteString("seriatim helper normalize stdout\n")
|
||||
_, _ = os.Stderr.WriteString("seriatim helper normalize stderr\n")
|
||||
os.Exit(0)
|
||||
case "fail":
|
||||
_, _ = os.Stderr.WriteString("seriatim helper failure\n")
|
||||
os.Exit(9)
|
||||
@@ -459,21 +661,47 @@ func TestSeriatimSubprocessHelper(t *testing.T) {
|
||||
writeSeriatimHelperFile(reportPath, `{"report":true}`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "normalize_missing_output":
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `{"schema":"seriatim.normalize.report.v1","ok":true}`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "invalid_output":
|
||||
writeSeriatimHelperFile(outputPath, `not-json`)
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `{"report":true}`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "normalize_invalid_output":
|
||||
writeSeriatimHelperFile(outputPath, `not-json`)
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `{"schema":"seriatim.normalize.report.v1","ok":true}`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "invalid_report":
|
||||
writeSeriatimHelperFile(outputPath, `{"merged":true}`)
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `not-json`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "normalize_invalid_report":
|
||||
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `not-json`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "trim_missing_segments":
|
||||
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1"}`)
|
||||
os.Exit(0)
|
||||
case "normalize_missing_segments":
|
||||
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1"}`)
|
||||
if reportPath != "" {
|
||||
writeSeriatimHelperFile(reportPath, `{"schema":"seriatim.normalize.report.v1","ok":true}`)
|
||||
}
|
||||
os.Exit(0)
|
||||
case "normalize_report_missing":
|
||||
writeSeriatimHelperFile(outputPath, `{"schema":"seriatim.intermediate.v1","segments":[]}`)
|
||||
os.Exit(0)
|
||||
default:
|
||||
_, _ = os.Stderr.WriteString(fmt.Sprintf("unknown helper mode %q\n", mode))
|
||||
os.Exit(2)
|
||||
@@ -529,6 +757,26 @@ func trimReqForTest(t *testing.T) TrimRequest {
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeReqForTest(t *testing.T, withReport bool) NormalizeRequest {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
input := filepath.Join(dir, "processed.json")
|
||||
writeSeriatimFile(t, input, `{"schema":"audita.processed.v1","segments":[]}`)
|
||||
|
||||
req := NormalizeRequest{
|
||||
InputTranscriptPath: input,
|
||||
OutputNormalizedPath: filepath.Join(dir, "normalized.json"),
|
||||
OutputSchema: "seriatim-intermediate",
|
||||
GeneratedConfigPath: filepath.Join(dir, "seriatim.normalize.generated.yml"),
|
||||
StdoutLogPath: filepath.Join(dir, "seriatim.normalize.stdout.log"),
|
||||
StderrLogPath: filepath.Join(dir, "seriatim.normalize.stderr.log"),
|
||||
}
|
||||
if withReport {
|
||||
req.ReportPath = filepath.Join(dir, "seriatim.normalize.report.json")
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func mustRunner(t *testing.T, binary string, report bool) *SubprocessRunner {
|
||||
t.Helper()
|
||||
coalesce := 3.0
|
||||
|
||||
Reference in New Issue
Block a user