Skip to content

Commit 906e8dc

Browse files
author
CHAS Technology
committed
add tests
1 parent 9498f3b commit 906e8dc

5 files changed

Lines changed: 216 additions & 15 deletions

File tree

‎backend/ohq/serializers.py‎

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.core.exceptions import ObjectDoesNotExist
55
from django.utils import timezone
66
from django.utils.crypto import get_random_string
7+
from django.http import JsonResponse
78
from phonenumber_field.serializerfields import PhoneNumberField
89
from rest_framework import serializers
910
from rest_live.signals import save_handler
@@ -51,17 +52,41 @@ def save(self):
5152
self.validated_data["queue"] = Queue.objects.get(pk=self.context["view"].kwargs["queue_pk"])
5253
return super().save()
5354

54-
class QuestionRouteMixin(serializers.ModelSerializer):
55+
class QuestionReviewRouteMixin(serializers.ModelSerializer):
5556
"""
5657
Mixin for serializers that overrides the save method to
5758
properly handle the URL parameter for questions.
5859
"""
5960

6061
def save(self):
6162
self.validated_data["question"] = Question.objects.get(pk=self.context["view"].kwargs["question_pk"])
62-
return super().save()
63-
64-
63+
64+
if self.validated_data["question"].status != "ANSWERED":
65+
return JsonResponse({"detail": "This question has not been answered by a TA yet"})
66+
67+
if self.context["request"].method == "POST" and "rating" in self.validated_data:
68+
if "rating" not in self.validated_data:
69+
return JsonResponse({"detail": "A rating must be provided"})
70+
if self.validated_data["question"].review != None:
71+
return JsonResponse({"detail": "This question is already reviewed."})
72+
review = Review(content="", rating=self.validated_data["rating"])
73+
if "content" in self.validated_data:
74+
review.content = self.validated_data["content"]
75+
review.save()
76+
self.validated_data["question"].review = review
77+
self.validated_data["question"].save()
78+
return JsonResponse({"detail": "Your review has been posted."})
79+
80+
if self.context["request"].method == "PATCH":
81+
self.validated_data["review"] = Review.objects.get(pk=self.context["view"].kwargs["pk"])
82+
if "rating" not in self.validated_data and "content" not in self.validated_data:
83+
return JsonResponse({"detail": "Your review does not contain any content or rating."})
84+
if "rating" in self.validated_data:
85+
self.validated_data["review"].rating = self.validated_data["rating"]
86+
if "content" in self.validated_data:
87+
self.validated_data["review"].content = self.validated_data["content"]
88+
self.validated_data["review"].save()
89+
return JsonResponse({"detail": "Your review is updated"})
6590

6691
class SemesterSerializer(serializers.ModelSerializer):
6792
pretty = serializers.SerializerMethodField()
@@ -221,7 +246,7 @@ class Meta:
221246
fields = ("id", "name")
222247

223248

224-
class ReviewSerializer(QuestionRouteMixin):
249+
class ReviewSerializer(QuestionReviewRouteMixin):
225250
"""
226251
Serializer for review
227252
"""
@@ -240,7 +265,7 @@ class QuestionSerializer(QueueRouteMixin):
240265
responded_to_by = UserSerializer(read_only=True)
241266
tags = TagSerializer(many=True)
242267
position = serializers.IntegerField(default=-1, read_only=True)
243-
review = ReviewSerializer()
268+
review = ReviewSerializer(read_only=True)
244269

245270
class Meta:
246271
model = Question
@@ -272,6 +297,7 @@ class Meta:
272297
"should_send_up_soon_notification",
273298
"resolved_note",
274299
"position",
300+
"review",
275301
)
276302

277303
def update(self, instance, validated_data):

‎backend/ohq/views.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,6 @@ class ReviewViewSet(viewsets.ModelViewSet):
824824

825825
def get_queryset(self):
826826
membership = Membership.objects.get(course=self.kwargs["course_pk"], user=self.request.user)
827-
828827
if membership.kind == "TA":
829828
return Review.objects.filter(question=self.kwargs["question_pk"], question__responded_to_by=self.request.user)
830829

