Skip to content

Commit 77e86a7

Browse files
Fix leaderboard auto-enrollment and add comprehensive tests
- Leaderboard no longer auto-enrolls authenticated users - Add test: course_view does not auto-enroll - Add test: leaderboard_view does not auto-enroll - Add test: leaderboard for unauthenticated users - Add test: leaderboard for authenticated without enrollment - Add test: leaderboard for authenticated with enrollment - Update test: not_enrolled_yet_but_leaderboard_displays Users now only get enrolled when they explicitly visit the enrollment page and submit their details. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 8579da7 commit 77e86a7

2 files changed

Lines changed: 199 additions & 9 deletions

File tree

courses/tests/test_course.py

Lines changed: 194 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ def test_new_enrollment_at_the_end_of_leaderboard(self):
360360
self.assertEqual(actual_positions, expected_positions)
361361

362362
def test_not_enrolled_yet_but_leaderboard_displays(self):
363+
"""Test that the leaderboard displays even when user is not enrolled"""
363364
self.create_enrollment("e1", 100, 1)
364365
self.create_enrollment("e2", 90, 2)
365366
self.create_enrollment("e3", 80, 3)
@@ -377,16 +378,20 @@ def test_not_enrolled_yet_but_leaderboard_displays(self):
377378
response = self.client.get(url)
378379
self.assertEqual(response.status_code, 200)
379380

381+
# Current enrollment should be None (no auto-enrollment)
380382
current_enrollment = response.context[
381383
"current_student_enrollment"
382384
]
383-
self.assertEqual(current_enrollment.student.id, self.user.id)
384-
self.assertEqual(current_enrollment.total_score, 0)
385-
self.assertIsNone(current_enrollment.position_on_leaderboard)
385+
self.assertIsNone(current_enrollment)
386386

387+
# Leaderboard should still show other enrollments
387388
enrollments = response.context["enrollments"]
388-
last_enrollment = enrollments[len(enrollments) - 1]
389-
self.assertEqual(last_enrollment.id, current_enrollment.id)
389+
self.assertEqual(len(enrollments), 5)
390+
391+
# Verify the order is correct
392+
expected_order = ["e1", "e2", "e3", "e4", "e5"]
393+
actual_order = [e.display_name for e in enrollments]
394+
self.assertEqual(actual_order, expected_order)
390395

391396
def test_not_enrolled_but_can_edit_details(self):
392397
self.enrollment.delete()
@@ -832,3 +837,187 @@ def test_project_deadline_display_for_peer_review_state(self):
832837
peer_review_deadline_str = pr_project.peer_review_due_date.strftime('%Y-%m-%d')
833838
self.assertIn(peer_review_deadline_str, content)
834839

840+
def test_course_view_does_not_auto_enroll(self):
841+
"""Test that visiting the course page does not auto-enroll a user"""
842+
# Delete the existing enrollment
843+
self.enrollment.delete()
844+
845+
# Verify enrollment is deleted
846+
enrollment_count = Enrollment.objects.filter(
847+
student=self.user,
848+
course=self.course
849+
).count()
850+
self.assertEqual(enrollment_count, 0)
851+
852+
# Login and visit the course page
853+
self.client.login(**credentials)
854+
url = reverse("course", kwargs={"course_slug": self.course.slug})
855+
response = self.client.get(url)
856+
857+
self.assertEqual(response.status_code, 200)
858+
859+
# Verify NO enrollment was created
860+
enrollment_count = Enrollment.objects.filter(
861+
student=self.user,
862+
course=self.course
863+
).count()
864+
self.assertEqual(enrollment_count, 0,
865+
"Course view should not auto-enroll users")
866+
867+
def test_leaderboard_view_does_not_auto_enroll(self):
868+
"""Test that visiting the leaderboard page does not auto-enroll a user"""
869+
# Create some other users' enrollments
870+
self.create_enrollment("e1", 100, 1)
871+
self.create_enrollment("e2", 90, 2)
872+
873+
# Delete the existing enrollment
874+
self.enrollment.delete()
875+
876+
# Verify enrollment is deleted
877+
enrollment_count = Enrollment.objects.filter(
878+
student=self.user,
879+
course=self.course
880+
).count()
881+
self.assertEqual(enrollment_count, 0)
882+
883+
# Login and visit the leaderboard page
884+
self.client.login(**credentials)
885+
url = reverse("leaderboard", kwargs={"course_slug": self.course.slug})
886+
response = self.client.get(url)
887+
888+
self.assertEqual(response.status_code, 200)
889+
890+
# Verify NO enrollment was created
891+
enrollment_count = Enrollment.objects.filter(
892+
student=self.user,
893+
course=self.course
894+
).count()
895+
self.assertEqual(enrollment_count, 0,
896+
"Leaderboard view should not auto-enroll users")
897+
898+
# Verify the context shows None for current enrollment
899+
current_enrollment = response.context.get("current_student_enrollment")
900+
self.assertIsNone(current_enrollment,
901+
"Current student enrollment should be None when not enrolled")
902+
903+
def test_leaderboard_unauthenticated_user(self):
904+
"""Test leaderboard for unauthenticated users"""
905+
# Create some enrollments for the leaderboard
906+
self.create_enrollment("Alice", 100, 1)
907+
self.create_enrollment("Bob", 90, 2)
908+
self.create_enrollment("Charlie", 80, 3)
909+
910+
# Logout and visit leaderboard without authentication
911+
self.client.logout()
912+
913+
url = reverse("leaderboard", kwargs={"course_slug": self.course.slug})
914+
response = self.client.get(url)
915+
916+
self.assertEqual(response.status_code, 200)
917+
918+
# Context checks
919+
current_enrollment = response.context.get("current_student_enrollment")
920+
self.assertIsNone(current_enrollment)
921+
current_enrollment_id = response.context.get("current_student_enrollment_id")
922+
self.assertIsNone(current_enrollment_id)
923+
924+
enrollments = response.context["enrollments"]
925+
self.assertEqual(len(enrollments), 4) # Alice, Bob, Charlie, and self.enrollment
926+
927+
# HTML content checks - should NOT show "Your Record" section
928+
self.assertNotContains(response, "Your Record")
929+
self.assertNotContains(response, "Your total score")
930+
self.assertNotContains(response, "Display name")
931+
self.assertNotContains(response, "Jump to your record")
932+
933+
# Should show the leaderboard with other students
934+
self.assertContains(response, "Alice")
935+
self.assertContains(response, "Bob")
936+
self.assertContains(response, "Charlie")
937+
938+
def test_leaderboard_authenticated_without_enrollment(self):
939+
"""Test leaderboard for authenticated users who are not enrolled"""
940+
# Create some enrollments for the leaderboard
941+
self.create_enrollment("Alice", 100, 1)
942+
self.create_enrollment("Bob", 90, 2)
943+
self.create_enrollment("Charlie", 80, 3)
944+
945+
# Delete the test user's enrollment
946+
self.enrollment.delete()
947+
948+
# Login and visit leaderboard
949+
self.client.login(**credentials)
950+
951+
url = reverse("leaderboard", kwargs={"course_slug": self.course.slug})
952+
response = self.client.get(url)
953+
954+
self.assertEqual(response.status_code, 200)
955+
956+
# Context checks
957+
current_enrollment = response.context.get("current_student_enrollment")
958+
self.assertIsNone(current_enrollment)
959+
current_enrollment_id = response.context.get("current_student_enrollment_id")
960+
self.assertIsNone(current_enrollment_id)
961+
962+
enrollments = response.context["enrollments"]
963+
self.assertEqual(len(enrollments), 3) # Only Alice, Bob, Charlie
964+
965+
# HTML content checks - should NOT show "Your Record" section
966+
self.assertNotContains(response, "Your Record")
967+
self.assertNotContains(response, "Your total score")
968+
self.assertNotContains(response, "Display name")
969+
self.assertNotContains(response, "Jump to your record")
970+
971+
# Should show the leaderboard with other students
972+
self.assertContains(response, "Alice")
973+
self.assertContains(response, "Bob")
974+
self.assertContains(response, "Charlie")
975+
976+
def test_leaderboard_authenticated_with_enrollment(self):
977+
"""Test leaderboard for authenticated users who are enrolled"""
978+
# Create some other enrollments
979+
e1 = self.create_enrollment("Alice", 100, 1)
980+
e2 = self.create_enrollment("Bob", 90, 2)
981+
e3 = self.create_enrollment("Charlie", 80, 3)
982+
983+
# Set up test user's enrollment
984+
self.enrollment.display_name = "TestUser"
985+
self.enrollment.total_score = 95
986+
self.enrollment.position_on_leaderboard = 2
987+
self.enrollment.save()
988+
989+
# Login and visit leaderboard
990+
self.client.login(**credentials)
991+
992+
url = reverse("leaderboard", kwargs={"course_slug": self.course.slug})
993+
response = self.client.get(url)
994+
995+
self.assertEqual(response.status_code, 200)
996+
997+
# Context checks
998+
current_enrollment = response.context.get("current_student_enrollment")
999+
self.assertIsNotNone(current_enrollment)
1000+
self.assertEqual(current_enrollment.id, self.enrollment.id)
1001+
self.assertEqual(current_enrollment.display_name, "TestUser")
1002+
self.assertEqual(current_enrollment.total_score, 95)
1003+
1004+
current_enrollment_id = response.context.get("current_student_enrollment_id")
1005+
self.assertEqual(current_enrollment_id, self.enrollment.id)
1006+
1007+
enrollments = response.context["enrollments"]
1008+
self.assertEqual(len(enrollments), 4) # Alice, TestUser, Bob, Charlie
1009+
1010+
# HTML content checks - should show "Your Record" section
1011+
self.assertContains(response, "Your Record")
1012+
self.assertContains(response, "Your total score: 95")
1013+
self.assertContains(response, "Position: 2")
1014+
self.assertContains(response, "Display name: TestUser")
1015+
self.assertContains(response, "Jump to your record")
1016+
self.assertContains(response, f"record-{self.enrollment.id}")
1017+
1018+
# Should show the leaderboard with all students
1019+
self.assertContains(response, "Alice")
1020+
self.assertContains(response, "Bob")
1021+
self.assertContains(response, "Charlie")
1022+
self.assertContains(response, "TestUser")
1023+

courses/views/course.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,14 @@ def leaderboard_view(request, course_slug: str):
231231
current_student_enrollment_id = None
232232

233233
if user.is_authenticated:
234-
current_student_enrollment, _ = (
235-
Enrollment.objects.get_or_create(
234+
try:
235+
current_student_enrollment = Enrollment.objects.get(
236236
student=user,
237237
course=course,
238238
)
239-
)
240-
current_student_enrollment_id = current_student_enrollment.id
239+
current_student_enrollment_id = current_student_enrollment.id
240+
except Enrollment.DoesNotExist:
241+
pass
241242

242243
enrollments = Enrollment.objects.filter(course=course).order_by(
243244
Coalesce("position_on_leaderboard", Value(999999)),

0 commit comments

Comments
 (0)