All checks were successful
Deploy Quartz site to GitHub Pages / build (push) Successful in 2m29s
308 lines
10 KiB
Python
308 lines
10 KiB
Python
from django.test import TestCase, Client
|
|
from django.urls import reverse
|
|
from quiz.models import Question, QuizUser, QuizSession, QuizResult, Course, Exam, Tag, Option
|
|
|
|
|
|
class QuizViewsTestCase(TestCase):
|
|
"""Comprehensive tests for all quiz endpoints"""
|
|
|
|
def setUp(self):
|
|
"""Set up test data"""
|
|
self.client = Client()
|
|
|
|
# Ensure session exists and get its key
|
|
s = self.client.session
|
|
s.save()
|
|
session_key = s.session_key
|
|
|
|
# Create test user with the same session key
|
|
self.user = QuizUser.objects.create(session_key=session_key)
|
|
|
|
# Create test course
|
|
self.course = Course.objects.create(name="Test Course")
|
|
|
|
# Create test exam
|
|
self.exam = Exam.objects.create(
|
|
name="Test Exam",
|
|
course=self.course,
|
|
date="2025-01-01" # Required field
|
|
)
|
|
|
|
# Create test tags
|
|
self.tag1 = Tag.objects.create(name="Tag 1", slug="tag-1")
|
|
self.tag2 = Tag.objects.create(name="Tag 2", slug="tag-2")
|
|
|
|
# Create test questions
|
|
self.question1 = Question.objects.create(
|
|
text="Test question 1?",
|
|
correct_answer="A",
|
|
exam=self.exam,
|
|
file_path="test1.md"
|
|
)
|
|
self.question1.tags.add(self.tag1)
|
|
|
|
Option.objects.create(question=self.question1, letter="A", text="Answer A")
|
|
Option.objects.create(question=self.question1, letter="B", text="Answer B")
|
|
|
|
self.question2 = Question.objects.create(
|
|
text="Test question 2 (multi)?",
|
|
correct_answer="A,B",
|
|
exam=self.exam,
|
|
file_path="test2.md"
|
|
)
|
|
self.question2.tags.add(self.tag1, self.tag2)
|
|
|
|
Option.objects.create(question=self.question2, letter="A", text="Answer A")
|
|
Option.objects.create(question=self.question2, letter="B", text="Answer B")
|
|
Option.objects.create(question=self.question2, letter="C", text="Answer C")
|
|
|
|
# Set user in session
|
|
session = self.client.session
|
|
session['quiz_user_id'] = self.user.id
|
|
session.save()
|
|
|
|
def test_index_view(self):
|
|
"""Test dashboard index view"""
|
|
response = self.client.get(reverse('quiz:index'))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Snabbstart')
|
|
self.assertIn('active_sessions', response.context)
|
|
self.assertIn('form', response.context)
|
|
|
|
def test_create_quiz(self):
|
|
"""Test quiz creation"""
|
|
response = self.client.post(reverse('quiz:create_quiz'), {
|
|
'course': self.course.id,
|
|
'tags': [self.tag1.id],
|
|
})
|
|
self.assertEqual(response.status_code, 302) # Redirect
|
|
|
|
# Verify session was created
|
|
session = QuizSession.objects.filter(user=self.user).first()
|
|
self.assertIsNotNone(session)
|
|
self.assertEqual(session.course, self.course)
|
|
self.assertTrue(session.is_active)
|
|
|
|
def test_quiz_mode_view(self):
|
|
"""Test dedicated quiz mode view"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
response = self.client.get(reverse('quiz:quiz_mode', args=[session.id]))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Test Course')
|
|
self.assertIn('session', response.context)
|
|
|
|
def test_quiz_question_view(self):
|
|
"""Test quiz question endpoint"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
response = self.client.get(reverse('quiz:quiz_question', args=[session.id]))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn('question', response.context)
|
|
self.assertIn('session', response.context)
|
|
|
|
def test_navigate_question(self):
|
|
"""Test question navigation"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
# Test next navigation
|
|
response = self.client.get(
|
|
reverse('quiz:navigate_question', args=[session.id, 'next']),
|
|
{'q': self.question1.id}
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test previous navigation
|
|
response = self.client.get(
|
|
reverse('quiz:navigate_question', args=[session.id, 'previous']),
|
|
{'q': self.question2.id}
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_submit_single_answer(self):
|
|
"""Test submitting a single-choice answer"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
response = self.client.post(
|
|
reverse('quiz:submit_answer', args=[session.id]),
|
|
{
|
|
'question_id': self.question1.id,
|
|
'answer': 'A'
|
|
}
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Verify result was created
|
|
result = QuizResult.objects.get(
|
|
user=self.user,
|
|
question=self.question1,
|
|
quiz_session=session
|
|
)
|
|
self.assertEqual(result.selected_answer, 'A')
|
|
self.assertTrue(result.is_correct)
|
|
|
|
def test_submit_multi_answer(self):
|
|
"""Test submitting a multi-choice answer"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
# Test correct multi-answer (order shouldn't matter)
|
|
response = self.client.post(
|
|
reverse('quiz:submit_answer', args=[session.id]),
|
|
{
|
|
'question_id': self.question2.id,
|
|
'answer': 'B,A' # Reversed order
|
|
}
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
result = QuizResult.objects.get(
|
|
user=self.user,
|
|
question=self.question2,
|
|
quiz_session=session
|
|
)
|
|
self.assertTrue(result.is_correct) # Should be correct despite order
|
|
|
|
def test_submit_difficulty(self):
|
|
"""Test submitting FSRS difficulty rating"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
# First submit an answer
|
|
QuizResult.objects.create(
|
|
user=self.user,
|
|
question=self.question1,
|
|
quiz_session=session,
|
|
selected_answer='A',
|
|
is_correct=True
|
|
)
|
|
|
|
# Then submit difficulty
|
|
response = self.client.post(
|
|
reverse('quiz:submit_difficulty', args=[session.id]),
|
|
{
|
|
'question_id': self.question1.id,
|
|
'difficulty': 'easy'
|
|
}
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Verify difficulty was saved
|
|
result = QuizResult.objects.get(
|
|
user=self.user,
|
|
question=self.question1,
|
|
quiz_session=session
|
|
)
|
|
self.assertEqual(result.difficulty, 'easy')
|
|
|
|
def test_close_quiz(self):
|
|
"""Test closing a quiz session"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course,
|
|
is_active=True
|
|
)
|
|
|
|
response = self.client.post(reverse('quiz:close_quiz', args=[session.id]))
|
|
self.assertEqual(response.status_code, 302) # Redirect to index
|
|
|
|
# Verify session was deactivated
|
|
session.refresh_from_db()
|
|
self.assertFalse(session.is_active)
|
|
|
|
def test_invalid_session_access(self):
|
|
"""Test accessing another user's session"""
|
|
other_user = QuizUser.objects.create(session_key="other_session_key")
|
|
session = QuizSession.objects.create(
|
|
user=other_user,
|
|
course=self.course
|
|
)
|
|
|
|
# Try to access it
|
|
response = self.client.get(reverse('quiz:quiz_mode', args=[session.id]))
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_answer_without_question_id(self):
|
|
"""Test error handling for missing question_id"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
response = self.client.post(
|
|
reverse('quiz:submit_answer', args=[session.id]),
|
|
{'answer': 'A'} # Missing question_id
|
|
)
|
|
self.assertEqual(response.status_code, 400)
|
|
|
|
def test_difficulty_without_result(self):
|
|
"""Test error handling for difficulty without existing result"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
response = self.client.post(
|
|
reverse('quiz:submit_difficulty', args=[session.id]),
|
|
{
|
|
'question_id': self.question1.id,
|
|
'difficulty': 'easy'
|
|
}
|
|
)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_answer_normalization(self):
|
|
"""Test that multi-choice answers are normalized correctly"""
|
|
session = QuizSession.objects.create(
|
|
user=self.user,
|
|
course=self.course
|
|
)
|
|
|
|
# Submit answers in different orders
|
|
test_cases = [
|
|
('A,B', True), # Correct order
|
|
('B,A', True), # Reversed order
|
|
('A,C', False), # Wrong answer
|
|
('A', False), # Incomplete answer
|
|
]
|
|
|
|
for answer, expected_correct in test_cases:
|
|
result = QuizResult.objects.filter(
|
|
user=self.user,
|
|
question=self.question2,
|
|
quiz_session=session
|
|
).delete() # Clean up
|
|
|
|
response = self.client.post(
|
|
reverse('quiz:submit_answer', args=[session.id]),
|
|
{
|
|
'question_id': self.question2.id,
|
|
'answer': answer
|
|
}
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
result = QuizResult.objects.get(
|
|
user=self.user,
|
|
question=self.question2,
|
|
quiz_session=session
|
|
)
|
|
self.assertEqual(result.is_correct, expected_correct,
|
|
f"Answer '{answer}' should be {'correct' if expected_correct else 'incorrect'}")
|