‎backend/tests/ohq/test_permissions.py‎

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,3 +1311,105 @@ def test_modify(self, user):
13111311
reverse("ohq:occurrence-detail", args=[self.occurrence.id]),
13121312
{"title": self.new_title, "courseId": self.course.id},
13131313
)
1314+
1315+
class ReviewTestCase(TestCase):
1316+
def setUp(self):
1317+
setUp(self)
1318+
self.semester = Semester.objects.create(year=2020, term=Semester.TERM_SUMMER)
1319+
self.course = Course.objects.create(
1320+
course_code="000", department="Penn Labs", semester=self.semester
1321+
)
1322+
self.head_ta = User.objects.create(username="head_ta")
1323+
self.ta = User.objects.create(username="ta")
1324+
self.other_ta = User.objects.create(username="other_ta")
1325+
self.student = User.objects.create(username="student")
1326+
self.other_student = User.objects.create(username="other_student")
1327+
Membership.objects.create(
1328+
course=self.course, user=self.head_ta, kind=Membership.KIND_HEAD_TA
1329+
)
1330+
Membership.objects.create(course=self.course, user=self.ta, kind=Membership.KIND_TA)
1331+
Membership.objects.create(
1332+
course=self.course, user=self.student, kind=Membership.KIND_STUDENT
1333+
)
1334+
Membership.objects.create(
1335+
course=self.course, user=self.other_student, kind=Membership.KIND_STUDENT
1336+
)
1337+
self.queue = Queue.objects.create(name="Queue", course=self.course)
1338+
self.question_text = "This is a question"
1339+
self.question_1 = Question.objects.create(
1340+
queue=self.queue, asked_by=self.student, text=self.question_text, status="ANSWERED"
1341+
)
1342+
self.review_1 = Review.objects.create(content="TA was helpful", rating=5)
1343+
self.question_1.review = self.review_1
1344+
se;f.question_1.save()
1345+
self.question_2 = Question.objects.create(
1346+
queue=self.queue, asked_by=self.student, text=self.question_text, status="ANSWERED"
1347+
)
1348+
self.review_2 = Review.objects.create(content="TA was mid", rating=2)
1349+
self.question_2.review = self.review_2
1350+
se;f.question_2.save()
1351+
1352+
1353+
# Expected results
1354+
self.expected = {
1355+
"list": {
1356+
"professor": 200,
1357+
"head_ta": 200,
1358+
"ta": 200,
1359+
"student": 200,
1360+
"non_member": 403,
1361+
"anonymous": 403,
1362+
},
1363+
"retrieve": {
1364+
"professor": 200,
1365+
"head_ta": 200,
1366+
"ta": 200,
1367+
"student": 200,
1368+
"non_member": 403,
1369+
"anonymous": 403,
1370+
},
1371+
"modify": {
1372+
"professor": 403,
1373+
"head_ta": 403,
1374+
"ta": 403,
1375+
"student": 200,
1376+
"non_member": 403,
1377+
"anonymous": 403,
1378+
},
1379+
}
1380+
1381+
@parameterized.expand(users, name_func=get_test_name)
1382+
def test_list(self, user):
1383+
test(
1384+
self,
1385+
user,
1386+
"list",
1387+
"get",
1388+
"/api/occurrences/?course="
1389+
+ str(self.course.id)
1390+
+ "&filter_start="
1391+
+ self.filter_start
1392+
+ "&filter_end="
1393+
+ self.filter_end,
1394+
)
1395+
1396+
@parameterized.expand(users, name_func=get_test_name)
1397+
def test_retrieve(self, user):
1398+
test(
1399+
self,
1400+
user,
1401+
"retrieve",
1402+
"get",
1403+
reverse("ohq:occurrence-detail", args=[self.occurrence.id]),
1404+
)
1405+
1406+
@parameterized.expand(users, name_func=get_test_name)
1407+
def test_modify(self, user):
1408+
test(
1409+
self,
1410+
user,
1411+
"modify",
1412+
"patch",
1413+
reverse("ohq:occurrence-detail", args=[self.occurrence.id]),
1414+
{"title": self.new_title, "courseId": self.course.id},
1415+
)

‎backend/tests/ohq/test_serializers.py‎

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
MembershipSerializer,
1717
SemesterSerializer,
1818
UserPrivateSerializer,
19+
ReviewSerializer,
1920
)
2021

2122

@@ -302,7 +303,7 @@ def setUp(self):
302303

303304
def test_create(self, mock_delay):
304305
self.client.force_authenticate(user=self.student2)
305-
self.client.post(
306+
response = self.client.post(
306307
reverse("ohq:question-list", args=[self.course.id, self.queue.id]),
307308
{"text": "Help me", "tags": [{"name": "Tag"}]},
308309
)
@@ -733,6 +734,7 @@ def setUp(self):
733734
)
734735
self.head_ta = User.objects.create(username="head_ta")
735736
self.ta = User.objects.create(username="ta")
737+
self.other_ta = User.objects.create(username="other_ta")
736738
self.student = User.objects.create(username="student")
737739
self.other_student = User.objects.create(username="other_student")
738740
Membership.objects.create(
@@ -748,17 +750,17 @@ def setUp(self):
748750
self.queue = Queue.objects.create(name="Queue", course=self.course)
749751
self.question_text = "This is a question"
750752
self.question_1 = Question.objects.create(
751-
queue=self.queue, asked_by=self.student, text=self.question_text
753+
queue=self.queue, asked_by=self.student, text=self.question_text, status="ANSWERED"
752754
)
753755
self.review_content_1 = "TA was helpful"
754756
self.review_rating_1 = 5
755757
self.question_2 = Question.objects.create(
756-
queue=self.queue, asked_by=self.student, text=self.question_text
758+
queue=self.queue, asked_by=self.student, text=self.question_text, status="ANSWERED"
757759
)
758760
self.review_content_2 = "TA was mid"
759761
self.review_rating_2 = 2
760762
self.question_3 = Question.objects.create(
761-
queue=self.queue, asked_by=self.student, text=self.question_text
763+
queue=self.queue, asked_by=self.student, text=self.question_text, status="ANSWERED"
762764
)
763765
self.review_content_3 = "TA was decent"
764766
self.review_rating_3 = 4
@@ -777,6 +779,28 @@ def test_create(self):
777779
)
778780
self.assertEqual(1, Review.objects.all().count())
779781

