test(03-01): add Wave 0 xfail stubs and shared fixtures for Phase 3
- Add auth_user, admin_user, mock_minio_presigned, mock_minio_stat fixtures to conftest.py - Create test_quota.py with 4 xfail stubs (STORE-03, STORE-05, STORE-06, SC2 race) - Append test_migration_0003 to test_alembic.py (full pre-seed + post-migration assertions) - Append 3 classifier xfail stubs (DOC-03, DOC-05, D-15) - Append 6 document xfail stubs (D-05, STORE-04, SEC-04, D-16) - Append 4 topic xfail stubs (DOC-04, D-09, D-17) - Append test_settings_endpoint_removed stub (D-12) - All 19 new test IDs collect cleanly with xfail(strict=False)
This commit is contained in:
@@ -176,3 +176,129 @@ def sample_pdf(tmp_path):
|
||||
doc.save(str(pdf_path))
|
||||
doc.close()
|
||||
return pdf_path
|
||||
|
||||
|
||||
# ── Phase 3 shared fixtures ───────────────────────────────────────────────────
|
||||
# These fixtures are used by test_quota.py, test_documents.py, test_topics.py,
|
||||
# and test_classifier.py in Plans 03-02 through 03-04.
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def auth_user(db_session: AsyncSession):
|
||||
"""Create a regular user with a Quota row and return auth context.
|
||||
|
||||
Returns dict with keys:
|
||||
- user: User ORM instance
|
||||
- token: signed JWT access token
|
||||
- headers: {"Authorization": "Bearer <token>"}
|
||||
|
||||
The fixture issues a valid access token via services.auth.create_access_token
|
||||
so that get_current_user accepts it in downstream endpoint tests.
|
||||
"""
|
||||
import uuid as _uuid
|
||||
from db.models import User, Quota
|
||||
from services.auth import hash_password, create_access_token
|
||||
|
||||
user_id = _uuid.uuid4()
|
||||
user = User(
|
||||
id=user_id,
|
||||
handle=f"testuser_{user_id.hex[:8]}",
|
||||
email=f"testuser_{user_id.hex[:8]}@example.com",
|
||||
password_hash=hash_password("Testpassword123!"),
|
||||
role="user",
|
||||
is_active=True,
|
||||
password_must_change=False,
|
||||
)
|
||||
quota = Quota(
|
||||
user_id=user_id,
|
||||
limit_bytes=104857600, # 100 MB
|
||||
used_bytes=0,
|
||||
)
|
||||
db_session.add(user)
|
||||
db_session.add(quota)
|
||||
await db_session.commit()
|
||||
|
||||
token = create_access_token(str(user_id), "user")
|
||||
return {
|
||||
"user": user,
|
||||
"token": token,
|
||||
"headers": {"Authorization": f"Bearer {token}"},
|
||||
}
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def admin_user(db_session: AsyncSession):
|
||||
"""Create an admin user with a Quota row and return auth context.
|
||||
|
||||
Returns the same dict shape as auth_user but with role="admin".
|
||||
"""
|
||||
import uuid as _uuid
|
||||
from db.models import User, Quota
|
||||
from services.auth import hash_password, create_access_token
|
||||
|
||||
user_id = _uuid.uuid4()
|
||||
user = User(
|
||||
id=user_id,
|
||||
handle=f"adminuser_{user_id.hex[:8]}",
|
||||
email=f"adminuser_{user_id.hex[:8]}@example.com",
|
||||
password_hash=hash_password("Testpassword123!"),
|
||||
role="admin",
|
||||
is_active=True,
|
||||
password_must_change=False,
|
||||
)
|
||||
quota = Quota(
|
||||
user_id=user_id,
|
||||
limit_bytes=104857600,
|
||||
used_bytes=0,
|
||||
)
|
||||
db_session.add(user)
|
||||
db_session.add(quota)
|
||||
await db_session.commit()
|
||||
|
||||
token = create_access_token(str(user_id), "admin")
|
||||
return {
|
||||
"user": user,
|
||||
"token": token,
|
||||
"headers": {"Authorization": f"Bearer {token}"},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_minio_presigned(monkeypatch):
|
||||
"""Patch MinIOBackend.generate_presigned_put_url with an AsyncMock.
|
||||
|
||||
The patched method does not exist yet — it is added in Plan 03-02.
|
||||
Using raising=False ensures the patch installs before the attribute exists.
|
||||
|
||||
Yields the AsyncMock so tests can assert call counts and args.
|
||||
"""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
mock = AsyncMock(return_value="http://localhost:9000/docuvault/test-presigned-url")
|
||||
try:
|
||||
from storage.minio_backend import MinIOBackend
|
||||
monkeypatch.setattr(MinIOBackend, "generate_presigned_put_url", mock, raising=False)
|
||||
except ImportError:
|
||||
pass # storage module not yet available — patch is best-effort
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_minio_stat(monkeypatch):
|
||||
"""Patch MinIOBackend.stat_object with an AsyncMock returning 1024 bytes.
|
||||
|
||||
The patched method does not exist yet — it is added in Plan 03-02.
|
||||
Using raising=False ensures the patch installs before the attribute exists.
|
||||
|
||||
Yields the AsyncMock for per-test customization:
|
||||
mock_minio_stat.return_value = 50_000_000
|
||||
"""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
mock = AsyncMock(return_value=1024)
|
||||
try:
|
||||
from storage.minio_backend import MinIOBackend
|
||||
monkeypatch.setattr(MinIOBackend, "stat_object", mock, raising=False)
|
||||
except ImportError:
|
||||
pass # storage module not yet available — patch is best-effort
|
||||
yield mock
|
||||
|
||||
Reference in New Issue
Block a user