Spaces:
Runtime error
Runtime error
| import pytest | |
| from rest_framework.test import APIClient | |
| from apps.skills.models import Skill | |
| pytestmark = pytest.mark.django_db | |
| REGISTER_URL = '/api/auth/register/' | |
| LOGIN_URL = '/api/auth/login/' | |
| PROFILE_URL = '/api/auth/profile/' | |
| PARSE_RESUME_URL = '/api/auth/profile/parse-resume/' | |
| SKILLS_URL = '/api/skills/' | |
| USER_SKILLS_URL = '/api/user-skills/' | |
| VALID_USER = { | |
| 'name': 'Test User', | |
| 'email': 'test2@example.com', | |
| 'password': 'TestPass123!', | |
| 'password_confirm': 'TestPass123!' | |
| } | |
| def api_client(): | |
| return APIClient() | |
| def skill(db): | |
| return Skill.objects.create( | |
| skill_name='Python', | |
| category='Programming', | |
| difficulty_level='BEGINNER' | |
| ) | |
| def auth_client(api_client): | |
| api_client.post(REGISTER_URL, data=VALID_USER, format='json') | |
| login_resp = api_client.post( | |
| LOGIN_URL, | |
| data={'email': VALID_USER['email'], 'password': VALID_USER['password']}, | |
| format='json' | |
| ) | |
| token = login_resp.data['access'] | |
| api_client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}') | |
| return api_client | |
| class TestProfile: | |
| def test_get_profile(self, auth_client): | |
| """TC-05: GET /profile/ returns profile + skills.""" | |
| response = auth_client.get(PROFILE_URL) | |
| assert response.status_code == 200 | |
| assert 'user' in response.data | |
| assert 'profile' in response.data | |
| assert 'skills' in response.data | |
| def test_update_profile(self, auth_client): | |
| """TC-05: PUT /profile/ updates degree, semester, bio.""" | |
| data = { | |
| 'degree': 'BS Computer Science', | |
| 'semester': 6, | |
| 'bio': 'I love coding', | |
| 'interests': 'AI, Web Dev' | |
| } | |
| response = auth_client.put(PROFILE_URL, data=data, format='json') | |
| assert response.status_code == 200 | |
| assert response.data['profile']['degree'] == 'BS Computer Science' | |
| assert response.data['profile']['semester'] == 6 | |
| def test_update_profile_with_skills(self, auth_client, skill): | |
| """TC-06: PUT /profile/ with skills upserts UserSkills.""" | |
| data = { | |
| 'degree': 'BS CS', | |
| 'skills': [ | |
| {'skill_id': skill.id, 'proficiency': 65} | |
| ] | |
| } | |
| response = auth_client.put(PROFILE_URL, data=data, format='json') | |
| assert response.status_code == 200 | |
| skills = response.data['skills'] | |
| assert len(skills) == 1 | |
| assert skills[0]['proficiency'] == 65 | |
| assert skills[0]['user_level'] == 'ADVANCED' # 65 > 60 | |
| def test_user_level_auto_computed(self, auth_client, skill): | |
| """TC-06: user_level is auto-derived from proficiency.""" | |
| data = {'skills': [{'skill_id': skill.id, 'proficiency': 30}]} | |
| response = auth_client.put(PROFILE_URL, data=data, format='json') | |
| assert response.status_code == 200 | |
| assert response.data['skills'][0]['user_level'] == 'BEGINNER' # 30 <= 40 | |
| def test_profile_requires_auth(self, api_client): | |
| """Profile endpoints require authentication.""" | |
| response = api_client.get(PROFILE_URL) | |
| assert response.status_code == 401 | |
| class TestSkillCatalog: | |
| def test_list_skills(self, auth_client, skill): | |
| """GET /skills/ returns skill catalog (paginated).""" | |
| response = auth_client.get(SKILLS_URL) | |
| assert response.status_code == 200 | |
| assert response.data['count'] >= 1 | |
| def test_filter_by_category(self, auth_client, skill): | |
| """GET /skills/?category=Programming filters skills.""" | |
| response = auth_client.get(f'{SKILLS_URL}?category=Programming') | |
| assert response.status_code == 200 | |
| assert all(s['category'] == 'Programming' for s in response.data['results']) | |
| class TestUserSkills: | |
| def test_add_user_skill(self, auth_client, skill): | |
| """POST /user-skills/ adds a skill to user.""" | |
| data = {'skill_id': skill.id, 'proficiency': 50} | |
| response = auth_client.post(USER_SKILLS_URL, data=data, format='json') | |
| assert response.status_code == 201 | |
| assert response.data['proficiency'] == 50 | |
| assert response.data['user_level'] == 'INTERMEDIATE' | |
| def test_update_user_skill(self, auth_client, skill): | |
| """PUT /user-skills/<id>/ updates proficiency.""" | |
| # First add | |
| add_resp = auth_client.post( | |
| USER_SKILLS_URL, data={'skill_id': skill.id, 'proficiency': 30}, format='json' | |
| ) | |
| skill_id = add_resp.data['id'] | |
| # Then update | |
| update_resp = auth_client.put( | |
| f'{USER_SKILLS_URL}{skill_id}/', | |
| data={'skill_id': skill.id, 'proficiency': 70}, | |
| format='json' | |
| ) | |
| assert update_resp.status_code == 200 | |
| assert update_resp.data['user_level'] == 'ADVANCED' | |
| class TestParseResumeStub: | |
| """The Module 2 "stub" endpoint is now the real Module 8 MVP endpoint. | |
| Kept under this class name for continuity — full happy-path + edge-case | |
| coverage lives in apps/accounts/tests/test_resume_parser.py. | |
| """ | |
| def test_requires_auth(self, api_client): | |
| resp = api_client.post(PARSE_RESUME_URL, data={}, format='json') | |
| assert resp.status_code == 401 | |
| def test_empty_body_returns_400(self, auth_client): | |
| resp = auth_client.post(PARSE_RESUME_URL, data={}, format='multipart') | |
| assert resp.status_code == 400 | |
| assert 'detail' in resp.data | |