test(server): add comprehensive test suite (47 tests), fix DB lock and schema bugs

- Add pytest test suite: test_auth.py, test_admin.py, test_pass.py,
  test_proxy.py, test_rate_limit.py, test_client_contract.py
- Fix SQLite 'database is locked' errors: moved log_audit() calls outside
  with get_db() blocks in register, login, logout, refresh, activate_pass
- Enable WAL mode and busy_timeout in get_db() for concurrent access
- Fix /admin/me: removed non-existent 'email' column from query
- Fix /admin/users list: disambiguated activated_at column in JOIN query
- Fix /auth/refresh: now returns refresh_token + expires_in + username/uuid/role
  to match AuthManager.AuthSession expectations; revokes old refresh token
- Fix conftest.py: unique usernames per test to avoid conflicts
- All 47 tests passing
This commit is contained in:
SashegDev
2026-05-04 22:14:06 +00:00
parent c96b502ad4
commit c0310ed573
9 changed files with 808 additions and 52 deletions
+70
View File
@@ -0,0 +1,70 @@
"""Tests for rate limiting (TTLCache-based)."""
import pytest
from auth import check_rate_limit, record_login_attempt, MAX_LOGIN_ATTEMPTS, LOGIN_BLOCK_MINUTES
class TestRateLimit:
"""Test rate limiting functions."""
def test_no_attempts_allowed(self):
"""Fresh IP should be allowed."""
allowed, wait = check_rate_limit("fresh-ip")
assert allowed is True
assert wait is None
def test_single_attempt_allowed(self):
"""One failed attempt should still be allowed."""
ip = "single-attempt-ip"
record_login_attempt(ip, False)
allowed, wait = check_rate_limit(ip)
assert allowed is True
def test_max_attempts_blocks(self):
"""MAX_LOGIN_ATTEMPTS failed attempts should block."""
ip = "blocked-ip"
for _ in range(MAX_LOGIN_ATTEMPTS):
record_login_attempt(ip, False)
allowed, wait = check_rate_limit(ip)
assert allowed is False
assert wait is not None
assert wait > 0
# Wait should be approximately LOGIN_BLOCK_MINUTES * 60
assert wait <= LOGIN_BLOCK_MINUTES * 60
def test_success_resets_attempts(self):
"""Successful login should reset rate limit."""
ip = "reset-ip"
for _ in range(MAX_LOGIN_ATTEMPTS - 1):
record_login_attempt(ip, False)
# One success should reset
record_login_attempt(ip, True)
allowed, wait = check_rate_limit(ip)
assert allowed is True
assert wait is None
def test_success_then_fail_starts_fresh(self):
"""After success reset, failing again should start from 1."""
ip = "fresh-start-ip"
record_login_attempt(ip, False)
record_login_attempt(ip, True)
record_login_attempt(ip, False)
allowed, wait = check_rate_limit(ip)
assert allowed is True # Only 1 attempt after reset
def test_separate_ips_independent(self):
"""Rate limit should be per-IP."""
ip1 = "ip-one"
ip2 = "ip-two"
for _ in range(MAX_LOGIN_ATTEMPTS):
record_login_attempt(ip1, False)
allowed1, _ = check_rate_limit(ip1)
allowed2, _ = check_rate_limit(ip2)
assert allowed1 is False
assert allowed2 is True