media-rip/backend/tests/test_config.py
xpltd 1592407658 First-run admin setup wizard, password persistence, forced setup gate
- Admin enabled by default (was opt-in via env var)
- New /admin/status (public) and /admin/setup (first-run only) endpoints
- Setup endpoint locked after first use (returns 403)
- Admin password persisted to SQLite config table (survives restarts)
- Change password now persists to DB (was in-memory only)
- Frontend router guard forces /admin redirect until setup is complete
- AdminSetup.vue wizard: username + password + confirm
- Public config exposes admin_enabled/admin_setup_complete for frontend
- TLS warning only fires when password is actually configured
2026-03-21 20:01:13 -05:00

96 lines
3 KiB
Python

"""Tests for the pydantic-settings config system."""
from __future__ import annotations
from pathlib import Path
from app.core.config import AppConfig
class TestZeroConfig:
"""Verify AppConfig works out of the box with zero user config."""
def test_defaults_load_without_crash(self):
config = AppConfig()
assert config.server.host == "0.0.0.0"
assert config.server.port == 8000
assert config.server.db_path == "mediarip.db"
def test_downloads_defaults(self):
config = AppConfig()
assert config.downloads.output_dir == "/downloads"
assert config.downloads.max_concurrent == 3
def test_session_defaults(self):
config = AppConfig()
assert config.session.mode == "isolated"
assert config.session.timeout_hours == 72
def test_admin_defaults(self):
config = AppConfig()
assert config.admin.enabled is True
assert config.admin.username == "admin"
assert config.admin.password_hash == ""
def test_source_templates_default_entries(self):
config = AppConfig()
templates = config.downloads.source_templates
assert "youtube.com" in templates
assert "soundcloud.com" in templates
assert "*" in templates
class TestEnvVarOverride:
"""Environment variables with MEDIARIP__ prefix override defaults."""
def test_override_max_concurrent(self, monkeypatch):
monkeypatch.setenv("MEDIARIP__DOWNLOADS__MAX_CONCURRENT", "5")
config = AppConfig()
assert config.downloads.max_concurrent == 5
def test_override_server_port(self, monkeypatch):
monkeypatch.setenv("MEDIARIP__SERVER__PORT", "9000")
config = AppConfig()
assert config.server.port == 9000
def test_override_session_timeout(self, monkeypatch):
monkeypatch.setenv("MEDIARIP__SESSION__TIMEOUT_HOURS", "24")
config = AppConfig()
assert config.session.timeout_hours == 24
class TestYamlConfig:
"""YAML file loading and graceful fallback."""
def test_yaml_values_load(self, tmp_path: Path, monkeypatch):
yaml_content = """
server:
port: 7777
log_level: debug
downloads:
max_concurrent: 10
"""
yaml_file = tmp_path / "config.yaml"
yaml_file.write_text(yaml_content)
monkeypatch.setitem(AppConfig.model_config, "yaml_file", str(yaml_file))
config = AppConfig()
assert config.server.port == 7777
assert config.server.log_level == "debug"
assert config.downloads.max_concurrent == 10
def test_missing_yaml_no_crash(self, tmp_path: Path, monkeypatch):
"""A non-existent YAML path should not raise — zero-config mode."""
monkeypatch.setitem(
AppConfig.model_config, "yaml_file",
str(tmp_path / "nonexistent.yaml"),
)
config = AppConfig()
# Falls back to defaults
assert config.server.port == 8000
def test_yaml_file_none(self):
"""Explicitly None yaml_file should be fine."""
config = AppConfig()
assert config is not None