package config import "testing" func TestCookieSecureDerivation(t *testing.T) { t.Setenv("COOKIE_SECURE", "") https := &Config{NextAuthURL: "https://app.example.com"} if !https.CookieSecure() { t.Fatal("https NEXTAUTH_URL must imply Secure cookies") } local := &Config{NextAuthURL: "http://localhost:3000"} if local.CookieSecure() { t.Fatal("http NEXTAUTH_URL must not set Secure") } if (&Config{}).CookieSecure() { t.Fatal("unset NEXTAUTH_URL must not set Secure") } t.Setenv("COOKIE_SECURE", "1") if !local.CookieSecure() { t.Fatal("explicit COOKIE_SECURE=1 must win over http URL") } t.Setenv("COOKIE_SECURE", "0") if https.CookieSecure() { t.Fatal("explicit COOKIE_SECURE=0 must win over https URL") } } func TestSecretPrecedence(t *testing.T) { c := &Config{NextAuthSecret: "next", AuthSecret: "auth"} if c.Secret() != "next" { t.Fatal("NEXTAUTH_SECRET must win") } if (&Config{AuthSecret: "auth"}).Secret() != "auth" { t.Fatal("AUTH_SECRET fallback broken") } } // Review M-12: fail fast on unusable configuration at startup. func TestValidate(t *testing.T) { if err := (&Config{}).Validate(); err == nil { t.Fatal("missing secret passed validation") } if err := (&Config{AuthSecret: "secret", NextAuthURL: "not a url"}).Validate(); err == nil { t.Fatal("relative NEXTAUTH_URL passed validation") } for _, u := range []string{"", "http://localhost:3000", "https://app.example.com"} { if err := (&Config{AuthSecret: "secret", NextAuthURL: u}).Validate(); err != nil { t.Fatalf("valid configuration %q rejected: %v", u, err) } } } // Review M-12: the parsed ADMIN_EMAILS list is memoized but still follows // env changes (tests and operator restarts rely on that). func TestAdminEmailsTracksEnv(t *testing.T) { t.Setenv("ADMIN_EMAILS", " A@Example.com ,, b@example.com ") if got := AdminEmails(); len(got) != 2 || got[0] != "a@example.com" || got[1] != "b@example.com" { t.Fatalf("unexpected parse: %v", got) } t.Setenv("ADMIN_EMAILS", "") if got := AdminEmails(); len(got) != 0 { t.Fatalf("cache did not follow the env change: %v", got) } if IsAdminEmail("a@example.com") { t.Fatal("stale admin email after env change") } } // Review OPS-01: a dedicated ENCRYPTION_KEY decouples integration-credential // encryption from the session signing secret. func TestEncryptionSecretPrecedence(t *testing.T) { c := &Config{AuthSecret: "auth"} t.Setenv("ENCRYPTION_KEY", "") if c.EncryptionSecret() != "auth" { t.Fatal("without ENCRYPTION_KEY the auth secret must remain the key") } t.Setenv("ENCRYPTION_KEY", "dedicated-key") if c.EncryptionSecret() != "dedicated-key" { t.Fatal("ENCRYPTION_KEY must win over the auth secret") } }