fix: remove unit tests redundant with e2e, fix test DB isolation
Remove 7 test files fully covered by e2e tests (admin, account, models, API routes, integration MFA/OIDC, notifications). Trim 5 more files to keep only edge cases not reachable via e2e. Fix conftest to replace wiregui.db engine/session at import time so all code uses the test database. Use session-scoped tables with per-test savepoint isolation to prevent data leaking between tests.
This commit is contained in:
parent
a9f62d5caf
commit
a012635dff
15 changed files with 153 additions and 2006 deletions
|
|
@ -1,15 +1,12 @@
|
|||
"""Tests for API dependency injection — Bearer token auth and admin guard."""
|
||||
|
||||
import hashlib
|
||||
from datetime import timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from wiregui.auth.api_token import generate_api_token
|
||||
from wiregui.auth.api_token import generate_api_token, resolve_bearer_token
|
||||
from wiregui.auth.passwords import hash_password
|
||||
from wiregui.db import async_session
|
||||
from wiregui.models.api_token import ApiToken
|
||||
from wiregui.models.user import User
|
||||
from wiregui.utils.time import utcnow
|
||||
|
|
@ -18,143 +15,80 @@ from wiregui.utils.time import utcnow
|
|||
# ========== resolve_bearer_token ==========
|
||||
|
||||
|
||||
async def test_resolve_valid_token():
|
||||
async def test_resolve_valid_token(session):
|
||||
"""Valid, non-expired token resolves to user."""
|
||||
from wiregui.auth.api_token import resolve_bearer_token
|
||||
|
||||
plaintext, token_hash = generate_api_token()
|
||||
|
||||
async with async_session() as session:
|
||||
user = User(email="api-test@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
user = User(email="api-test@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.flush()
|
||||
|
||||
api_token = ApiToken(
|
||||
token_hash=token_hash,
|
||||
user_id=user.id,
|
||||
expires_at=utcnow() + timedelta(hours=1),
|
||||
)
|
||||
session.add(api_token)
|
||||
await session.commit()
|
||||
api_token = ApiToken(token_hash=token_hash, user_id=user.id, expires_at=utcnow() + timedelta(hours=1))
|
||||
session.add(api_token)
|
||||
await session.flush()
|
||||
|
||||
try:
|
||||
async with async_session() as session:
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is not None
|
||||
assert resolved.id == user.id
|
||||
assert resolved.email == "api-test@test.com"
|
||||
finally:
|
||||
async with async_session() as session:
|
||||
await session.delete(await session.get(ApiToken, api_token.id))
|
||||
await session.delete(await session.get(User, user.id))
|
||||
await session.commit()
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is not None
|
||||
assert resolved.id == user.id
|
||||
assert resolved.email == "api-test@test.com"
|
||||
|
||||
|
||||
async def test_resolve_expired_token():
|
||||
async def test_resolve_expired_token(session):
|
||||
"""Expired token returns None."""
|
||||
from wiregui.auth.api_token import resolve_bearer_token
|
||||
|
||||
plaintext, token_hash = generate_api_token()
|
||||
|
||||
async with async_session() as session:
|
||||
user = User(email="api-expired@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
user = User(email="api-expired@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.flush()
|
||||
|
||||
api_token = ApiToken(
|
||||
token_hash=token_hash,
|
||||
user_id=user.id,
|
||||
expires_at=utcnow() - timedelta(hours=1), # already expired
|
||||
)
|
||||
session.add(api_token)
|
||||
await session.commit()
|
||||
api_token = ApiToken(token_hash=token_hash, user_id=user.id, expires_at=utcnow() - timedelta(hours=1))
|
||||
session.add(api_token)
|
||||
await session.flush()
|
||||
|
||||
try:
|
||||
async with async_session() as session:
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is None
|
||||
finally:
|
||||
async with async_session() as session:
|
||||
await session.delete(await session.get(ApiToken, api_token.id))
|
||||
await session.delete(await session.get(User, user.id))
|
||||
await session.commit()
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is None
|
||||
|
||||
|
||||
async def test_resolve_invalid_token():
|
||||
async def test_resolve_invalid_token(session):
|
||||
"""Nonexistent token returns None."""
|
||||
from wiregui.auth.api_token import resolve_bearer_token
|
||||
|
||||
async with async_session() as session:
|
||||
resolved = await resolve_bearer_token(session, "totally-bogus-token")
|
||||
assert resolved is None
|
||||
resolved = await resolve_bearer_token(session, "totally-bogus-token")
|
||||
assert resolved is None
|
||||
|
||||
|
||||
async def test_resolve_token_disabled_user():
|
||||
async def test_resolve_token_disabled_user(session):
|
||||
"""Token for disabled user returns None."""
|
||||
from wiregui.auth.api_token import resolve_bearer_token
|
||||
|
||||
plaintext, token_hash = generate_api_token()
|
||||
|
||||
async with async_session() as session:
|
||||
user = User(
|
||||
email="api-disabled@test.com", password_hash=hash_password("x"),
|
||||
role="admin", disabled_at=utcnow(),
|
||||
)
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
user = User(
|
||||
email="api-disabled@test.com", password_hash=hash_password("x"),
|
||||
role="admin", disabled_at=utcnow(),
|
||||
)
|
||||
session.add(user)
|
||||
await session.flush()
|
||||
|
||||
api_token = ApiToken(
|
||||
token_hash=token_hash,
|
||||
user_id=user.id,
|
||||
expires_at=utcnow() + timedelta(hours=1),
|
||||
)
|
||||
session.add(api_token)
|
||||
await session.commit()
|
||||
api_token = ApiToken(token_hash=token_hash, user_id=user.id, expires_at=utcnow() + timedelta(hours=1))
|
||||
session.add(api_token)
|
||||
await session.flush()
|
||||
|
||||
try:
|
||||
async with async_session() as session:
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is None
|
||||
finally:
|
||||
async with async_session() as session:
|
||||
await session.delete(await session.get(ApiToken, api_token.id))
|
||||
await session.delete(await session.get(User, user.id))
|
||||
await session.commit()
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is None
|
||||
|
||||
|
||||
async def test_resolve_token_no_expiry():
|
||||
async def test_resolve_token_no_expiry(session):
|
||||
"""Token without expires_at (never expires) resolves successfully."""
|
||||
from wiregui.auth.api_token import resolve_bearer_token
|
||||
|
||||
plaintext, token_hash = generate_api_token()
|
||||
|
||||
async with async_session() as session:
|
||||
user = User(email="api-noexp@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
user = User(email="api-noexp@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.flush()
|
||||
|
||||
api_token = ApiToken(
|
||||
token_hash=token_hash,
|
||||
user_id=user.id,
|
||||
expires_at=None,
|
||||
)
|
||||
session.add(api_token)
|
||||
await session.commit()
|
||||
api_token = ApiToken(token_hash=token_hash, user_id=user.id, expires_at=None)
|
||||
session.add(api_token)
|
||||
await session.flush()
|
||||
|
||||
try:
|
||||
async with async_session() as session:
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is not None
|
||||
assert resolved.id == user.id
|
||||
finally:
|
||||
async with async_session() as session:
|
||||
await session.delete(await session.get(ApiToken, api_token.id))
|
||||
await session.delete(await session.get(User, user.id))
|
||||
await session.commit()
|
||||
resolved = await resolve_bearer_token(session, plaintext)
|
||||
assert resolved is not None
|
||||
assert resolved.id == user.id
|
||||
|
||||
|
||||
# ========== get_current_api_user (via FastAPI deps) ==========
|
||||
|
|
@ -187,7 +121,7 @@ async def test_get_current_api_user_bad_scheme():
|
|||
assert exc_info.value.status_code == 401
|
||||
|
||||
|
||||
async def test_get_current_api_user_invalid_token():
|
||||
async def test_get_current_api_user_invalid_token(session):
|
||||
"""Valid Bearer scheme but bogus token raises 401."""
|
||||
from fastapi import HTTPException
|
||||
from wiregui.api.deps import get_current_api_user
|
||||
|
|
@ -195,45 +129,31 @@ async def test_get_current_api_user_invalid_token():
|
|||
request = MagicMock()
|
||||
request.headers = {"Authorization": "Bearer bogus-token-value"}
|
||||
|
||||
async with async_session() as session:
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await get_current_api_user(request, session=session)
|
||||
assert exc_info.value.status_code == 401
|
||||
assert "Invalid" in exc_info.value.detail
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await get_current_api_user(request, session=session)
|
||||
assert exc_info.value.status_code == 401
|
||||
assert "Invalid" in exc_info.value.detail
|
||||
|
||||
|
||||
async def test_get_current_api_user_valid_token():
|
||||
async def test_get_current_api_user_valid_token(session):
|
||||
"""Valid Bearer token resolves to user."""
|
||||
from wiregui.api.deps import get_current_api_user
|
||||
|
||||
plaintext, token_hash = generate_api_token()
|
||||
|
||||
async with async_session() as session:
|
||||
user = User(email="api-dep-test@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
user = User(email="api-dep-test@test.com", password_hash=hash_password("x"), role="admin")
|
||||
session.add(user)
|
||||
await session.flush()
|
||||
|
||||
api_token = ApiToken(
|
||||
token_hash=token_hash,
|
||||
user_id=user.id,
|
||||
expires_at=utcnow() + timedelta(hours=1),
|
||||
)
|
||||
session.add(api_token)
|
||||
await session.commit()
|
||||
api_token = ApiToken(token_hash=token_hash, user_id=user.id, expires_at=utcnow() + timedelta(hours=1))
|
||||
session.add(api_token)
|
||||
await session.flush()
|
||||
|
||||
try:
|
||||
request = MagicMock()
|
||||
request.headers = {"Authorization": f"Bearer {plaintext}"}
|
||||
request = MagicMock()
|
||||
request.headers = {"Authorization": f"Bearer {plaintext}"}
|
||||
|
||||
async with async_session() as session:
|
||||
resolved = await get_current_api_user(request, session=session)
|
||||
assert resolved.id == user.id
|
||||
finally:
|
||||
async with async_session() as session:
|
||||
await session.delete(await session.get(ApiToken, api_token.id))
|
||||
await session.delete(await session.get(User, user.id))
|
||||
await session.commit()
|
||||
resolved = await get_current_api_user(request, session=session)
|
||||
assert resolved.id == user.id
|
||||
|
||||
|
||||
# ========== require_admin ==========
|
||||
|
|
@ -260,4 +180,4 @@ async def test_require_admin_rejects_unprivileged():
|
|||
with pytest.raises(HTTPException) as exc_info:
|
||||
await require_admin(user=regular_user)
|
||||
assert exc_info.value.status_code == 403
|
||||
assert "Admin" in exc_info.value.detail
|
||||
assert "Admin" in exc_info.value.detail
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue