from django.urls import reverse from django.core import mail from rich.pretty import pprint from bs4 import BeautifulSoup from django.contrib.auth.models import Group import pytest @pytest.fixture def create_users(db, django_user_model): user1 = django_user_model.objects.create_user( "user1", "user1@user.com", "password" ) user2 = django_user_model.objects.create_user( "user2", "user2@user.com", "password" ) #g = Group.objects.create(name="cid_user_manager") #g.save() #user.groups.add(g) return user1, user2 @pytest.fixture def create_cid_manager(db, django_user_model): user = django_user_model.objects.create_user( "cid_user", "cid@user.com", "password1234" ) g = Group.objects.create(name="cid_user_manager") g.save() user.groups.add(g) return user def test_profile_access_and_management(db, client, create_users, create_cid_manager): response = client.get(reverse(f"profile")) # should redirect to login page assert response.status_code == 302 assert "accounts/login/" in response.url # Test login client.login(username="user1", password="password") response = client.get(reverse(f"profile")) assert response.status_code == 200 soup = BeautifulSoup(response.content, "html.parser") assert soup.find(id="username").text == "user1" assert soup.find(id="email").text == "user1@user.com" # Check the links to change password and update profile password_url = soup.find(id="password-change")["href"] print(password_url) password_change_response = client.get(password_url) assert password_change_response.status_code == 200 password_soup = BeautifulSoup(password_change_response.content, "html.parser") assert "Old password" in password_soup.text assert "New password" in password_soup.text update_profile = soup.find(id="update-profile-link")["href"] assert update_profile == reverse("account_profile_update", kwargs={"slug": "user1"}) # check we can't access another users profile response_fail = client.get(reverse("account_profile_update", kwargs={"slug": "user2"})) assert response_fail.status_code == 404 print(response_fail.content) # And that we can access our own update_profile_response = client.get(reverse("account_profile_update", kwargs={"slug": "user1"})) assert update_profile_response.status_code == 200 assert "peninsula_trainee" not in BeautifulSoup(update_profile_response.content, "html.parser").text update_response = client.post(reverse("account_profile_update", kwargs={"slug": "user1"}), { "registration_number": 1234567 }) assert update_response.status_code == 302 # we reirect back to profile page on form submission assert update_response.url == reverse("profile") # check that the form has updated update_profile_response = client.get(reverse("account_profile_update", kwargs={"slug": "user1"})) assert BeautifulSoup(update_profile_response.content, "html.parser").find(id="id_registration_number")["value"] @pytest.mark.django_db def test_create_user_reuses_existing_account(client, create_cid_manager, django_user_model): existing_user = django_user_model.objects.create_user( username="existing@example.com", email="existing@example.com", password="password123", first_name="Old", last_name="Name", ) client.force_login(create_cid_manager) response = client.post( reverse("create_user"), { "username": "existing@example.com", "first_name": "Updated", "last_name": "User", "grade": "", }, ) assert response.status_code == 302 assert response.url == reverse("accounts_list") existing_user.refresh_from_db() assert existing_user.first_name == "Updated" assert existing_user.last_name == "User" @pytest.mark.django_db def test_cid_manager_can_send_password_reset_from_account_profile(client, create_cid_manager, django_user_model): managed_user = django_user_model.objects.create_user( username="managed-user", email="managed@example.com", password="password123", ) client.force_login(create_cid_manager) response = client.post( reverse("account_send_password_reset", kwargs={"slug": managed_user.username}), {"next": reverse("account_profile", kwargs={"slug": managed_user.username})}, ) assert response.status_code == 302 assert "password_reset_action=sent" in response.url assert len(mail.outbox) == 1 assert mail.outbox[0].to == ["managed@example.com"] @pytest.mark.django_db def test_people_page_and_user_search(client, create_users, create_cid_manager): # Log in as cid_user_manager client.force_login(create_cid_manager) # 1. Test standard GET request to /people page response = client.get(reverse("people")) assert response.status_code == 200 assert b"Quick User Search" in response.content assert b"hx-indicator=\"#search-indicator\"" in response.content # 2. Test HTMX GET request with empty query response_empty = client.get( reverse("people"), {"q": ""}, HTTP_HX_REQUEST="true", ) assert response_empty.status_code == 200 assert response_empty.content == b"" # 3. Test HTMX GET request with matching query user1, user2 = create_users response_search = client.get( reverse("people"), {"q": "user1"}, HTTP_HX_REQUEST="true", ) assert response_search.status_code == 200 assert b"user1" in response_search.content assert b"user1@user.com" in response_search.content assert b"Actions" in response_search.content # 4. Test HTMX GET request with no matching users response_no_match = client.get( reverse("people"), {"q": "nonexistent_username_xyz"}, HTTP_HX_REQUEST="true", ) assert response_no_match.status_code == 200 assert b"No users found matching" in response_no_match.content @pytest.mark.django_db def test_hide_exams_and_collections(client, create_users): from django.contrib.contenttypes.models import ContentType from physics.models import Exam as PhysicsExam from atlas.models import CaseCollection from generic.models import UserHiddenItem user1, user2 = create_users # 1. Create a PhysicsExam and associate with user1 exam = PhysicsExam.objects.create( name="Test Physics Exam", exam_mode=True, active=True ) user1.user_physics_exams.add(exam) # 2. Create a CaseCollection and associate with user1 collection = CaseCollection.objects.create( name="Test Case Collection", exam_mode=True, active=True ) user1.user_casecollection_exams.add(collection) # Log in as user1 client.force_login(user1) # Verify they are visible on user scores page initially response = client.get(reverse("user_scores")) assert response.status_code == 200 assert b"Test Physics Exam" in response.content # Load atlas collections partial response_cc = client.get(reverse("atlas:user_collections")) assert response_cc.status_code == 200 assert b"Test Case Collection" in response_cc.content # 3. Hide the PhysicsExam exam_ct = ContentType.objects.get_for_model(PhysicsExam) response_hide_exam = client.post( reverse("generic:toggle_hide_item"), {"content_type_id": exam_ct.id, "object_id": exam.id}, ) assert response_hide_exam.status_code == 200 assert response_hide_exam.headers.get("HX-Refresh") == "true" # Verify UserHiddenItem exists assert UserHiddenItem.objects.filter( user=user1, content_type=exam_ct, object_id=exam.id ).exists() # Verify exam is hidden now from standard user_scores response_hidden = client.get(reverse("user_scores")) assert response_hidden.status_code == 200 assert b"Test Physics Exam" not in response_hidden.content # But present when show_hidden=true is requested response_show_hidden = client.get(reverse("user_scores") + "?show_hidden=true") assert response_show_hidden.status_code == 200 assert b"Test Physics Exam" in response_show_hidden.content assert b"Hidden" in response_show_hidden.content # 4. Hide the CaseCollection cc_ct = ContentType.objects.get_for_model(CaseCollection) response_hide_cc = client.post( reverse("generic:toggle_hide_item"), {"content_type_id": cc_ct.id, "object_id": collection.pk}, ) assert response_hide_cc.status_code == 200 assert response_hide_cc.headers.get("HX-Refresh") == "true" assert UserHiddenItem.objects.filter( user=user1, content_type=cc_ct, object_id=collection.pk ).exists() # Verify collection is hidden from normal view response_cc_hidden = client.get(reverse("atlas:user_collections")) assert response_cc_hidden.status_code == 200 assert b"Test Case Collection" not in response_cc_hidden.content # Verify collection is visible in show_hidden view response_cc_show_hidden = client.get(reverse("atlas:user_collections") + "?show_hidden=true") assert response_cc_show_hidden.status_code == 200 assert b"Test Case Collection" in response_cc_show_hidden.content assert b"Hidden" in response_cc_show_hidden.content # 5. Unhide the PhysicsExam by toggling again response_unhide_exam = client.post( reverse("generic:toggle_hide_item"), {"content_type_id": exam_ct.id, "object_id": exam.id}, ) assert response_unhide_exam.status_code == 200 assert not UserHiddenItem.objects.filter( user=user1, content_type=exam_ct, object_id=exam.id ).exists() response_unhidden = client.get(reverse("user_scores")) assert response_unhidden.status_code == 200 assert b"Test Physics Exam" in response_unhidden.content @pytest.mark.django_db def test_create_user_with_linked_supervisor(client, create_cid_manager, django_user_model): from generic.models import Supervisor client.force_login(create_cid_manager) # 1. Create a user WITH create_supervisor checked response = client.post( reverse("create_user"), { "username": "newsupervisor@example.com", "first_name": "New", "last_name": "Supervisor", "grade": "", "create_supervisor": True, }, ) assert response.status_code == 302 assert response.url == reverse("accounts_list") # Verify user created new_user = django_user_model.objects.get(username="newsupervisor@example.com") assert new_user.first_name == "New" assert new_user.last_name == "Supervisor" # Verify linked supervisor created supervisor = Supervisor.objects.get(email="newsupervisor@example.com") assert supervisor.name == "New Supervisor" assert supervisor.user == new_user # 2. Create a user WITHOUT create_supervisor checked response_no_sv = client.post( reverse("create_user"), { "username": "regularuser@example.com", "first_name": "Regular", "last_name": "User", "grade": "", "create_supervisor": False, }, ) assert response_no_sv.status_code == 302 # Verify user created regular_user = django_user_model.objects.get(username="regularuser@example.com") # Verify no supervisor was created assert not Supervisor.objects.filter(email="regularuser@example.com").exists()