33from rest_framework .test import APITestCase
44from rest_framework import status
55from django .contrib .auth .models import User
6- # from apps.lessons.models import Assignment, Quiz, Question, Choice
7- from .models import Assignment , Quiz , Question , Choice
6+ # from apps.lessons.models import Assignment, Quiz, Question, Choice, Solution
7+ from .models import Assignment , Quiz , Question , Choice , Solution
88
99class AssignmentAPITests (APITestCase ):
1010 def setUp (self ):
@@ -38,7 +38,7 @@ def setUp(self):
3838 attempts = 1 ,
3939 is_active = True
4040 )
41- # Create a question for testing choices
41+ # Create a question for testing choices (used 'MC' for multiple choice)
4242 self .question = Question .objects .create (
4343 quiz = self .quiz ,
4444 question_type = "MC" ,
@@ -47,7 +47,7 @@ def setUp(self):
4747 points = 1
4848 )
4949
50- # Define URLs
50+ # Define URLs - used for assignment testing
5151 self .detail_url = reverse ('assignment-detail' , kwargs = {'pk' : self .assignment .pk })
5252 self .create_url = reverse ('assignment-create' )
5353
@@ -56,13 +56,13 @@ def setUp(self):
5656 # self.detail_url = f'/lessons/assignments/{self.assignment.pk}/'
5757 # self.create_url = '/lessons/assignments/create/'
5858
59- # Test GET single assignment
59+ # Test GET: Retrieve a single assignment
6060 def test_get_assignment (self ):
6161 response = self .client .get (self .detail_url )
6262 self .assertEqual (response .status_code , status .HTTP_200_OK )
6363 self .assertEqual (response .data ['data' ]['title' ], 'Initial Assignment' )
6464
65- # Test POST create new assignment
65+ # Test POST: Create a new assignment
6666 def test_create_assignment (self ):
6767 data = {
6868 "title" : "New Assignment" ,
@@ -74,38 +74,37 @@ def test_create_assignment(self):
7474 self .assertEqual (response .status_code , status .HTTP_201_CREATED )
7575 self .assertEqual (response .data ['data' ]['title' ], 'New Assignment' )
7676
77- # Test PATCH update assignment
77+ # Test PATCH: Update an existing assignment
7878 def test_update_assignment (self ):
7979 data = {"title" : "Updated Title" }
8080 response = self .client .patch (self .detail_url , data , format = 'json' )
8181 self .assertEqual (response .status_code , status .HTTP_200_OK )
8282 self .assertEqual (response .data ['data' ]['title' ], 'Updated Title' )
8383
84- # Test DELETE assignment
84+ # Test DELETE: Delete an assignment
8585 def test_delete_assignment (self ):
8686 response = self .client .delete (self .detail_url )
8787 self .assertEqual (response .status_code , status .HTTP_200_OK )
8888 self .assertEqual (response .data ['status' ], 'success' )
89-
9089 # Further verify that it is deleted
9190 response = self .client .get (self .detail_url )
9291 self .assertEqual (response .status_code , status .HTTP_404_NOT_FOUND )
9392
9493# ----------------------- + ------------------------------
9594# Quiz testing zone:
96- # Test POST creating a quiz (from an assignment in setup)
95+ # Test POST: Create a quiz (directly tied to an assignment in setup)
9796 def test_create_quiz (self ):
9897 url = reverse ('quiz-list-create' , kwargs = {'assignment_id' : self .assignment .id })
9998 data = {"assignment" : self .assignment .id , "time_limit" : 30 , "num_of_questions" : 30 , "attempts" : 1 ,
10099 "is_active" : False }
101100 response = self .client .post (url , data , format = 'json' )
102101 self .assertEqual (response .status_code , status .HTTP_201_CREATED )
103102 self .assertEqual (response .data ['data' ]['assignment' ], self .assignment .id )
104- # EXTRA : Check that the assignment linked to the quiz has the correct title
103+ # Verify : Check that the assignment linked to the quiz has the correct title
105104 quiz = Quiz .objects .get (pk = response .data ['data' ]['id' ])
106105 self .assertEqual (quiz .assignment .title , "Initial Assignment" )
107106
108- # Test GET list of quizzes (made from 1 assignment)
107+ # Test GET: Retrieve a list of quizzes (made from a single assignment)
109108 def test_get_quiz_list (self ):
110109 # Create a couple of quizzes for the same assignment
111110 Quiz .objects .create (assignment = self .assignment , time_limit = 20 , num_of_questions = 10 , attempts = 1 , is_active = True )
@@ -114,25 +113,27 @@ def test_get_quiz_list(self):
114113 url = reverse ('quiz-list-create' , kwargs = {'assignment_id' : self .assignment .id })
115114 response = self .client .get (url )
116115 self .assertEqual (response .status_code , status .HTTP_200_OK )
117- # Expecting 2 quizzes to be returned + 1 self-made one (used for questions)
116+ # Expecting 2 quizzes to be returned + 1 self-made one (used later for questions)
118117 self .assertEqual (len (response .data ['data' ]), 3 )
119118
120- # Test GET single quiz
119+ # Test GET: Retrieve a single quiz
121120 def test_get_quiz_detail (self ):
122121 # Create a quiz instance to retrieve
123122 quiz = Quiz .objects .create (assignment = self .assignment , time_limit = 45 , num_of_questions = 15 , attempts = 3 ,
124123 is_active = True )
124+
125125 url = reverse ('quiz-detail' , kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : quiz .id })
126126 response = self .client .get (url )
127127 self .assertEqual (response .status_code , status .HTTP_200_OK )
128128 self .assertEqual (response .data ['data' ]['id' ], quiz .id )
129129 self .assertEqual (response .data ['data' ]['assignment' ], self .assignment .id )
130130
131- # Test PATCH update quiz
131+ # Test PATCH: Update quiz
132132 def test_update_quiz (self ):
133133 # Create a quiz instance to update
134134 quiz = Quiz .objects .create (assignment = self .assignment , time_limit = 40 , num_of_questions = 25 , attempts = 2 ,
135135 is_active = False )
136+
136137 url = reverse ('quiz-detail' , kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : quiz .id })
137138 data = {"is_active" : True , "time_limit" : 50 }
138139 response = self .client .patch (url , data , format = 'json' )
@@ -142,11 +143,12 @@ def test_update_quiz(self):
142143 self .assertTrue (quiz .is_active )
143144 self .assertEqual (quiz .time_limit , 50 )
144145
145- # Test DELETE quiz
146+ # Test DELETE: Delete a quiz
146147 def test_delete_quiz (self ):
147148 # Create a quiz instance to delete
148149 quiz = Quiz .objects .create (assignment = self .assignment , time_limit = 30 , num_of_questions = 30 , attempts = 1 ,
149150 is_active = True )
151+
150152 url = reverse ('quiz-detail' , kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : quiz .id })
151153 response = self .client .delete (url )
152154 self .assertEqual (response .status_code , status .HTTP_200_OK )
@@ -157,7 +159,7 @@ def test_delete_quiz(self):
157159
158160# ----------------------- + ------------------------------
159161# Quiz-question testing zone:
160- # Test GET a list of questions for a given quiz
162+ # Test GET: Retrieve a list of questions for a given quiz
161163 def test_get_question_list (self ):
162164 # Create two questions for self.quiz
163165 Question .objects .create (
@@ -174,21 +176,18 @@ def test_get_question_list(self):
174176 question_text = "Describe gravity in one sentence." ,
175177 points = 2
176178 )
177- url = reverse (
178- 'quiz-question-list' ,
179- kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id }
180- )
179+
180+ url = reverse ('quiz-question-list' ,
181+ kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id })
181182 response = self .client .get (url )
182183 self .assertEqual (response .status_code , status .HTTP_200_OK )
183- # Verify: 2 questions are returned + 1 self-made one (used for choices)
184+ # Verify: 2 questions are returned + 1 self-made one (used later for choices)
184185 self .assertEqual (len (response .data ['data' ]), 3 )
185186
186- # Test POST creating a new question for a quiz
187+ # Test POST: Create a new question for a quiz
187188 def test_create_question (self ):
188- url = reverse (
189- 'question-create' ,
190- kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id }
191- )
189+ url = reverse ('question-create' ,
190+ kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id })
192191 data = {
193192 "question_type" : "MC" ,
194193 "order_of_question" : 1 ,
@@ -197,11 +196,11 @@ def test_create_question(self):
197196 }
198197 response = self .client .post (url , data , format = 'json' )
199198 self .assertEqual (response .status_code , status .HTTP_201_CREATED )
200- # Ensure the response contains the quiz id and correct question details
199+ # Verify/ Ensure the response contains the quiz id and correct question details
201200 self .assertEqual (response .data ['data' ]['quiz' ], self .quiz .id )
202201 self .assertEqual (response .data ['data' ]['question_text' ], "What is the capital of France?" )
203202
204- # Test PUT updating an existing question
203+ # Test PUT: Update an existing question
205204 def test_update_question (self ):
206205 # Create a question to update
207206 question = Question .objects .create (
@@ -211,10 +210,9 @@ def test_update_question(self):
211210 question_text = "Old question text" ,
212211 points = 1
213212 )
214- url = reverse (
215- 'question-update' ,
216- kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id , 'question_id' : question .id }
217- )
213+
214+ url = reverse ('question-update' ,
215+ kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id , 'question_id' : question .id })
218216 update_data = {
219217 "question_text" : "Updated question text" ,
220218 "points" : 5
@@ -226,7 +224,7 @@ def test_update_question(self):
226224 self .assertEqual (question .question_text , "Updated question text" )
227225 self .assertEqual (question .points , 5 )
228226
229- # Test DELETE question
227+ # Test DELETE: Delete a question
230228 def test_delete_question (self ):
231229 # Create a question to delete
232230 question = Question .objects .create (
@@ -236,10 +234,9 @@ def test_delete_question(self):
236234 question_text = "Question to be deleted" ,
237235 points = 1
238236 )
239- url = reverse (
240- 'question-delete' ,
241- kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id , 'question_id' : question .id }
242- )
237+
238+ url = reverse ('question-delete' ,
239+ kwargs = {'assignment_id' : self .assignment .id , 'quiz_id' : self .quiz .id , 'question_id' : question .id })
243240 response = self .client .delete (url )
244241 self .assertEqual (response .status_code , status .HTTP_200_OK )
245242 self .assertEqual (response .data ['status' ], 'success' )
@@ -248,27 +245,25 @@ def test_delete_question(self):
248245
249246# ----------------------- + ------------------------------
250247# Choices testing zone:
251- # Test GET list of choices for a given question.
248+ # Test GET: Retrieve a list of choices for a given question
252249 def test_get_choice_list (self ):
253250 # Create two choices associated with the (setup) question
254251 Choice .objects .create (question = self .question , choice_text = "Blue" , is_correct = True )
255252 Choice .objects .create (question = self .question , choice_text = "Red" , is_correct = False )
256253
257- url = reverse (
258- "choice-list" ,
259- kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id , "question_id" : self .question .id }
260- )
254+ url = reverse ("choice-list" ,
255+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
256+ "question_id" : self .question .id })
261257 response = self .client .get (url )
262258 self .assertEqual (response .status_code , status .HTTP_200_OK )
263259 # Verify that two choices are returned
264260 self .assertEqual (len (response .data ["data" ]), 2 )
265261
266- # Test POST bulk -create choices for a given question
262+ # Test POST: Bulk -create choices for a given question
267263 def test_create_choices (self ):
268- url = reverse (
269- "choice-create" ,
270- kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,"question_id" : self .question .id }
271- )
264+ url = reverse ("choice-create" ,
265+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
266+ "question_id" : self .question .id })
272267 # choices data with multiple choices
273268 choices_payload = {
274269 "choices" : [{"choice_text" : "Blue" , "is_correct" : True }, {"choice_text" : "Green" , "is_correct" : False }]
@@ -280,20 +275,16 @@ def test_create_choices(self):
280275 for choice_data in response .data ["data" ]:
281276 self .assertEqual (choice_data ["question" ], self .question .id )
282277
283- # Test PUT bulk -update choices for a given question
278+ # Test PUT: Bulk -update choices for a given question
284279 def test_update_choices (self ):
285280 # Create two initial choices
286- choice1 = Choice .objects .create (
287- question = self .question , choice_text = "Blue" , is_correct = True
288- )
289- choice2 = Choice .objects .create (
290- question = self .question , choice_text = "Green" , is_correct = False
291- )
292- url = reverse (
293- "choice-update" ,
294- kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id , "question_id" : self .question .id }
295- )
296- # data with multiple choices
281+ choice1 = Choice .objects .create (question = self .question , choice_text = "Blue" , is_correct = True )
282+ choice2 = Choice .objects .create (question = self .question , choice_text = "Green" , is_correct = False )
283+
284+ url = reverse ("choice-update" ,
285+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
286+ "question_id" : self .question .id })
287+ # Data with multiple choices
297288 update_data = {
298289 "choices" : [
299290 {"id" : choice1 .id , "choice_text" : "Navy Blue" , "is_correct" : True },
@@ -307,33 +298,97 @@ def test_update_choices(self):
307298 # Reload the choices from the database and verify updates - AI & Websearch assisted
308299 for updated in updated_choices :
309300 choice_obj = Choice .objects .get (id = updated ["id" ])
310- expected = next (
311- item for item in update_data ["choices" ] if item ["id" ] == updated ["id" ]
312- )
301+ expected = next (item for item in update_data ["choices" ] if item ["id" ] == updated ["id" ])
313302 self .assertEqual (choice_obj .choice_text , expected ["choice_text" ])
314303 self .assertEqual (choice_obj .is_correct , expected ["is_correct" ])
315304
316- # Test DELETE all choices for a question
305+ # Test DELETE: Delete all choices for a question
317306 def test_delete_choices (self ):
318307 # Create multiple (3) choices for the (setup) question
319308 Choice .objects .create (question = self .question , choice_text = "Blue" , is_correct = True )
320309 Choice .objects .create (question = self .question , choice_text = "Green" , is_correct = False )
321310 Choice .objects .create (question = self .question , choice_text = "Red" , is_correct = False )
322311
323- url = reverse (
324- "choice-delete" ,
325- kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id , "question_id" : self .question .id }
326- )
312+ url = reverse ("choice-delete" ,
313+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
314+ "question_id" : self .question .id })
327315 response = self .client .delete (url )
328316 self .assertEqual (response .status_code , status .HTTP_200_OK )
329317 self .assertEqual (response .data ["status" ], "success" )
330318 # Verify that the choices are actually deleted. A follow-up GET request should return no choices.
331- list_url = reverse (
332- "choice-list" ,
333- kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id , "question_id" : self .question .id }
334- )
319+ list_url = reverse ("choice-list" ,
320+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
321+ "question_id" : self .question .id })
335322 get_response = self .client .get (list_url )
336323 self .assertEqual (get_response .status_code , status .HTTP_200_OK )
337324 self .assertEqual (len (get_response .data ["data" ]), 0 )
325+
338326# ----------------------- + ------------------------------
339327# Solutions testing zone:
328+ # Test GET: Retrieve a solution for a question
329+ def test_get_solution_detail (self ):
330+ # Create a choice and a solution instance for the question
331+ choice = Choice .objects .create (question = self .question , choice_text = "Blue" , is_correct = True )
332+ solution = Solution .objects .create (question = self .question )
333+ solution .choices .add (choice )
334+
335+ url = reverse ("solution-detail" ,
336+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
337+ "question_id" : self .question .id })
338+ response = self .client .get (url )
339+ self .assertEqual (response .status_code , status .HTTP_200_OK )
340+ data = response .data ["data" ]
341+ self .assertEqual (data ["question" ], self .question .id )
342+ # Verify the returned solution has the correct number of associated choices.
343+ self .assertEqual (len (data ["choices" ]), 1 )
344+
345+ # Test POST: Create a new solution by providing choices (or a short answer)
346+ def test_create_solution (self ):
347+ # For an MC question, create a choice to associate with the solution.
348+ choice = Choice .objects .create (question = self .question , choice_text = "Blue" , is_correct = True )
349+
350+ url = reverse ("solution-create" ,
351+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
352+ "question_id" : self .question .id })
353+ data = {
354+ "choices" : [choice .id ]
355+ # For a short answer question, you might include: "short_answer_text": "Your answer"
356+ }
357+ response = self .client .post (url , data , format = "json" )
358+ self .assertEqual (response .status_code , status .HTTP_201_CREATED )
359+ self .assertEqual (response .data ["data" ]["question" ], self .question .id )
360+ # Verify the created solution includes the provided choice
361+ self .assertIn (choice .id , response .data ["data" ]["choices" ])
362+
363+ # Test PUT: Update an existing solution
364+ def test_update_solution (self ):
365+ # Create an initial solution with a short answer
366+ solution = Solution .objects .create (question = self .question , short_answer_text = "Initial solution text" )
367+
368+ url = reverse ("solution-update" ,
369+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
370+ "question_id" : self .question .id })
371+ update_data = {"short_answer_text" : "Updated solution text" }
372+ response = self .client .put (url , update_data , format = "json" )
373+ self .assertEqual (response .status_code , status .HTTP_200_OK )
374+ # Refresh the solution from the database and verify the update
375+ solution .refresh_from_db ()
376+ self .assertEqual (solution .short_answer_text , "Updated solution text" )
377+
378+ # Test DELETE: Delete a solution
379+ def test_delete_solution (self ):
380+ # Create a solution to delete
381+ solution = Solution .objects .create (question = self .question , short_answer_text = "Solution to be deleted" )
382+
383+ url = reverse ("solution-delete" ,
384+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
385+ "question_id" : self .question .id })
386+ response = self .client .delete (url )
387+ self .assertEqual (response .status_code , status .HTTP_200_OK )
388+ self .assertEqual (response .data ["status" ], "success" )
389+ # Verify deletion by attempting to retrieve the solution and expecting a 404.
390+ get_url = reverse ("solution-detail" ,
391+ kwargs = {"assignment_id" : self .assignment .id , "quiz_id" : self .quiz .id ,
392+ "question_id" : self .question .id })
393+ get_response = self .client .get (get_url )
394+ self .assertEqual (get_response .status_code , status .HTTP_404_NOT_FOUND )
0 commit comments