diff --git a/internal/config/uploads_test.go b/internal/config/uploads_test.go index 3d120bf..858cdf1 100644 --- a/internal/config/uploads_test.go +++ b/internal/config/uploads_test.go @@ -15,6 +15,7 @@ func clearUploadEnv(t *testing.T) { "TAPIR_TRANSCRIBE_MODEL", "TAPIR_TRANSCRIBE_FALLBACK_MODEL", "TAPIR_TRANSCRIBE_TIMEOUT", "TAPIR_UPLOAD_TMP_DIR", "TAPIR_FFMPEG_BIN", "TAPIR_FFPROBE_BIN", "TAPIR_MAX_UPLOADED_TRANSCRIPT_CHARS", "TAPIR_UPLOAD_CHAT_MODEL", + "TAPIR_RECONCILE_BASE_MODEL", "TAPIR_RECONCILE_MODEL", "TAPIR_RECONCILE_CHECK_ORDER", } { unset(t, k) } @@ -43,8 +44,11 @@ func TestLoad_UploadDefaults(t *testing.T) { if c.TranscribeFallbackModel != "berget/kb-whisper" { t.Errorf("TranscribeFallbackModel = %q", c.TranscribeFallbackModel) } - if c.TranscribeTimeout != 30*time.Minute { - t.Errorf("TranscribeTimeout = %v, want 30m (a 90 min recording took ~7.5 min)", c.TranscribeTimeout) + if c.TranscribeTimeout != 60*time.Minute { + t.Errorf("TranscribeTimeout = %v, want 60m (ADR-034: two engines plus ~300 adjudications)", c.TranscribeTimeout) + } + if c.ReconcileBaseModel != "" || c.ReconcileModel != "" || c.ReconcileCheckOrder { + t.Errorf("reconciliation must be OFF by default: %q %q %v", c.ReconcileBaseModel, c.ReconcileModel, c.ReconcileCheckOrder) } if c.UploadTmpDir != os.TempDir() { t.Errorf("UploadTmpDir = %q, want os.TempDir()", c.UploadTmpDir) @@ -139,3 +143,72 @@ func TestLoad_UploadRejectsInvalidValues(t *testing.T) { }) } } + +func TestLoad_ReconcileSettingsAreParsed(t *testing.T) { + clearUploadEnv(t) + setEnv(t, map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "iguana/pianissimo", + "TAPIR_RECONCILE_MODEL": "iguana/gemma4-31b", + "TAPIR_RECONCILE_CHECK_ORDER": "true", + }) + + c, err := Load() + if err != nil { + t.Fatalf("Load: %v", err) + } + + if c.ReconcileBaseModel != "iguana/pianissimo" || c.ReconcileModel != "iguana/gemma4-31b" || !c.ReconcileCheckOrder { + t.Errorf("not applied: %+v", c.UploadConfig) + } +} + +func TestLoad_ReconcileRefusesAnUnsafeOrIncompleteSetup(t *testing.T) { + tests := []struct { + name string + env map[string]string + }{ + {"a base engine with no adjudicator", map[string]string{"TAPIR_RECONCILE_BASE_MODEL": "iguana/pianissimo"}}, + {"a cloud adjudicator: private meeting content must not reach it", map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "iguana/pianissimo", "TAPIR_RECONCILE_MODEL": "berget/mistral-small", + }}, + {"a cloud adjudicator from another provider", map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "iguana/pianissimo", "TAPIR_RECONCILE_MODEL": "gemini/gemini-2.5-pro", + }}, + {"an adjudicator alias with no host prefix", map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "iguana/pianissimo", "TAPIR_RECONCILE_MODEL": "gemma4-26b", + }}, + {"a cloud base engine", map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "berget/kb-whisper", "TAPIR_RECONCILE_MODEL": "iguana/gemma4-26b", + }}, + {"the same engine twice", map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "iguana/kb-whisper", "TAPIR_RECONCILE_MODEL": "iguana/gemma4-26b", + }}, + {"a bad order-check flag", map[string]string{ + "TAPIR_RECONCILE_BASE_MODEL": "iguana/pianissimo", "TAPIR_RECONCILE_MODEL": "iguana/gemma4-26b", "TAPIR_RECONCILE_CHECK_ORDER": "perhaps", + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clearUploadEnv(t) + setEnv(t, tt.env) + + if _, err := Load(); err == nil { + t.Errorf("Load accepted %v", tt.env) + } + }) + } +} + +func TestLoad_AnAdjudicatorWithoutABaseEngineIsIgnoredNotAnError(t *testing.T) { + clearUploadEnv(t) + t.Setenv("TAPIR_RECONCILE_MODEL", "iguana/gemma4-26b") + + c, err := Load() + if err != nil { + t.Fatalf("Load: %v", err) + } + + if c.ReconcileBaseModel != "" { + t.Errorf("reconciliation must stay off without a base engine") + } +}