# Run from /backend with: python manage.py test matching
import json
from django.test import TestCase
from django.urls import reverse
from matching.models import Score
from matching.utils import UNMATCH_CONFIRMATION
from users.models import User
[docs]
class MatchingAPITests(TestCase):
[docs]
def setUp(self):
# Create a verified mentee and two mentors
self.password = 'testpassword123'
self.mentee = User.objects.create_user(email="mentee@ucla.edu", password=self.password, role='MENTEE', is_verified=True)
self.mentor1 = User.objects.create_user(email="mentor1@ucla.edu", password=self.password, role='MENTOR', is_verified=True)
self.mentor2 = User.objects.create_user(email="mentor2@ucla.edu", password=self.password, role='MENTOR', is_verified=True)
# Set up mandatory profile data so they appear in matches
for u in [self.mentee, self.mentor1, self.mentor2]:
u.major = ["Computer Science"]
u.year = "2"
u.save()
Score.objects.update_or_create(
mentee_email=self.mentee.email,
mentor_email=self.mentor1.email,
defaults={'score': 85.0}
)
Score.objects.update_or_create(
mentee_email=self.mentee.email,
mentor_email=self.mentor2.email,
defaults={'score': 70.0}
)
[docs]
def test_get_mentor_matches_ranked(self):
"""Ensures mentee receives mentors sorted by high score first"""
self.client.login(email=self.mentee.email, password=self.password)
response = self.client.get(reverse('mentor_matches'))
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data['total'], 2)
# Verify mentor1 (score 85) is listed before mentor2 (score 70)
self.assertEqual(data['matches'][0]['email'], self.mentor1.email)
[docs]
def test_select_mentor_success(self):
"""Ensures a mentee can successfully match with a mentor"""
self.client.login(email=self.mentee.email, password=self.password)
payload = {'mentor_email': self.mentor1.email}
response = self.client.post(reverse('select_mentor'), data=json.dumps(payload), content_type='application/json')
self.assertEqual(response.status_code, 200)
self.mentee.refresh_from_db()
self.assertTrue(self.mentee.isMatched)
self.assertEqual(self.mentee.matchedMentorEmail, self.mentor1.email)
[docs]
def test_mentor_capacity_limit(self):
"""Ensures a mentor with 2 mentees is rejected"""
# Manually fill mentor1's slots
self.mentor1.current_mentees = ["student1@ucla.edu", "student2@ucla.edu"]
self.mentor1.save()
self.client.login(email=self.mentee.email, password=self.password)
payload = {'mentor_email': self.mentor1.email}
response = self.client.post(reverse('select_mentor'), data=json.dumps(payload), content_type='application/json')
# Should return 409 Conflict as mentor is full
self.assertEqual(response.status_code, 409)
[docs]
def test_unauthorized_role_blocked(self):
"""Ensures a mentor cannot access mentee-only matching endpoints"""
self.client.login(email=self.mentor1.email, password=self.password)
response = self.client.get(reverse('mentor_matches'))
# Should return 403 Forbidden
self.assertEqual(response.status_code, 403)
[docs]
def test_incomplete_profile_no_matches(self):
"""Ensures users without major/year see 0 matches"""
# Create a user with no major or year
new_mentee = User.objects.create_user(
email="incomplete@ucla.edu",
password=self.password,
role='MENTEE',
is_verified=True
)
self.client.login(email=new_mentee.email, password=self.password)
response = self.client.get(reverse('mentor_matches'))
self.assertEqual(response.status_code, 200)
data = response.json()
# Should be 0 because Score signals don't fire for incomplete profiles
self.assertEqual(data['total'], 0)
[docs]
def test_unmatch_lifecycle(self):
"""Verifies unmatching clears state and blacklists the mentor"""
# First, establish a match
self.mentee.isMatched = True
self.mentee.matchedMentorEmail = self.mentor1.email
self.mentee.save()
self.mentor1.current_mentees = [self.mentee.email]
self.mentor1.save()
self.client.login(email=self.mentee.email, password=self.password)
# The unmatch endpoint requires a confirmation string
payload = {
'mentor_email': self.mentor1.email,
'confirmation': UNMATCH_CONFIRMATION
}
response = self.client.post(reverse('unmatch'), data=json.dumps(payload), content_type='application/json')
self.assertEqual(response.status_code, 200)
self.mentee.refresh_from_db()
self.mentor1.refresh_from_db()
# Verify relationship is cleared
self.assertFalse(self.mentee.isMatched)
self.assertEqual(self.mentee.matchedMentorEmail, '')
self.assertNotIn(self.mentee.email, self.mentor1.current_mentees)
# Verify mentor is blacklisted so they don't show up in matches again
self.assertIn(self.mentor1.email, self.mentee.blacklisted_mentors)
[docs]
def test_deleted_user_cannot_match(self):
"""Ensures users pending deletion are blocked from matching"""
self.mentee.request_deletion() # Sets is_deleted=True
self.client.login(email=self.mentee.email, password=self.password)
response = self.client.get(reverse('mentor_matches'))
# Should return 403 or specific error defined in require_not_deleted
self.assertEqual(response.status_code, 403)
self.assertIn('Account pending deletion', response.json()['error'])
[docs]
def test_rematch_endpoint_logic(self):
"""Verifies the rematch endpoint returns the same ranked data"""
self.client.login(email=self.mentee.email, password=self.password)
response = self.client.get(reverse('rematch'))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['total'], 2)