gapguide-api / tests /test_qa_perf.py
arifRB's picture
Deploy GapGuide backend (Docker)
ffd36e0 verified
Raw
History Blame Contribute Delete
3.36 kB
"""Query-count QA checks — converted from scripts/qa_perf_checks.py (F33).
The original script mutated the configured DB (created a user + 50 UserSkills,
best-effort cleanup at the end) and only *printed* the query counts. Here the
same paths run against the pytest test DB and the CaptureQueriesContext bounds
become hard `django_assert_max_num_queries` assertions — so an accidental N+1
regression fails the suite instead of scrolling past in stdout.
"""
from io import StringIO
import pytest
from django.contrib.auth import get_user_model
from django.core.management import call_command
from rest_framework.test import APIClient
from apps.analysis.services import compute_gap_report, compute_recommendations
from apps.progress.models import UserProgress
from apps.resources.models import Resource
from apps.roles.models import Role, UserTargetRole
from apps.skills.models import Skill, UserSkill
User = get_user_model()
@pytest.fixture
def seeded(db):
call_command('seed_initial_skills', stdout=StringIO())
call_command('seed_initial_roles', stdout=StringIO())
call_command('seed_initial_resources', stdout=StringIO())
return True
@pytest.fixture
def perf_user(seeded):
"""A user with an active target role and 50 UserSkills — enough to prove
the query count stays O(1) in the number of skills."""
u = User.objects.create_user(username='qa.perf@test.xx', email='qa.perf@test.xx',
password='x!9AAAAA', name='QP')
role = Role.objects.filter(is_active=True).first()
UserTargetRole.objects.create(user=u, role=role, is_active=True)
for s in Skill.objects.all()[:50]:
UserSkill.objects.create(user=u, skill=s, proficiency=70, user_level='ADVANCED')
# Several progress rows so the progress-list query count is a real N+1
# guard (an empty list would pass the bound trivially).
for res in Resource.objects.all()[:10]:
UserProgress.objects.create(user=u, resource=res,
status='IN_PROGRESS', progress=40)
return u, role
@pytest.mark.django_db
def test_gap_report_query_count(perf_user, django_assert_max_num_queries):
u, role = perf_user
with django_assert_max_num_queries(5):
compute_gap_report(u, role)
@pytest.mark.django_db
def test_recommendations_query_count(perf_user, django_assert_max_num_queries):
u, role = perf_user
with django_assert_max_num_queries(6):
compute_recommendations(u, role)
@pytest.mark.django_db
def test_resource_detail_query_count(perf_user, django_assert_max_num_queries):
u, _ = perf_user
c = APIClient(); c.force_authenticate(user=u)
res = Resource.objects.order_by('id').first()
with django_assert_max_num_queries(8):
c.get(f'/api/resources/{res.id}/')
@pytest.mark.django_db
def test_progress_list_query_count(perf_user, django_assert_max_num_queries):
u, _ = perf_user
c = APIClient(); c.force_authenticate(user=u)
# The original only printed this count. With 10 progress rows the list is
# O(1) in N (3 queries observed); bound at 6 so an accidental N+1 (which
# would push it to ~13 with 10 rows) fails the guard with margin to spare.
with django_assert_max_num_queries(6):
c.get('/api/progress/')