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:
@@ -0,0 +1,100 @@
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def auth_headers(token):
|
||||
"""Create Authorization headers."""
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def test_db_dir():
|
||||
"""Temporary directory for test databases."""
|
||||
d = tempfile.mkdtemp(prefix="zern_test_")
|
||||
yield Path(d)
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def test_app(test_db_dir):
|
||||
"""Create FastAPI app with test database."""
|
||||
# Patch auth module paths BEFORE importing anything
|
||||
import auth
|
||||
auth.AUTH_DB = test_db_dir / "auth.db"
|
||||
auth.SECRET_KEY = test_db_dir / ".secret_key"
|
||||
auth._rate_limit_cache.clear()
|
||||
|
||||
# Initialize test database
|
||||
auth.init_db()
|
||||
|
||||
from main import app
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(test_app):
|
||||
"""TestClient instance."""
|
||||
from fastapi.testclient import TestClient
|
||||
return TestClient(test_app)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def registered_user(client):
|
||||
"""Register a unique test user."""
|
||||
import secrets
|
||||
username = f"testuser_{secrets.token_hex(4)}"
|
||||
password = "TestPassword123"
|
||||
|
||||
resp = client.post("/auth/register", json={"username": username, "password": password})
|
||||
assert resp.status_code == 200, f"Registration failed: {resp.text}"
|
||||
return {"username": username, "password": password}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def logged_in_user(client, registered_user):
|
||||
"""Login and return tokens."""
|
||||
resp = client.post("/auth/login", json=registered_user)
|
||||
assert resp.status_code == 200, f"Login failed: {resp.text}"
|
||||
data = resp.json()
|
||||
return {
|
||||
"username": registered_user["username"],
|
||||
"password": registered_user["password"],
|
||||
"access_token": data["access_token"],
|
||||
"refresh_token": data["refresh_token"],
|
||||
"expires_in": data["expires_in"],
|
||||
"uuid": data["uuid"],
|
||||
"role": data["role"],
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin_user(client):
|
||||
"""Create and login a creator/admin user."""
|
||||
import secrets
|
||||
import sqlite3
|
||||
import auth
|
||||
|
||||
username = f"admin_{secrets.token_hex(4)}"
|
||||
password = "AdminPassword123"
|
||||
|
||||
resp = client.post("/auth/register", json={"username": username, "password": password})
|
||||
assert resp.status_code == 200
|
||||
|
||||
# Promote to creator
|
||||
conn = sqlite3.connect(str(auth.AUTH_DB))
|
||||
conn.execute("UPDATE users SET role = 4 WHERE username = ?", (username,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
resp = client.post("/auth/login", json={"username": username, "password": password})
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
return {
|
||||
"username": username,
|
||||
"access_token": data["access_token"],
|
||||
"refresh_token": data["refresh_token"],
|
||||
}
|
||||
Reference in New Issue
Block a user