- tests/test_auth.py: enable/disable via env, valid/invalid creds - tests/test_rate_limit.py: bucket math + window expiry - tests/test_router_media.py: path traversal (file/thumb), paginated subreddits/authors, blacklist preview shape, DELETE flow, cleanup-by-type 400 paths - tests/test_router_stats.py: cache TTL, .json exclusion, missing dir - tests/test_router_favorites.py: paginated favorites/authors, no N+1 (query count), favorites_only filter - tests/test_router_config.py: CRUD subreddits/users/blacklist - tests/test_config_manager.py: case-insensitive dedupe, blacklist removes from collection, domain normalization - tests/test_extractors_imgur.py: i.imgur direct, gifv->mp4, albums/galleries -> None src/database.py: resolve RMC_DB_PATH lazily in Database() so tests can monkeypatch the env per case (no more module-import capture) Result: 135 passed (was 77). Coverage 55% global; 100% on auth/imgur, 96% rate_limit, 84% stats router, 80% config_manager.
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
"""Tests for optional HTTP Basic auth (src.web.auth)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from src.web.app import app
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
return TestClient(app)
|
|
|
|
|
|
def _basic(user: str, password: str) -> dict[str, str]:
|
|
token = base64.b64encode(f"{user}:{password}".encode()).decode()
|
|
return {"Authorization": f"Basic {token}"}
|
|
|
|
|
|
def test_no_auth_when_env_unset(client, monkeypatch):
|
|
"""Without RMC_AUTH_USER/RMC_AUTH_PASS, all routes are public."""
|
|
monkeypatch.delenv("RMC_AUTH_USER", raising=False)
|
|
monkeypatch.delenv("RMC_AUTH_PASS", raising=False)
|
|
response = client.get("/api/stats")
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_partial_env_does_not_enable_auth(client, monkeypatch):
|
|
monkeypatch.setenv("RMC_AUTH_USER", "alice")
|
|
monkeypatch.delenv("RMC_AUTH_PASS", raising=False)
|
|
response = client.get("/api/stats")
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_auth_required_when_env_set(client, monkeypatch):
|
|
monkeypatch.setenv("RMC_AUTH_USER", "alice")
|
|
monkeypatch.setenv("RMC_AUTH_PASS", "s3cret")
|
|
response = client.get("/api/stats")
|
|
assert response.status_code == 401
|
|
assert response.headers.get("www-authenticate", "").lower().startswith("basic")
|
|
|
|
|
|
def test_auth_accepts_valid_credentials(client, monkeypatch):
|
|
monkeypatch.setenv("RMC_AUTH_USER", "alice")
|
|
monkeypatch.setenv("RMC_AUTH_PASS", "s3cret")
|
|
response = client.get("/api/stats", headers=_basic("alice", "s3cret"))
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_auth_rejects_invalid_credentials(client, monkeypatch):
|
|
monkeypatch.setenv("RMC_AUTH_USER", "alice")
|
|
monkeypatch.setenv("RMC_AUTH_PASS", "s3cret")
|
|
response = client.get("/api/stats", headers=_basic("alice", "wrong"))
|
|
assert response.status_code == 401
|
|
|
|
|
|
def test_auth_rejects_wrong_username(client, monkeypatch):
|
|
monkeypatch.setenv("RMC_AUTH_USER", "alice")
|
|
monkeypatch.setenv("RMC_AUTH_PASS", "s3cret")
|
|
response = client.get("/api/stats", headers=_basic("bob", "s3cret"))
|
|
assert response.status_code == 401
|