package config import ( "os" "path/filepath" "testing" ) func TestLoadConfigAllowsOptionalEmptyValues(t *testing.T) { path := filepath.Join(t.TempDir(), "app.env") content := `APP_ENV=dev LOG_LEVEL=debug ASTERISK_ARI_URL=http://127.0.0.1:8088/ari ASTERISK_ARI_WS_URL=ws://127.0.0.1:8088/ari/events ASTERISK_ARI_USER=ai_operator ASTERISK_ARI_PASSWORD=test-password ASTERISK_ARI_APP=ai-operator ASTERISK_MEDIA_MODE=chan_websocket ASTERISK_MEDIA_CODEC=slin16 OPENAI_API_KEY= OPENAI_REALTIME_MODEL= DATABASE_URL= ` if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatal(err) } cfg, resolved, err := Load(path) if err != nil { t.Fatalf("Load() error = %v", err) } if resolved != path { t.Fatalf("resolved path = %q, want %q", resolved, path) } if cfg.OpenAI.APIKey != "" { t.Fatal("OpenAI API key should be optional and empty") } if cfg.Database.URL != "" { t.Fatal("Database URL should be optional and empty") } } func TestLoadConfigRequiresCriticalFields(t *testing.T) { path := filepath.Join(t.TempDir(), "bad.env") if err := os.WriteFile(path, []byte("APP_ENV=dev\n"), 0600); err != nil { t.Fatal(err) } if _, _, err := Load(path); err == nil { t.Fatal("Load() error = nil, want validation error") } } func TestMasking(t *testing.T) { if got := MaskSecret("password"); got != "***MASKED***" { t.Fatalf("MaskSecret() = %q", got) } if got := MaskOpenAIKey(""); got != "***EMPTY***" { t.Fatalf("MaskOpenAIKey(empty) = %q", got) } if got := MaskOpenAIKey("sk-test"); got != "sk-***MASKED***" { t.Fatalf("MaskOpenAIKey(non-empty) = %q", got) } got := MaskURLCredentials("postgres://user:pass@localhost/db") want := "postgres://%2A%2A%2AMASKED%2A%2A%2A:%2A%2A%2AMASKED%2A%2A%2A@localhost/db" if got != want { t.Fatalf("MaskURLCredentials() = %q, want %q", got, want) } } func TestDialogueConfigDefaults(t *testing.T) { path := filepath.Join(t.TempDir(), "dialogue.env") content := `APP_ENV=dev LOG_LEVEL=debug ASTERISK_ARI_URL=http://127.0.0.1:8088/ari ASTERISK_ARI_WS_URL=ws://127.0.0.1:8088/ari/events ASTERISK_ARI_USER=ai_operator ASTERISK_ARI_PASSWORD=test-password ASTERISK_ARI_APP=ai-operator ASTERISK_MEDIA_MODE=chan_websocket ASTERISK_MEDIA_CODEC=slin16 ` if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatal(err) } cfg, _, err := Load(path) if err != nil { t.Fatal(err) } if !cfg.Dialogue.EnforceLanguageAndRegion { t.Fatal("dialogue enforcement should default true") } if cfg.Dialogue.DefaultLanguage != "" || cfg.Dialogue.DefaultRegionCode != "" { t.Fatal("dialogue defaults should be empty") } } func TestDialogueEnforcementCannotBeDisabledInProd(t *testing.T) { path := filepath.Join(t.TempDir(), "prod.env") content := `APP_ENV=prod LOG_LEVEL=info ASTERISK_ARI_URL=http://127.0.0.1:8088/ari ASTERISK_ARI_WS_URL=ws://127.0.0.1:8088/ari/events ASTERISK_ARI_USER=ai_operator ASTERISK_ARI_PASSWORD=test-password ASTERISK_ARI_APP=ai-operator ASTERISK_MEDIA_MODE=chan_websocket ASTERISK_MEDIA_CODEC=slin16 DIALOGUE_ENFORCE_LANGUAGE_REGION=false ` if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatal(err) } if _, _, err := Load(path); err == nil { t.Fatal("expected prod validation error") } } func TestLanguageConfigDefaults(t *testing.T) { path := filepath.Join(t.TempDir(), "language.env") content := `APP_ENV=dev LOG_LEVEL=debug ASTERISK_ARI_URL=http://127.0.0.1:8088/ari ASTERISK_ARI_WS_URL=ws://127.0.0.1:8088/ari/events ASTERISK_ARI_USER=ai_operator ASTERISK_ARI_PASSWORD=test-password ASTERISK_ARI_APP=ai-operator ASTERISK_MEDIA_MODE=chan_websocket ASTERISK_MEDIA_CODEC=slin16 ` if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatal(err) } cfg, _, err := Load(path) if err != nil { t.Fatal(err) } if !cfg.Dialogue.LanguageSelectionEnabled || cfg.Dialogue.LanguageConfidenceThreshold != 0.70 || cfg.Dialogue.LanguageChangeConfidenceThreshold != 0.90 || !cfg.Dialogue.LanguageAllowChangeAfterSelection || cfg.Dialogue.LanguageDefaultOnTimeout != "" { t.Fatalf("bad language defaults: %+v", cfg.Dialogue) } } func TestLanguageSelectionCannotBeDisabledInProd(t *testing.T) { path := filepath.Join(t.TempDir(), "prod-language.env") content := `APP_ENV=prod LOG_LEVEL=info ASTERISK_ARI_URL=http://127.0.0.1:8088/ari ASTERISK_ARI_WS_URL=ws://127.0.0.1:8088/ari/events ASTERISK_ARI_USER=ai_operator ASTERISK_ARI_PASSWORD=test-password ASTERISK_ARI_APP=ai-operator ASTERISK_MEDIA_MODE=chan_websocket ASTERISK_MEDIA_CODEC=slin16 LANGUAGE_SELECTION_ENABLED=false ` if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatal(err) } if _, _, err := Load(path); err == nil { t.Fatal("expected prod language validation error") } }