Brak opisu

tests.py 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. from django.test import TestCase
  2. from django.contrib.auth.models import User
  3. from rest_framework.test import APIClient
  4. from django.utils import timezone
  5. from api.models import Profile, Opportunity, IntroductionRequest, BenefitEvent
  6. from api.services.matching import (
  7. jaccard,
  8. cosine,
  9. tokenize_interests_text,
  10. rank_users_for,
  11. rank_opportunities_for,
  12. )
  13. from api.services.benefit import log_benefit_event, build_leaderboard_queryset
  14. class SimilarityUtilsTests(TestCase):
  15. def test_tokenize_and_jaccard(self):
  16. a = tokenize_interests_text("AI, Data Science")
  17. b = tokenize_interests_text("data science, analytics")
  18. self.assertGreater(jaccard(a, b), 0)
  19. self.assertLessEqual(jaccard(a, b), 1)
  20. def test_cosine(self):
  21. self.assertAlmostEqual(cosine(["ai", "ai"], ["ai"]) > 0, True)
  22. self.assertEqual(cosine(["a"], ["b"]), 0.0)
  23. class MatchingServiceTests(TestCase):
  24. def setUp(self):
  25. # Users
  26. self.u1 = User.objects.create_user(username="alice", password="x")
  27. self.u2 = User.objects.create_user(username="bob", password="x")
  28. self.u3 = User.objects.create_user(username="carol", password="x")
  29. # Profiles (user post_save signal creates a default Profile; update it)
  30. self.p1 = Profile.objects.get(user=self.u1)
  31. self.p1.bio = "Loves AI and data"
  32. self.p1.interests = "AI, Data"
  33. self.p1.is_verified = True
  34. self.p1.save()
  35. self.p2 = Profile.objects.get(user=self.u2)
  36. self.p2.bio = "AI researcher"
  37. self.p2.interests = "AI, ML"
  38. self.p2.save()
  39. self.p3 = Profile.objects.get(user=self.u3)
  40. self.p3.bio = "Designer"
  41. self.p3.interests = "UX, UI"
  42. self.p3.save()
  43. # Taggit tags (best-effort; fine if taggit not fully migrated yet)
  44. try:
  45. self.p1.tags.set(["ai", "data"]) # prefer tags in matching
  46. self.p2.tags.set(["ai", "ml"])
  47. self.p3.tags.set(["ux", "ui"])
  48. except Exception:
  49. pass
  50. self.p1.industry = "Technology"
  51. self.p2.industry = "Technology"
  52. self.p3.industry = "Design"
  53. self.p1.save(); self.p2.save(); self.p3.save()
  54. # Opportunity
  55. self.o1 = Opportunity.objects.create(title="Build Analytics Dashboard", description="Work with data and AI dashboards")
  56. def test_rank_users_for_returns_scored_results(self):
  57. ranked = rank_users_for(self.u1, k=5)
  58. self.assertTrue(any(r.profile.user == self.u2 for r in ranked))
  59. # Existing request excludes candidate
  60. IntroductionRequest.objects.create(from_user=self.u1, to_user=self.u2, message="hi")
  61. ranked2 = rank_users_for(self.u1, k=5)
  62. self.assertFalse(any(r.profile.user == self.u2 for r in ranked2))
  63. def test_rank_opportunities_for_uses_text_similarity(self):
  64. ranked = rank_opportunities_for(self.u1, k=5)
  65. self.assertTrue(any(r.opportunity == self.o1 and r.score > 0 for r in ranked))
  66. class MatchingEndpointsTests(TestCase):
  67. def setUp(self):
  68. self.client = APIClient()
  69. self.user = User.objects.create_user(username="dave", password="x")
  70. self.other = User.objects.create_user(username="erin", password="x")
  71. self.pu = Profile.objects.get(user=self.user)
  72. self.pu.bio = "Data person"
  73. self.pu.interests = "Data, Analytics"
  74. self.pu.save()
  75. self.po = Profile.objects.get(user=self.other)
  76. self.po.bio = "Analytics work"
  77. self.po.interests = "Analytics, BI"
  78. self.po.save()
  79. self.pu.industry = "Technology"
  80. self.po.industry = "Technology"
  81. self.pu.save(); self.po.save()
  82. try:
  83. self.pu.tags.set(["data", "analytics"]) # optional
  84. self.po.tags.set(["analytics", "bi"]) # optional
  85. except Exception:
  86. pass
  87. Opportunity.objects.create(title="Analytics Role", description="Looking for data analytics help")
  88. def test_profiles_match_requires_auth(self):
  89. resp = self.client.get("/api/profiles/match/")
  90. self.assertIn(resp.status_code, (401, 403))
  91. def test_profiles_match_returns_results_for_authed_user(self):
  92. self.client.force_authenticate(user=self.user)
  93. resp = self.client.get("/api/profiles/match/?k=10")
  94. self.assertEqual(resp.status_code, 200)
  95. self.assertIsInstance(resp.json(), list)
  96. class BenefitAndLeaderboardTests(TestCase):
  97. def setUp(self):
  98. self.client = APIClient()
  99. self.a = User.objects.create_user(username="alice", password="x")
  100. self.b = User.objects.create_user(username="bob", password="x")
  101. self.c = User.objects.create_user(username="carol", password="x")
  102. # Default user for authenticated endpoints in this suite
  103. self.user = self.a
  104. def test_benefit_event_disallows_self(self):
  105. with self.assertRaises(Exception):
  106. BenefitEvent.objects.create(
  107. benefactor=self.a,
  108. beneficiary=self.a,
  109. kind=BenefitEvent.KIND_REFERRAL,
  110. points=10,
  111. )
  112. def test_leaderboard_all_time_and_weekly(self):
  113. # a helps b twice, c once
  114. log_benefit_event(benefactor=self.a, beneficiary=self.b, kind=BenefitEvent.KIND_REFERRAL)
  115. log_benefit_event(benefactor=self.a, beneficiary=self.c, kind=BenefitEvent.KIND_ACCEPTED_ANSWER)
  116. # c older event outside 7d window
  117. old = BenefitEvent(
  118. benefactor=self.c,
  119. beneficiary=self.b,
  120. kind=BenefitEvent.KIND_RECOMMENDATION,
  121. points=5,
  122. )
  123. old.created_at = timezone.now() - timezone.timedelta(days=60)
  124. old.save()
  125. all_qs = build_leaderboard_queryset("all")
  126. usernames = [u.username for u in all_qs]
  127. self.assertIn("alice", usernames)
  128. weekly_qs = build_leaderboard_queryset("weekly")
  129. w_usernames = [u.username for u in weekly_qs]
  130. self.assertIn("alice", w_usernames)
  131. # carol's only event is too old for weekly
  132. self.assertNotIn("carol", w_usernames)
  133. def test_leaderboard_endpoints(self):
  134. log_benefit_event(benefactor=self.a, beneficiary=self.b, kind=BenefitEvent.KIND_REFERRAL)
  135. # Public leaderboard
  136. resp = self.client.get("/api/leaderboard/?period=all&limit=10")
  137. self.assertEqual(resp.status_code, 200)
  138. self.assertIn("results", resp.json())
  139. # Me endpoint requires auth
  140. resp2 = self.client.get("/api/leaderboard/me/?period=all")
  141. self.assertIn(resp2.status_code, (401, 403))
  142. self.client.force_authenticate(user=self.a)
  143. resp3 = self.client.get("/api/leaderboard/me/?period=all")
  144. self.assertEqual(resp3.status_code, 200)
  145. self.assertIsNotNone(resp3.json().get("result"))
  146. def test_opportunities_recommend_returns_results_for_authed_user(self):
  147. self.client.force_authenticate(user=self.user)
  148. resp = self.client.get("/api/opportunities/recommend/?k=10")
  149. self.assertEqual(resp.status_code, 200)
  150. self.assertIsInstance(resp.json(), list)