Split backend tests into unit and integration suites, add isolated fixtures and coverage enforcement, and update CI and test documentation. Cover current authentication, content, notification, payment, media, and admin flows; fix behaviors revealed by the new tests. Validate both suites and the per-module gate against local PostgreSQL.
110 lines
3.1 KiB
Python
110 lines
3.1 KiB
Python
"""Shared pytest fixtures and collection checks for the backend."""
|
|
|
|
import itertools
|
|
|
|
import fakeredis
|
|
import pytest
|
|
import requests
|
|
|
|
|
|
def pytest_collection_modifyitems(items):
|
|
for item in items:
|
|
parts = item.path.parts
|
|
expected = "unit" if "unit" in parts else "integration" if "integration" in parts else None
|
|
marked = [kind for kind in ("unit", "integration") if list(item.iter_markers(kind))]
|
|
if expected is None or marked != [expected]:
|
|
raise pytest.UsageError(
|
|
f"{item.nodeid}: place tests under unit/ or integration/ "
|
|
"and mark each test with exactly its directory's marker"
|
|
)
|
|
if expected == "unit" and list(item.iter_markers("django_db")):
|
|
raise pytest.UsageError(f"{item.nodeid}: unit tests cannot use the database")
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def no_outbound_http(monkeypatch):
|
|
def refuse_request(*args, **kwargs):
|
|
raise AssertionError("Tests must fake outbound HTTP requests")
|
|
|
|
monkeypatch.setattr(requests.sessions.Session, "request", refuse_request)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def isolate_redis(monkeypatch):
|
|
from apps.notifications import services
|
|
from apps.users.services import auth
|
|
|
|
fake = fakeredis.FakeRedis(decode_responses=True)
|
|
otp_fake = fakeredis.FakeRedis(decode_responses=False)
|
|
monkeypatch.setattr(services, "redis_client", fake)
|
|
monkeypatch.setattr(auth, "get_redis_connection", lambda alias: otp_fake)
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_redis():
|
|
return fakeredis.FakeRedis(decode_responses=False)
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_notification_redis():
|
|
return fakeredis.FakeRedis(decode_responses=True)
|
|
|
|
|
|
@pytest.fixture
|
|
def user_factory(db):
|
|
from apps.users.models import User
|
|
|
|
sequence = itertools.count(1)
|
|
|
|
def create(**overrides):
|
|
index = next(sequence)
|
|
values = {
|
|
"username": f"test_user_{index}",
|
|
"mobile": f"0919000{index:04d}",
|
|
"password": "TestPass123!",
|
|
}
|
|
values.update(overrides)
|
|
return User.objects.create_user(**values)
|
|
|
|
return create
|
|
|
|
|
|
@pytest.fixture
|
|
def auth_headers():
|
|
from core.authentication import create_jwt_token
|
|
|
|
def headers(user):
|
|
return {"HTTP_AUTHORIZATION": f"Bearer {create_jwt_token(user)}"}
|
|
|
|
return headers
|
|
|
|
|
|
@pytest.fixture
|
|
def event_factory(db):
|
|
from datetime import timedelta
|
|
|
|
from django.utils import timezone
|
|
|
|
from apps.events.models import Event
|
|
|
|
sequence = itertools.count(1)
|
|
|
|
def create(**overrides):
|
|
now = timezone.now()
|
|
index = next(sequence)
|
|
values = {
|
|
"title": f"Test event {index}",
|
|
"slug": f"test-event-{index}",
|
|
"description": "Test event description",
|
|
"start_time": now + timedelta(days=2),
|
|
"end_time": now + timedelta(days=2, hours=2),
|
|
"registration_start_date": now - timedelta(days=1),
|
|
"registration_end_date": now + timedelta(days=1),
|
|
"capacity": 10,
|
|
"status": Event.StatusChoices.PUBLISHED,
|
|
}
|
|
values.update(overrides)
|
|
return Event.objects.create(**values)
|
|
|
|
return create
|