Spaces:
Sleeping
Sleeping
| """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() | |
| 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 | |
| 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 | |
| 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) | |
| 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) | |
| 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}/') | |
| 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/') | |