package config import ( "os" "path/filepath" "strings" "testing" ) func TestOmittingNormalizeSpeakersDoesNotRequireSpeakers(t *testing.T) { dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") _, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: "validate-raw", PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("expected speakers file to be optional when normalize-speakers is omitted, got %v", err) } } func TestDuplicateInputFilesFailValidation(t *testing.T) { dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") _, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input, input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err == nil { t.Fatal("expected duplicate input error") } if !strings.Contains(err.Error(), "duplicate --input-file") { t.Fatalf("unexpected error: %v", err) } } func TestOverlapWordRunGapDefaultsTo075(t *testing.T) { t.Setenv(OverlapWordRunGapEnv, "") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.OverlapWordRunGap != DefaultOverlapWordRunGap { t.Fatalf("gap = %f, want %f", cfg.OverlapWordRunGap, DefaultOverlapWordRunGap) } } func TestOverlapWordRunGapUsesValidEnvOverride(t *testing.T) { t.Setenv(OverlapWordRunGapEnv, "1.25") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.OverlapWordRunGap != 1.25 { t.Fatalf("gap = %f, want 1.25", cfg.OverlapWordRunGap) } } func TestOverlapWordRunGapRejectsInvalidEnvOverride(t *testing.T) { tests := []struct { name string value string want string }{ {name: "non-numeric", value: "fast", want: "must be a positive number"}, {name: "zero", value: "0", want: "must be positive"}, {name: "negative", value: "-0.1", want: "must be positive"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Setenv(OverlapWordRunGapEnv, test.value) dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") _, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err == nil { t.Fatal("expected error") } if !strings.Contains(err.Error(), test.want) { t.Fatalf("expected error to contain %q, got %v", test.want, err) } }) } } func TestWordRunReorderWindowDefaultsTo04(t *testing.T) { t.Setenv(WordRunReorderWindowEnv, "") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.WordRunReorderWindow != DefaultWordRunReorderWindow { t.Fatalf("window = %f, want %f", cfg.WordRunReorderWindow, DefaultWordRunReorderWindow) } } func TestWordRunReorderWindowUsesValidEnvOverride(t *testing.T) { t.Setenv(WordRunReorderWindowEnv, "0.2") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.WordRunReorderWindow != 0.2 { t.Fatalf("window = %f, want 0.2", cfg.WordRunReorderWindow) } } func TestWordRunReorderWindowRejectsInvalidEnvOverride(t *testing.T) { tests := []struct { name string value string want string }{ {name: "non-numeric", value: "fast", want: "must be a positive number"}, {name: "zero", value: "0", want: "must be positive"}, {name: "negative", value: "-0.1", want: "must be positive"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Setenv(WordRunReorderWindowEnv, test.value) dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") _, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err == nil { t.Fatal("expected error") } if !strings.Contains(err.Error(), test.want) { t.Fatalf("expected error to contain %q, got %v", test.want, err) } }) } } func TestBackchannelMaxDurationDefaultsTo2(t *testing.T) { t.Setenv(BackchannelMaxDurationEnv, "") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.BackchannelMaxDuration != DefaultBackchannelMaxDuration { t.Fatalf("backchannel max duration = %f, want %f", cfg.BackchannelMaxDuration, DefaultBackchannelMaxDuration) } } func TestBackchannelMaxDurationUsesValidEnvOverride(t *testing.T) { t.Setenv(BackchannelMaxDurationEnv, "1.5") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.BackchannelMaxDuration != 1.5 { t.Fatalf("backchannel max duration = %f, want 1.5", cfg.BackchannelMaxDuration) } } func TestBackchannelMaxDurationRejectsInvalidEnvOverride(t *testing.T) { assertPositiveFloatEnvValidation(t, BackchannelMaxDurationEnv) } func TestFillerMaxDurationDefaultsTo125(t *testing.T) { t.Setenv(FillerMaxDurationEnv, "") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.FillerMaxDuration != DefaultFillerMaxDuration { t.Fatalf("filler max duration = %f, want %f", cfg.FillerMaxDuration, DefaultFillerMaxDuration) } } func TestFillerMaxDurationUsesValidEnvOverride(t *testing.T) { t.Setenv(FillerMaxDurationEnv, "1.75") dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.FillerMaxDuration != 1.75 { t.Fatalf("filler max duration = %f, want 1.75", cfg.FillerMaxDuration) } } func TestFillerMaxDurationRejectsInvalidEnvOverride(t *testing.T) { assertPositiveFloatEnvValidation(t, FillerMaxDurationEnv) } func TestCoalesceGapDefaultsTo3(t *testing.T) { dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.CoalesceGap != DefaultCoalesceGap { t.Fatalf("coalesce gap = %f, want %f", cfg.CoalesceGap, DefaultCoalesceGap) } } func TestCoalesceGapUsesValidOverride(t *testing.T) { dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, CoalesceGap: "1.5", }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.CoalesceGap != 1.5 { t.Fatalf("coalesce gap = %f, want 1.5", cfg.CoalesceGap) } } func TestCoalesceGapAllowsZero(t *testing.T) { dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") cfg, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, CoalesceGap: "0", }) if err != nil { t.Fatalf("config failed: %v", err) } if cfg.CoalesceGap != 0 { t.Fatalf("coalesce gap = %f, want 0", cfg.CoalesceGap) } } func TestCoalesceGapRejectsInvalidOverride(t *testing.T) { tests := []struct { name string value string want string }{ {name: "non-numeric", value: "fast", want: "must be a non-negative number"}, {name: "negative", value: "-0.1", want: "must be non-negative"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") _, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, CoalesceGap: test.value, }) if err == nil { t.Fatal("expected error") } if !strings.Contains(err.Error(), test.want) { t.Fatalf("expected error to contain %q, got %v", test.want, err) } }) } } func assertPositiveFloatEnvValidation(t *testing.T, envName string) { t.Helper() tests := []struct { name string value string want string }{ {name: "non-numeric", value: "fast", want: "must be a positive number"}, {name: "zero", value: "0", want: "must be positive"}, {name: "negative", value: "-0.1", want: "must be positive"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Setenv(envName, test.value) dir := t.TempDir() input := writeTempFile(t, dir, "input.json") output := filepath.Join(dir, "merged.json") _, err := NewMergeConfig(MergeOptions{ InputFiles: []string{input}, OutputFile: output, InputReader: DefaultInputReader, OutputModules: DefaultOutputModules, PreprocessingModules: DefaultPreprocessingModules, PostprocessingModules: DefaultPostprocessingModules, }) if err == nil { t.Fatal("expected error") } if !strings.Contains(err.Error(), test.want) { t.Fatalf("expected error to contain %q, got %v", test.want, err) } }) } } func writeTempFile(t *testing.T, dir string, name string) string { t.Helper() path := filepath.Join(dir, name) if err := os.WriteFile(path, []byte("{}\n"), 0o600); err != nil { t.Fatalf("write temp file: %v", err) } return path }