import pytest from django.urls import reverse from django.contrib.contenttypes.models import ContentType from survey.models import Survey, SurveyQuestion, SurveyResponse, SurveyAnswer from survey.views import check_survey_interception from atlas.models import CaseCollection @pytest.mark.django_db def test_survey_creation(): survey = Survey.objects.create(name="Test Course Survey", description="Feedback on course") assert survey.name == "Test Course Survey" assert survey.active is True assert survey.anonymous is False @pytest.mark.django_db def test_survey_questions(): survey = Survey.objects.create(name="Test Survey") q1 = SurveyQuestion.objects.create( survey=survey, text="Rate this case", question_type=SurveyQuestion.QuestionType.RATING, position=10 ) q2 = SurveyQuestion.objects.create( survey=survey, text="Any comments?", question_type=SurveyQuestion.QuestionType.TEXT, position=20 ) assert survey.questions.count() == 2 assert q1.get_choices_list() == [] @pytest.mark.django_db def test_survey_response_and_answers(admin_user): survey = Survey.objects.create(name="Test Survey") q1 = SurveyQuestion.objects.create( survey=survey, text="Rate this case", question_type=SurveyQuestion.QuestionType.RATING ) # Simulate a survey response save response = SurveyResponse.objects.create( survey=survey, user=admin_user, pre_or_post="PRE" ) answer = SurveyAnswer.objects.create( response=response, question=q1, rating_answer=4 ) assert response.answers.count() == 1 assert answer.rating_answer == 4 @pytest.mark.django_db def test_interception_helper(rf, admin_user): survey = Survey.objects.create(name="Pre-take survey") collection = CaseCollection.objects.create(name="Test Collection", pre_survey=survey) request = rf.get('/atlas/collection/1/take/') request.user = admin_user # 1. First check: should redirect to pre-survey since they haven't filled it out redirect_response = check_survey_interception(request, collection) assert redirect_response is not None assert "take/" in redirect_response.url # 2. Add response and check again: should not redirect ct = ContentType.objects.get_for_model(collection) resp = SurveyResponse.objects.create( survey=survey, user=admin_user, content_type=ct, object_id=collection.pk, pre_or_post="PRE" ) no_redirect_response = check_survey_interception(request, collection) assert no_redirect_response is None @pytest.mark.django_db def test_survey_authorship(admin_user): survey = Survey.objects.create(name="Author Test Survey") survey.add_author(admin_user) assert survey.is_author(admin_user) is True assert admin_user in survey.get_author_objects() assert survey.get_authors() == admin_user.username @pytest.mark.django_db def test_can_manage_survey_permission(admin_user, django_user_model): from survey.views import can_manage_survey from django.contrib.auth.models import Group survey = Survey.objects.create(name="Permissions Test Survey", authors_only=True) # 1. Non-staff user should not have permission regular_user = django_user_model.objects.create_user(username="trainee", password="pw") assert can_manage_survey(regular_user, survey) is False # 2. Staff user in cid_user_manager but not author should not have permission for private survey manager_group, _ = Group.objects.get_or_create(name="cid_user_manager") manager_user = django_user_model.objects.create_user(username="manager", password="pw") manager_user.groups.add(manager_group) assert can_manage_survey(manager_user, survey) is False # 3. Superuser should always have permission superuser = django_user_model.objects.create_superuser(username="admin2", password="pw") assert can_manage_survey(superuser, survey) is True # 4. Author should have permission even if private survey.add_author(manager_user) assert can_manage_survey(manager_user, survey) is True # 5. Non-author manager should have permission if survey is public survey.authors_only = False survey.save() other_manager = django_user_model.objects.create_user(username="other_manager", password="pw") other_manager.groups.add(manager_group) assert can_manage_survey(other_manager, survey) is True @pytest.mark.django_db def test_integer_and_date_validation(): from survey.forms import SurveyTakeForm survey = Survey.objects.create(name="Validation Test Survey") # 1. Integer question with min=10, max=50 q_int = SurveyQuestion.objects.create( survey=survey, text="Enter age", question_type=SurveyQuestion.QuestionType.INTEGER, validation_rules={"min_value": 10, "max_value": 50} ) # 2. Date question with min="2026-01-01", max="2026-12-31" q_date = SurveyQuestion.objects.create( survey=survey, text="Select target date", question_type=SurveyQuestion.QuestionType.DATE, validation_rules={"min_date": "2026-01-01", "max_date": "2026-12-31"} ) # Valid submission form_valid = SurveyTakeForm( data={ f"question_{q_int.pk}": 25, f"question_{q_date.pk}": "2026-06-15" }, survey=survey ) assert form_valid.is_valid() is True # Out of bounds Integer submission form_invalid_int = SurveyTakeForm( data={ f"question_{q_int.pk}": 5, f"question_{q_date.pk}": "2026-06-15" }, survey=survey ) assert form_invalid_int.is_valid() is False assert f"question_{q_int.pk}" in form_invalid_int.errors # Out of bounds Date submission form_invalid_date = SurveyTakeForm( data={ f"question_{q_int.pk}": 25, f"question_{q_date.pk}": "2027-01-01" }, survey=survey ) assert form_invalid_date.is_valid() is False assert f"question_{q_date.pk}" in form_invalid_date.errors @pytest.mark.django_db def test_survey_skip(rf, admin_user): survey = Survey.objects.create(name="Optional pre-take survey") collection = CaseCollection.objects.create(name="Test Collection", pre_survey=survey, pre_survey_mandatory=False) request = rf.get('/atlas/collection/1/take/') request.user = admin_user # Initialize session middleware mock from django.contrib.sessions.middleware import SessionMiddleware middleware = SessionMiddleware(lambda req: None) middleware.process_request(request) request.session.save() # Not skipped yet: should return redirection response redirect_response = check_survey_interception(request, collection) assert redirect_response is not None assert "mandatory=false" in redirect_response.url # Skip by setting the session key session_key = f"skipped_pre_survey_{collection.pk}" request.session[session_key] = True request.session.save() # Now check again: should NOT redirect no_redirect_response = check_survey_interception(request, collection) assert no_redirect_response is None @pytest.mark.django_db def test_mandatory_pre_and_post_survey(rf, admin_user): survey_pre = Survey.objects.create(name="Mandatory Pre") survey_post = Survey.objects.create(name="Mandatory Post") collection = CaseCollection.objects.create( name="Test Collection", pre_survey=survey_pre, pre_survey_mandatory=True, post_survey=survey_post, post_survey_mandatory=True ) request = rf.get('/atlas/collection/1/take/') request.user = admin_user # Pre-take interception should happen and pass mandatory=true redirect_pre = check_survey_interception(request, collection) assert redirect_pre is not None assert "mandatory=true" in redirect_pre.url @pytest.mark.django_db def test_analytics_filtering(admin_user): from django.test import RequestFactory from survey.views import survey_results survey = Survey.objects.create(name="Common Survey") q = SurveyQuestion.objects.create( survey=survey, text="Rate something", question_type=SurveyQuestion.QuestionType.INTEGER ) collection = CaseCollection.objects.create(name="Collection 1") ct = ContentType.objects.get_for_model(collection) # Response 1: PRE-take r1 = SurveyResponse.objects.create( survey=survey, user=admin_user, content_type=ct, object_id=collection.pk, pre_or_post="PRE" ) SurveyAnswer.objects.create(response=r1, question=q, integer_answer=15) # Response 2: POST-take r2 = SurveyResponse.objects.create( survey=survey, user=admin_user, content_type=ct, object_id=collection.pk, pre_or_post="POST" ) SurveyAnswer.objects.create(response=r2, question=q, integer_answer=45) # Requesting results with filter_context='PRE' rf = RequestFactory() request_pre = rf.get('/survey/results/', {'filter_context': 'PRE'}) request_pre.user = admin_user response = survey_results(request_pre, survey.pk) # The rendered context should show responses_count = 1, and average_integer = 15.0 context = response.context_data assert context['responses_count'] == 1 assert context['analytics'][0]['average_integer'] == 15.0 # Requesting results with filter_context='POST' request_post = rf.get('/survey/results/', {'filter_context': 'POST'}) request_post.user = admin_user response_post = survey_results(request_post, survey.pk) context_post = response_post.context_data assert context_post['responses_count'] == 1 assert context_post['analytics'][0]['average_integer'] == 45.0