235 lines
7.7 KiB
Python
235 lines
7.7 KiB
Python
import json
|
|
import pytest
|
|
from django.urls import reverse
|
|
from django.contrib.auth.models import User
|
|
from django.test import Client
|
|
from django.utils import timezone
|
|
from anatomy.models import Exam as AnatomyExam, AnatomyQuestion, QuestionType, Structure
|
|
from generic.models import CidUser, CidUserGroup
|
|
|
|
@pytest.fixture
|
|
def logged_in_user(db):
|
|
user = User.objects.create_user(username="user1", password="password", email="user1@test.com")
|
|
return user
|
|
|
|
@pytest.fixture
|
|
def logged_in_client(logged_in_user):
|
|
client = Client()
|
|
client.force_login(logged_in_user)
|
|
return client
|
|
|
|
@pytest.fixture
|
|
def anonymous_client(db):
|
|
return Client()
|
|
|
|
@pytest.fixture
|
|
def exam_data(db):
|
|
exam = AnatomyExam.objects.create(
|
|
name="Anatomy Exam Test",
|
|
exam_mode=True,
|
|
active=True,
|
|
open_access=False,
|
|
exam_open_access=True, # allows open access so we don't have to populate cid/user lists for candidate test
|
|
)
|
|
qtype = QuestionType.objects.create(id=1, text="Test Type")
|
|
structure = Structure.objects.create(name="Test Structure")
|
|
question = AnatomyQuestion.objects.create(
|
|
question_type=qtype,
|
|
structure=structure,
|
|
image="test.jpg",
|
|
description="Test Question description",
|
|
)
|
|
question.exams.set([exam])
|
|
return exam, question
|
|
|
|
@pytest.mark.django_db
|
|
def test_authenticated_user_submit_success(logged_in_client, logged_in_user, exam_data):
|
|
exam, question = exam_data
|
|
payload = {
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk}",
|
|
"start_time": timezone.now().timestamp(),
|
|
"answers": json.dumps([
|
|
{
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk}",
|
|
"qid": question.pk,
|
|
"ans": "Normal",
|
|
"qidn": "1",
|
|
}
|
|
])
|
|
}
|
|
response = logged_in_client.post(reverse("global_exam_answers_submit"), payload)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["success"] is True
|
|
assert data["question_count"] == 1
|
|
|
|
@pytest.mark.django_db
|
|
def test_authenticated_user_submit_mismatch_other_user(logged_in_client, logged_in_user, exam_data):
|
|
exam, question = exam_data
|
|
payload = {
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk + 999}", # root cid doesn't match authenticated user
|
|
"start_time": timezone.now().timestamp(),
|
|
"answers": json.dumps([
|
|
{
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk}",
|
|
"qid": question.pk,
|
|
"ans": "Normal",
|
|
"qidn": "1",
|
|
}
|
|
])
|
|
}
|
|
response = logged_in_client.post(reverse("global_exam_answers_submit"), payload)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["success"] is False
|
|
assert "user / cid mismatch" in data["error"]
|
|
|
|
@pytest.mark.django_db
|
|
def test_authenticated_user_submit_mismatch_answer_level(logged_in_client, logged_in_user, exam_data):
|
|
exam, question = exam_data
|
|
payload = {
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk}",
|
|
"start_time": timezone.now().timestamp(),
|
|
"answers": json.dumps([
|
|
{
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk + 999}", # answer level cid doesn't match authenticated user
|
|
"qid": question.pk,
|
|
"ans": "Normal",
|
|
"qidn": "1",
|
|
}
|
|
])
|
|
}
|
|
response = logged_in_client.post(reverse("global_exam_answers_submit"), payload)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["success"] is False
|
|
assert "user / uid mismatch" in data["error"]
|
|
|
|
@pytest.mark.django_db
|
|
def test_anonymous_user_submit_user_prefix_fails(anonymous_client, exam_data):
|
|
exam, question = exam_data
|
|
payload = {
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": "u-1", # prefix u- is forbidden for anonymous users
|
|
"start_time": timezone.now().timestamp(),
|
|
"answers": json.dumps([
|
|
{
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": "u-1",
|
|
"qid": question.pk,
|
|
"ans": "Normal",
|
|
"qidn": "1",
|
|
}
|
|
])
|
|
}
|
|
response = anonymous_client.post(reverse("global_exam_answers_submit"), payload)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["success"] is False
|
|
assert "authentication required" in data["error"]
|
|
|
|
@pytest.mark.django_db
|
|
def test_candidate_submit_success(anonymous_client, exam_data):
|
|
exam, question = exam_data
|
|
cid_user = CidUser.objects.create(
|
|
cid=8888,
|
|
name="Candidate 1",
|
|
email="candidate1@test.com",
|
|
passcode="candidatepass",
|
|
)
|
|
# Associate CID user with the exam
|
|
exam.exam_open_access = False
|
|
group = CidUserGroup.objects.create(name="Test Group")
|
|
group.ciduser_set.add(cid_user)
|
|
exam.cid_user_groups.add(group)
|
|
exam.valid_cid_users.add(cid_user)
|
|
|
|
payload = {
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": "8888",
|
|
"start_time": timezone.now().timestamp(),
|
|
"answers": json.dumps([
|
|
{
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": "8888",
|
|
"passcode": "candidatepass",
|
|
"qid": question.pk,
|
|
"ans": "Normal",
|
|
"qidn": "1",
|
|
}
|
|
])
|
|
}
|
|
response = anonymous_client.post(reverse("global_exam_answers_submit"), payload)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["success"] is True
|
|
assert data["question_count"] == 1
|
|
|
|
|
|
@pytest.fixture
|
|
def cid_manager_client(db):
|
|
from django.contrib.auth.models import Group
|
|
user = User.objects.create_user(username="manager1", password="password", email="manager1@test.com")
|
|
g, _ = Group.objects.get_or_create(name="cid_user_manager")
|
|
user.groups.add(g)
|
|
client = Client()
|
|
client.force_login(user)
|
|
return client
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_import_exam_answers_unauthorized(anonymous_client):
|
|
# Anonymous or non-manager users should be redirected or blocked
|
|
url = reverse("generic:import_exam_answers")
|
|
response = anonymous_client.get(url)
|
|
assert response.status_code == 403
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_import_exam_answers_get(cid_manager_client):
|
|
url = reverse("generic:import_exam_answers")
|
|
response = cid_manager_client.get(url)
|
|
assert response.status_code == 200
|
|
assert b"Import Exam Answers" in response.content
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_import_exam_answers_post_success(cid_manager_client, exam_data, logged_in_user):
|
|
exam, question = exam_data
|
|
from io import BytesIO
|
|
from anatomy.models import UserAnswer as AnatomyUserAnswer
|
|
|
|
import_data = {
|
|
"eid": f"anatomy/{exam.pk}",
|
|
"cid": f"u-{logged_in_user.pk}",
|
|
"start_time": timezone.now().timestamp(),
|
|
"answers": [
|
|
{
|
|
"qid": question.pk,
|
|
"ans": "Imported Test Answer",
|
|
"qidn": "1",
|
|
}
|
|
]
|
|
}
|
|
|
|
file_content = json.dumps(import_data).encode("utf-8")
|
|
answers_file = BytesIO(file_content)
|
|
answers_file.name = "backup.json"
|
|
|
|
url = reverse("generic:import_exam_answers")
|
|
response = cid_manager_client.post(url, {"answers_file": answers_file}, format="multipart")
|
|
|
|
assert response.status_code == 302 # redirects to manage_cids on success
|
|
|
|
# Verify answers were saved
|
|
answers = AnatomyUserAnswer.objects.filter(exam=exam, user=logged_in_user)
|
|
assert answers.count() == 1
|
|
assert answers.first().answer == "Imported Test Answer"
|
|
|