promptlooper/backend/tests/test_config.py
John Lightner 309bbacb5d MAESTRO: Create backend/config.py with Pydantic Settings and SQLite/in-process fallback
All 13 environment variables from the spec defined with proper defaults.
SQLite fallback when DATABASE_URL is unset, in-process queue flag when
REDIS_URL is unset, JWT_SECRET auto-generation, empty API_KEY normalization.
13 unit tests covering all configuration paths.
2026-04-07 01:46:30 -05:00

105 lines
3.6 KiB
Python

"""Tests for backend/config.py."""
import os
from unittest.mock import patch
import pytest
from pydantic_settings import BaseSettings
from config import Settings
class TestSettings:
"""Test the Settings configuration class."""
def _make_settings(self, **env_vars: str) -> Settings:
"""Create a Settings instance with specific env vars, ignoring .env file."""
with patch.dict(os.environ, env_vars, clear=False):
return Settings(_env_file=None)
def test_defaults(self) -> None:
s = self._make_settings()
assert s.database_url is None
assert s.redis_url is None
assert s.host == "0.0.0.0"
assert s.port == 8400
assert s.api_key is None
assert s.default_endpoint_url is None
assert s.default_endpoint_key is None
assert s.max_concurrent_runs == 4
assert s.max_tokens_per_sweep == 0
assert s.data_dir == "/data"
assert s.mcp_enabled is True
assert s.mcp_port == 8401
def test_jwt_secret_auto_generated(self) -> None:
s = self._make_settings()
assert len(s.jwt_secret) > 0
def test_jwt_secret_auto_generated_unique(self) -> None:
s1 = self._make_settings()
s2 = self._make_settings()
assert s1.jwt_secret != s2.jwt_secret
def test_jwt_secret_from_env(self) -> None:
s = self._make_settings(JWT_SECRET="my-secret-key")
assert s.jwt_secret == "my-secret-key"
def test_sqlite_fallback_when_no_database_url(self) -> None:
s = self._make_settings(DATA_DIR="/tmp/test")
url = s.effective_database_url
assert url.startswith("sqlite:///")
assert url.endswith("promptlooper.db")
assert "tmp" in url and "test" in url
assert s.is_sqlite is True
def test_postgres_when_database_url_set(self) -> None:
url = "postgresql://user:pass@localhost:5432/promptlooper"
s = self._make_settings(DATABASE_URL=url)
assert s.effective_database_url == url
assert s.is_sqlite is False
def test_in_process_queue_when_no_redis(self) -> None:
s = self._make_settings()
assert s.use_in_process_queue is True
def test_celery_queue_when_redis_set(self) -> None:
s = self._make_settings(REDIS_URL="redis://localhost:6379/0")
assert s.use_in_process_queue is False
assert s.redis_url == "redis://localhost:6379/0"
def test_empty_api_key_becomes_none(self) -> None:
s = self._make_settings(API_KEY="")
assert s.api_key is None
def test_whitespace_api_key_becomes_none(self) -> None:
s = self._make_settings(API_KEY=" ")
assert s.api_key is None
def test_valid_api_key_preserved(self) -> None:
s = self._make_settings(API_KEY="sk-test-123")
assert s.api_key == "sk-test-123"
def test_env_overrides(self) -> None:
s = self._make_settings(
HOST="127.0.0.1",
PORT="9000",
MAX_CONCURRENT_RUNS="8",
MAX_TOKENS_PER_SWEEP="100000",
MCP_ENABLED="false",
MCP_PORT="9001",
)
assert s.host == "127.0.0.1"
assert s.port == 9000
assert s.max_concurrent_runs == 8
assert s.max_tokens_per_sweep == 100000
assert s.mcp_enabled is False
assert s.mcp_port == 9001
def test_default_endpoint_config(self) -> None:
s = self._make_settings(
DEFAULT_ENDPOINT_URL="http://localhost:11434/v1",
DEFAULT_ENDPOINT_KEY="sk-key",
)
assert s.default_endpoint_url == "http://localhost:11434/v1"
assert s.default_endpoint_key == "sk-key"