782+
# Ensure only one review can be made for one question
783+
self.client.post(
784+
reverse("ohq:review-list", args=[self.course.id, self.queue.id, self.question_1.id]),
785+
{
786+
"content": self.review_content_2,
787+
"rating": self.review_rating_2
788+
},
789+
)
790+
self.assertEqual(1, Review.objects.all().count())
791+
792+
# Ensure review cannot be made to unanswered question
793+
unanswered_question = Question.objects.create(
794+
queue=self.queue, asked_by=self.student, text=self.question_text
795+
)
796+
self.client.post(
797+
reverse("ohq:review-list", args=[self.course.id, self.queue.id, unanswered_question.id]),
798+
{
799+
"content": self.review_content_2,
800+
"rating": self.review_rating_2
801+
},
802+
)
803+
self.assertEqual(1, Review.objects.all().count())
780804

781805
# Ensure TA cannot create a review
782806
self.client.force_authenticate(user=self.ta)
@@ -837,14 +861,64 @@ def test_list(self):
837861
"rating": self.review_rating_1
838862
},
839863
)
840-
response = self.client.get("/api/courses/" + self.course.id
841-
+ "/queues/" + self.queue.id
842-
+ "/questions/" + self.question_1.id + "/reviews/")
864+
response = self.client.get("/api/courses/" +str( self.course.id)
865+
+ "/queues/" + str(self.queue.id)
866+
+ "/questions/" + str(self.question_1.id) + "/reviews/")
843867
data = json.loads(response.content)
844868
self.assertEqual(1, len(data))
845869

870+
# Student cannot get access to review made by other students
846871
self.client.force_authenticate(user=self.other_student)
872+
response = self.client.get("/api/courses/" + str( self.course.id)
873+
+ "/queues/" + str(self.queue.id)
874+
+ "/questions/" + str(self.question_1.id) + "/reviews/")
875+
data = json.loads(response.content)
876+
self.assertEqual(0, len(data))
877+
878+
def test_update(self):
879+
"""
880+
Ensure students can update reviews
881+
"""
882+
self.client.force_authenticate(user=self.student)
883+
self.client.post(
884+
reverse("ohq:review-list", args=[self.course.id, self.queue.id, self.question_1.id]),
885+
{
886+
"content": self.review_content_1,
887+
"rating": self.review_rating_1
888+
},
889+
)
890+
review = Review.objects.first()
891+
response = self.client.patch(
892+
reverse("ohq:review-detail", args=[self.course.id, self.queue.id, self.question_1.id, review.id]),
893+
{"content": "hello"},
894+
)
895+
review = Review.objects.first()
896+
self.assertEqual("hello", review.content)
897+
response = self.client.patch(
898+
reverse("ohq:review-detail", args=[self.course.id, self.queue.id, self.question_1.id, review.id]),
899+
{"rating": 2},
900+
)
901+
review = Review.objects.first()
902+
self.assertEqual(2, review.rating)
847903

904+
# Updating reviews without appropriate key will not work
905+
response = self.client.patch(
906+
reverse("ohq:review-detail", args=[self.course.id, self.queue.id, self.question_1.id, review.id]),
907+
{"something": 3},
908+
)
909+
self.assertEqual("hello", review.content)
910+
self.assertEqual(2, review.rating)
911+
912+
# Ensure TAs cannot update reviews
913+
self.client.force_authenticate(user=self.ta)
914+
review = Review.objects.first()
915+
response = self.client.patch(
916+
reverse("ohq:review-detail", args=[self.course.id, self.queue.id, self.question_1.id, review.id]),
917+
{"content": "The best ta ever", "rating": 5},
918+
)
919+
review = Review.objects.first()
920+
self.assertEqual("hello", review.content)
921+
self.assertEqual(2, review.rating)
848922

849923

850924

‎backend/tests/ohq/test_views.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def test_list(self):
309309
response = self.client.get(
310310
"/api/occurrences/?course="
311311
+ str(self.course.id)
312-
+ "&filter_start="b
312+
+ "&filter_start="
313313
+ self.filter_start
314314
+ "&filter_end="
315315
+ self.filter_end

0 commit comments

Comments
 (0)