diff --git a/api/src/cartsnitch_api/config.py b/api/src/cartsnitch_api/config.py index 5111997..7642deb 100644 --- a/api/src/cartsnitch_api/config.py +++ b/api/src/cartsnitch_api/config.py @@ -1,13 +1,16 @@ import base64 -from pydantic import model_validator +from pydantic import AliasChoices, Field, model_validator from pydantic_settings import BaseSettings class Settings(BaseSettings): model_config = {"env_prefix": "CARTSNITCH_"} - database_url: str = "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + database_url: str = Field( + default="postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch", + validation_alias=AliasChoices("CARTSNITCH_DATABASE_URL", "DATABASE_URL"), + ) redis_url: str = "redis://localhost:6379/0" jwt_secret_key: str = "change-me-in-production" @@ -49,5 +52,12 @@ class Settings(BaseSettings): ) from None return self + @model_validator(mode="after") + def normalize_database_url(self): + """Normalize postgresql:// → postgresql+asyncpg:// for the asyncpg driver.""" + if self.database_url.startswith("postgresql://"): + self.database_url = self.database_url.replace("postgresql://", "postgresql+asyncpg://", 1) + return self + settings = Settings() diff --git a/api/tests/test_config.py b/api/tests/test_config.py new file mode 100644 index 0000000..f594bc2 --- /dev/null +++ b/api/tests/test_config.py @@ -0,0 +1,48 @@ +"""Tests for Settings config, specifically the database_url env var fallback.""" + +import os + +from cartsnitch_api.config import Settings + + +def test_database_url_prefers_cartsnitch_prefix(): + """CARTSNITCH_DATABASE_URL takes precedence over DATABASE_URL.""" + env = { + "CARTSNITCH_DATABASE_URL": "postgresql+asyncpg://user1:pass1@host1:5432/db1", + "DATABASE_URL": "postgresql://user2:pass2@host2:5432/db2", + } + settings = Settings(**env) + assert settings.database_url == "postgresql+asyncpg://user1:pass1@host1:5432/db1" + + +def test_database_url_falls_back_to_database_url(): + """When CARTSNITCH_DATABASE_URL is absent, DATABASE_URL is accepted.""" + env = { + "DATABASE_URL": "postgresql://user:pass@dbhost:5432/mydb", + } + settings = Settings(**env) + assert settings.database_url == "postgresql+asyncpg://user:pass@dbhost:5432/mydb" + + +def test_database_url_normalizes_plain_postgresql_prefix(): + """DATABASE_URL with plain postgresql:// is normalized to postgresql+asyncpg://.""" + env = { + "DATABASE_URL": "postgresql://cartsnitch:cartsnitch@localhost:5432/cartsnitch", + } + settings = Settings(**env) + assert settings.database_url == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + + +def test_database_url_preserves_asyncpg_prefix(): + """CARTSNITCH_DATABASE_URL with postgresql+asyncpg:// is left unchanged.""" + env = { + "CARTSNITCH_DATABASE_URL": "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch", + } + settings = Settings(**env) + assert settings.database_url == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch" + + +def test_database_url_default(): + """When neither env var is set, the hardcoded default is used.""" + settings = Settings() + assert settings.database_url == "postgresql+asyncpg://cartsnitch:cartsnitch@localhost:5432/cartsnitch"