📦 EqualifyEverything / equalify-reflow

📄 test_config_validation.py · 82 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82"""Settings ``model_validator`` enforces per-mode auth requirements at startup.

These tests pin ``_env_file=None`` so the local ``.env`` cannot leak in and
mask a missing-required-field assertion. We're testing the validator's logic
in isolation, not whatever the developer happens to have configured locally.
"""

from __future__ import annotations

import pytest
from argon2 import PasswordHasher
from pydantic import ValidationError
from src.config import Settings


def _settings(**overrides: object) -> Settings:
    """Build a Settings strictly from kwargs — no .env, no environ."""
    return Settings(_env_file=None, _env_file_encoding=None, **overrides)  # type: ignore[arg-type]


@pytest.mark.unit
def test_none_mode_needs_no_auth_secret() -> None:
    s = _settings(auth_mode="none")
    assert s.auth_mode == "none"


@pytest.mark.unit
def test_basic_mode_requires_secret_and_users() -> None:
    with pytest.raises(ValidationError, match="AUTH_SECRET_KEY"):
        _settings(auth_mode="basic")

    # Secret too short.
    with pytest.raises(ValidationError, match="at least 32"):
        _settings(
            auth_mode="basic",
            auth_secret_key="short",
            auth_basic_users="alice:$argon2id$xxx",
        )


@pytest.mark.unit
def test_basic_mode_rejects_empty_user_csv() -> None:
    with pytest.raises(ValidationError, match="must contain at least one"):
        _settings(
            auth_mode="basic",
            auth_secret_key="x" * 32,
            auth_basic_users="",
        )


@pytest.mark.unit
def test_basic_mode_accepts_real_argon_hash() -> None:
    h = PasswordHasher().hash("hunter2")
    s = _settings(
        auth_mode="basic",
        auth_secret_key="x" * 32,
        auth_basic_users=f"alice:{h}",
    )
    assert s.auth_mode == "basic"


@pytest.mark.unit
def test_oidc_mode_requires_provider_array() -> None:
    with pytest.raises(ValidationError, match="AUTH_OIDC_PROVIDERS"):
        _settings(auth_mode="oidc", auth_secret_key="x" * 32)
    with pytest.raises(ValidationError, match="non-empty JSON array"):
        _settings(
            auth_mode="oidc",
            auth_secret_key="x" * 32,
            auth_oidc_providers="[]",
        )


@pytest.mark.unit
def test_oidc_mode_validates_required_keys() -> None:
    with pytest.raises(ValidationError, match="missing keys"):
        _settings(
            auth_mode="oidc",
            auth_secret_key="x" * 32,
            auth_oidc_providers='[{"id": "entra"}]',
        )