import json import pytest from django.urls import reverse from rich.pretty import pprint from bs4 import BeautifulSoup from django.contrib.auth.models import Group from rapids.views import GenericExamViews as RapidsExamViews from rapids.views import GenericExamViews as LongsExamViews from sbas.views import GenericExamViews as SbaExamViews from longs.views import GenericExamViews as LongsExamViews from physics.views import GenericExamViews as PhysicExamViews from anatomy.views import GenericExamViews as AnatomyExamViews from generic.models import Supervisor from loguru import logger APP_NAMES = ("rapids", "anatomy", "longs", "physics", "sbas") JSON_APPS = ("rapids", "anatomy", "longs") EXAM_VIEWS = ( RapidsExamViews, LongsExamViews, SbaExamViews, PhysicExamViews, AnatomyExamViews, ) JSON_VIEWS = ( RapidsExamViews, LongsExamViews, AnatomyExamViews, ) @pytest.fixture def create_superuser(db, django_user_model): return django_user_model.objects.create_superuser( "admin", "ross@xkjq.uk", "adminpassword" ) @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 @pytest.fixture def create_basic_user(db, django_user_model): user = django_user_model.objects.create_user( "basicuser", "basic@user.com", "password1234" ) return user @pytest.fixture(params=EXAM_VIEWS) def set_up_exams(request, django_user_model, create_superuser): basic_user = django_user_model.objects.create_user( "basicuser", "user@user.net", "pass2" ) e1 = request.param.Exam.objects.create(name="test exam", exam_mode=True) e = request.param.Exam.objects.create(name="test exam basic user", exam_mode=True) e.author.add(basic_user) return request.param # We only test JSON functionality with this so limit the views @pytest.fixture(params=JSON_VIEWS) def set_up_packets(request): e = request.param.Exam.objects.create(name="test packet", exam_mode=False, active=True) return request.param # @pytest.fixture # def create_exam(db): # return Exam.objects.create(name="test exam", exam_mode=True) # Very basic tests, make sure we can hit key urls without error def test_index2(client, set_up_exams): client.login(username="admin", password="adminpassword") response = client.get(reverse(f"{set_up_exams.app_name}:index")) assert response.status_code == 200 assert ( BeautifulSoup(response.content, "html.parser") .find("h4", {"class": "exam-number-title"}) .getText() == "2 exams found." ) def test_packet_json_access(db, client, set_up_packets): """ Simple test to check that json is retrievable from non exam mode packets """ if set_up_packets.app_name in JSON_APPS: exam_id = set_up_packets.Exam.objects.get(name="test packet").pk response = client.get(reverse(f"{set_up_packets.app_name}:exam_json", args=[exam_id])) assert response.status_code == 200 exam_json = json.loads(response.content) # Remove the s at the end of the app name # TODO: unify this.... #assert exam_json["eid"] == f"{set_up_packets.app_name/{exam_id}" assert exam_json["eid"].split("/")[-1] == f"{exam_id}" print(exam_json) assert exam_json["exam_mode"] == False assert exam_json["exam_name"] == "test packet" assert len(exam_json["questions"]) == 0 def test_overview(client, set_up_exams): client.login(username="admin", password="adminpassword") e1 = set_up_exams.Exam.objects.get(name="test exam").pk e2 = set_up_exams.Exam.objects.get(name="test exam basic user").pk response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e1])) assert response.status_code == 200 response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e2])) assert response.status_code == 200 client.login(username="basicuser", password="pass2") response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e1])) assert response.status_code == 403 response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e2])) assert response.status_code == 200 @pytest.mark.parametrize("exam_views", EXAM_VIEWS) def test_index(client, create_superuser, exam_views, django_user_model): basic_user = django_user_model.objects.create_user( "basicuser", "user@user.net", "pass2" ) e1 = exam_views.Exam.objects.create(name="test exam", exam_mode=True) e = exam_views.Exam.objects.create(name="test exam 2", exam_mode=True) e.author.add(basic_user) client.login(username="admin", password="adminpassword") response = client.get(reverse(f"{exam_views.app_name}:index")) assert response.status_code == 200 assert ( BeautifulSoup(response.content, "html.parser") .find("h4", {"class": "exam-number-title"}) .getText() == "2 exams found." ) response = client.get(reverse(f"{exam_views.app_name}:exam_list")) assert response.status_code == 200 assert ( len(BeautifulSoup(response.content, "html.parser") .select("a.exam-name")) == 2 ) if exam_views.app_name in JSON_APPS: response = client.get(reverse(f"{exam_views.app_name}:active_exams")) json_response = json.loads(response.content)["exams"] assert len(json_response) == 2 for json_exam in json_response: eid_app_type, eid = json_exam["eid"].split("/") # hack for long / longs mismathc assert ( eid_app_type == exam_views.app_name or eid_app_type == exam_views.app_name[:-1] ) assert exam_views.Exam.objects.get(pk=int(eid)).name == json_exam["name"] add_user_response = client.post( reverse(f"{exam_views.app_name}:exam_json_edit", args=(e1.pk,)), {"edit_user_user": basic_user.pk, "add": "true"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assert add_user_response.status_code == 200 assert add_user_response.headers["Content-Type"].startswith("text/html") assert add_user_response.headers["HX-Trigger"] == json.dumps( {"user_toggled": {"pk": basic_user.pk, "added": True}} ) assert e1.valid_user_users.filter(pk=basic_user.pk).exists() assert f'data-pk="{basic_user.pk}"' in add_user_response.content.decode() client.logout() client.force_login(basic_user) response = client.get(reverse(f"{exam_views.app_name}:index")) assert response.status_code == 200 assert ( BeautifulSoup(response.content, "html.parser") .find("h4", {"class": "exam-number-title"}) .getText() == "1 exams found." ) response = client.get(reverse(f"{exam_views.app_name}:exam_list")) assert ( len(BeautifulSoup(response.content, "html.parser") .select("a.exam-name")) == 1 ) if exam_views.app_name in JSON_APPS: response = client.get(reverse(f"{exam_views.app_name}:active_exams")) json_response = json.loads(response.content)["exams"] assert len(json_response) == 1 e1.active = True e1.save() response = client.get(reverse(f"{exam_views.app_name}:active_exams")) json_response = json.loads(response.content)["exams"] assert len(json_response) == 2 def test_cid_management(client, create_cid_manager, django_user_model, create_basic_user): urls = [ reverse(f"people"), #reverse(f"generic:update_cid"), #reverse(f"generic:update_cid_exams"), reverse(f"generic:manage_cids"), reverse(f"generic:manage_cid_exams"), reverse(f"generic:cid_group_view"), reverse(f"generic:cid_group_view_all"), reverse(f"generic:cid_group_create"), reverse(f"generic:supervisor"), ] for url in urls: response = client.get(url) # Some urls will try to redirect to a login page if response.status_code == 302: assert "accounts/login/" in response.url else: # Others just forbid assert response.status_code == 403 pass basic_user = create_basic_user client.force_login(basic_user) for url in urls: # When logged in there should be no redirection (if access denied) response = client.get(url) assert response.status_code == 403 cid_manager = create_cid_manager client.force_login(cid_manager) for url in urls: response = client.get(url) assert response.status_code == 200 pass def create_supervisor(db, faker): return Supervisor.objects.create(email=faker.email(), name=faker.name(), active=True) def test_supervisor_management(db, faker, client, create_cid_manager, django_user_model, create_basic_user ): s1 = create_supervisor(db, faker) s2 = create_supervisor(db, faker) print(s1) print(s2) basic_user = create_basic_user client.force_login(basic_user) response = client.get(reverse(f"generic:supervisor_detail", kwargs={"pk": s1.pk})) assert response.status_code == 403 response = client.get(reverse(f"generic:supervisor")) assert response.status_code == 403 # Check we can not delete response = client.post(reverse(f"generic:supervisor_delete", kwargs={"pk": s2.pk})) assert response.status_code == 403 cid_manager = create_cid_manager client.force_login(cid_manager) response = client.get(reverse(f"generic:supervisor")) assert response.status_code == 200 soup = BeautifulSoup(response.content, "html.parser") assert Supervisor.objects.count() == 2 response = client.get(reverse(f"generic:supervisor_detail", kwargs={"pk": s1.pk})) soup = BeautifulSoup(response.content, "html.parser") assert soup.find(id="name").text == s1.name assert soup.find(id="email").text == s1.email new_email = "new@email.com" response = client.post(reverse(f"generic:supervisor_edit", kwargs={"pk": s1.pk}), {"email":new_email, "name": s1.name, "active": s1.active}) soup = BeautifulSoup(response.content, "html.parser") assert response.status_code == 302 response = client.get(reverse(f"generic:supervisor_detail", kwargs={"pk": s1.pk})) soup = BeautifulSoup(response.content, "html.parser") assert soup.find(id="name").text == s1.name assert soup.find(id="email").text == new_email # Check we can delete response = client.post(reverse(f"generic:supervisor_delete", kwargs={"pk": s2.pk})) assert response.status_code == 302 response = client.get(reverse(f"generic:supervisor")) assert response.status_code == 200 soup = BeautifulSoup(response.content, "html.parser") assert Supervisor.objects.filter(pk=s2.pk).exists() is False assert Supervisor.objects.filter(pk=s1.pk).exists() is True #assert len(soup.find(id="supervisor-list").find_all("li")) == 1 response = client.post(reverse(f"generic:supervisor_create"), {"email":faker.email(), "name": faker.name(), "active": True}) assert response.status_code == 302 response = client.get(reverse(f"generic:supervisor")) assert response.status_code == 200 soup = BeautifulSoup(response.content, "html.parser") rows = soup.select('#supervisor-table > tbody > tr') assert len(rows) == 2 assert Supervisor.objects.count() == 2 def test_privacy(db, client): response = client.get(reverse(f"privacy")) assert response.status_code == 200 soup = BeautifulSoup(response.content, "html.parser") assert "Privacy Policy" in soup.find("h1").text def test_cookies(db, client): response = client.get(reverse(f"cookie_consent_cookie_group_list")) assert response.status_code == 200 soup = BeautifulSoup(response.content, "html.parser") assert "Cookies" in soup.find("h1").text # Check our 2 cookies are displayed (we would have to add them for this to work) #assert "csrftoken" in [i.text for i in soup.find_all("th")] #assert "sessionid" in [i.text for i in soup.find_all("th")] def test_rapids_mark2_views(db, client, create_superuser): client.force_login(create_superuser) from rapids.models import Exam, Rapid exam = Exam.objects.create(name="Test Exam", exam_mode=True) rapid = Rapid.objects.create(laterality=Rapid.NONE, normal=True) exam.exam_questions.add(rapid) # 1. Test mark2_overview url_overview = reverse("rapids:mark2_overview", kwargs={"pk": exam.pk}) resp = client.get(url_overview) assert resp.status_code == 200 # 2. Test mark2 question view url_mark2 = reverse("rapids:mark2", kwargs={"exam_pk": exam.pk, "sk": 0}) resp = client.get(url_mark2) assert resp.status_code == 200 # 3. Test mark2_exam_marked partial url_exam_marked = reverse("rapids:mark2_exam_marked", kwargs={"exam_pk": exam.pk, "sk": 0}) resp = client.get(url_exam_marked) assert resp.status_code == 200 # 4. Test mark2_question_marked partial url_question_marked = reverse("rapids:mark2_question_marked", kwargs={"pk": rapid.pk}) resp = client.get(url_question_marked) assert resp.status_code == 200