def test_delete_answer(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): answer_uuid = self.fixtures.answers[0].uuid # test login required rv = self.client.delete(self.base_url + '/' + answer_uuid) self.assert401(rv) # test unauthorized users with self.login(self.fixtures.students[1].username): rv = self.client.delete(self.base_url + '/' + answer_uuid) self.assert403(rv) # test invalid answer id with self.login(self.fixtures.students[0].username): rv = self.client.delete(self.base_url + '/999') self.assert404(rv) lti_consumer = self.lti_data.lti_consumer student = self.fixtures.students[0] (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( student, self.fixtures.course, self.fixtures.assignment) course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.students[0]).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.students[0]).grade # test deletion by author rv = self.client.delete(self.base_url + '/' + answer_uuid) self.assert200(rv) self.assertEqual(answer_uuid, rv.json['id']) # grades should decrease new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.students[0]) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.students[0]) self.assertLess(new_course_grade.grade, course_grade) self.assertLess(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock() # test deletion by user that can manage posts with self.login(self.fixtures.instructor.username): answer_uuid2 = self.fixtures.answers[1].uuid rv = self.client.delete(self.base_url + '/' + answer_uuid2) self.assert200(rv) self.assertEqual(answer_uuid2, rv.json['id'])
def test_delete_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): comment = self.data.get_answer_comments_by_assignment(self.assignment)[0] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) # test login required rv = self.client.delete(url) self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.delete(url) self.assert403(rv) # test invalid comment id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid="999") rv = self.client.delete(invalid_url) self.assert404(rv) # test authorized instructor rv = self.client.delete(url) self.assert200(rv) self.assertEqual(comment.uuid, rv.json['id']) # test author with self.login(self.data.get_extra_student(1).username): comment = self.data.get_answer_comments_by_assignment(self.assignment)[1] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.delete(url) self.assert200(rv) self.assertEqual(comment.uuid, rv.json['id']) # test delete self-evaulation answer = self.answers[self.assignment.id][0] self_evaluation = self.data.create_answer_comment(answer.user, answer, comment_type=AnswerCommentType.self_evaluation) self.assignment.calculate_grade(answer.user) self.course.calculate_grade(answer.user) lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( answer.user, self.course, self.assignment) with self.login(self.data.get_authorized_instructor().username): course_grade = CourseGrade.get_user_course_grade(self.course, answer.user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user).grade url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) rv = self.client.delete(url) self.assert200(rv) self.assertEqual(self_evaluation.uuid, rv.json['id']) # grades should decrease new_course_grade = CourseGrade.get_user_course_grade(self.course, answer.user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user) self.assertLess(new_course_grade.grade, course_grade) self.assertLess(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock()
def test_edit_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): comment = self.data.get_answer_comments_by_assignment(self.assignment)[0] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid ) content = { 'id': comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value } draft_comment = self.data.get_answer_comments_by_assignment(self.assignment)[2] draft_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=draft_comment.uuid) draft_content = { 'id': draft_comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value, 'draft': True } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999", answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid comment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test unmatched comment ids invalid = content.copy() invalid['id'] = self.data.get_answer_comments_by_assignment(self.assignment)[1].uuid rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Comment id does not match URL.", rv.json['error']) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) self.assertEqual("The comment content is empty!", rv.json['error']) # test empty comment_type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized instructor with self.login(self.data.get_authorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) # test author with self.login(self.data.get_extra_student(0).username): content['content'] = 'I am the author' rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) # ignored setting draft to True when draft is already False content['draft'] = True rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) # test draft author with self.login(self.data.get_extra_student(1).username): draft_content['content'] = 'I am the author' rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(draft_content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) # can change draft to False when draft is True draft_content['draft'] = False rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) answer = self.answers[self.assignment.id][0] self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) self_evaluation_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) with self.login(answer.user.username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( answer.user, self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade(self.course, answer.user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user).grade content = { 'id': self_evaluation.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.self_evaluation.value, 'draft': True } rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) # grades should not change new_course_grade = CourseGrade.get_user_course_grade(self.course, answer.user).grade new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user).grade self.assertEqual(new_course_grade, course_grade) self.assertEqual(new_assignment_grade, assignment_grade) # can change draft to False when draft is True content['draft'] = False rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) # grades should increase new_course_grade = CourseGrade.get_user_course_grade(self.course, answer.user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock()
def test_create_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) content = { 'comment_type': AnswerCommentType.private.value, 'content': 'great answer' } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid assignment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid="999", answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test empty comment type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized user rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) # test authorized user draft draft_content = content.copy() draft_content['draft'] = True rv = self.client.post(url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) # test authorized user draft - empty content empty = draft_content.copy() empty['content'] = None rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert200(rv) self.assertEqual(empty['content'], rv.json['content']) self.assertTrue(rv.json['draft']) with self.login(self.data.get_authorized_student().username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade(self.course, self.data.get_authorized_student()).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, self.data.get_authorized_student()).grade content = { 'comment_type': AnswerCommentType.self_evaluation.value, 'content': 'great answer' } rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) # grades should increase new_course_grade = CourseGrade.get_user_course_grade(self.course, self.data.get_authorized_student()) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, self.data.get_authorized_student()) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_assignment_grades_run.reset_mock()
def test_get_and_submit_comparison(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) users = [self.data.get_authorized_student(), self.data.get_authorized_instructor(), self.data.get_authorized_ta()] for user in users: compared_answer_uuids = set() valid_answer_uuids = set() for answer in self.data.get_student_answers(): if answer.assignment.id == self.assignment.id and answer.user_id != user.id: valid_answer_uuids.add(answer.uuid) if user.id == self.data.get_authorized_student().id: for comparison_example in self.data.comparisons_examples: if comparison_example.assignment_id == self.assignment.id: valid_answer_uuids.add(comparison_example.answer1_uuid) valid_answer_uuids.add(comparison_example.answer2_uuid) with self.login(user.username): if user.id in [self.data.get_authorized_instructor().id, self.data.get_authorized_ta().id]: self.assignment.educators_can_compare = False db.session.commit() # cannot compare answers unless educators_can_compare is set for assignment rv = self.client.get(self.base_url) self.assert403(rv) self.assignment.educators_can_compare = True db.session.commit() current = 0 while len(valid_answer_uuids - compared_answer_uuids) > 0: current += 1 if user.id == self.data.get_authorized_student().id: course_grade = CourseGrade.get_user_course_grade(self.course, user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, user).grade # establish expected data by first getting an answer pair rv = self.client.get(self.base_url) self.assert200(rv) actual_answer1_uuid = rv.json['objects'][0]['answer1_id'] actual_answer2_uuid = rv.json['objects'][0]['answer2_id'] self.assertIn(actual_answer1_uuid, valid_answer_uuids) self.assertIn(actual_answer2_uuid, valid_answer_uuids) self.assertNotEqual(actual_answer1_uuid, actual_answer2_uuid) self.assertTrue(rv.json['new_pair']) self.assertEqual(rv.json['current'], current) # fetch again rv = self.client.get(self.base_url) self.assert200(rv) expected_comparisons = rv.json self.assertEqual(actual_answer1_uuid, rv.json['objects'][0]['answer1_id']) self.assertEqual(actual_answer2_uuid, rv.json['objects'][0]['answer2_id']) self.assertFalse(rv.json['new_pair']) self.assertEqual(rv.json['current'], current) # test draft post comparison_submit = self._build_comparison_submit(rv.json['objects'][0]['answer1_id'], True) rv = self.client.post( self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert200(rv) actual_comparisons = rv.json['objects'] self._validate_comparison_submit(comparison_submit, actual_comparisons, expected_comparisons) # test draft post (no answer id) comparison_submit = self._build_comparison_submit(None) rv = self.client.post( self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert200(rv) actual_comparisons = rv.json['objects'] self._validate_comparison_submit(comparison_submit, actual_comparisons, expected_comparisons) # test normal post comparison_submit = self._build_comparison_submit(rv.json['objects'][0]['answer1_id']) rv = self.client.post( self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert200(rv) actual_comparisons = rv.json['objects'] compared_answer_uuids.add(actual_comparisons[0]['answer1_id']) compared_answer_uuids.add(actual_comparisons[0]['answer2_id']) self._validate_comparison_submit(comparison_submit, actual_comparisons, expected_comparisons) # grades should increase for every comparison if user.id == self.data.get_authorized_student().id: new_course_grade = CourseGrade.get_user_course_grade(self.course, user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, user) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock() mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() else: new_course_grade = CourseGrade.get_user_course_grade(self.course, user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, user) self.assertIsNone(new_course_grade) self.assertIsNone(new_assignment_grade) mocked_update_assignment_grades_run.assert_not_called() mocked_update_course_grades_run.assert_not_called() # Resubmit of same comparison should fail rv = self.client.post( self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert400(rv) # all answers has been compared by the user, errors out when trying to get another pair rv = self.client.get(self.base_url) self.assert400(rv)
def test_delete_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): comment = self.data.get_answer_comments_by_assignment( self.assignment)[0] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) # test login required rv = self.client.delete(url) self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.delete(url) self.assert403(rv) # test invalid comment id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid="999") rv = self.client.delete(invalid_url) self.assert404(rv) # test authorized instructor rv = self.client.delete(url) self.assert200(rv) self.assertEqual(comment.uuid, rv.json['id']) # test author with impersonation student = self.data.get_extra_student(1) with self.impersonate(self.data.get_authorized_instructor(), student): url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.delete(url) self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # test author with self.login(self.data.get_extra_student(1).username): comment = self.data.get_answer_comments_by_assignment( self.assignment)[1] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.delete(url) self.assert200(rv) self.assertEqual(comment.uuid, rv.json['id']) # test delete self-evaulation with impersonation answers = self.answers[self.assignment.id] for answer in [ a for a in answers if a.user.system_role == SystemRole.student ]: self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) with self.impersonate(self.data.get_authorized_instructor(), answer.user): url = self.get_url(course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) rv = self.client.delete(url) self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # test delete self-evaulation answer = self.answers[self.assignment.id][0] self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation) self.assignment.calculate_grade(answer.user) self.course.calculate_grade(answer.user) lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( answer.user, self.course, self.assignment) with self.login(self.data.get_authorized_instructor().username): course_grade = CourseGrade.get_user_course_grade( self.course, answer.user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user).grade url = self.get_url(course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) rv = self.client.delete(url) self.assert200(rv) self.assertEqual(self_evaluation.uuid, rv.json['id']) # grades should decrease new_course_grade = CourseGrade.get_user_course_grade( self.course, answer.user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user) self.assertLess(new_course_grade.grade, course_grade) self.assertLess(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [[ lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id ]]) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [[ lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id ]]) mocked_update_course_grades_run.reset_mock()
def test_edit_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): comment = self.data.get_answer_comments_by_assignment( self.assignment)[0] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) content = { 'id': comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value } draft_comment = self.data.get_answer_comments_by_assignment( self.assignment)[2] draft_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=draft_comment.uuid) draft_content = { 'id': draft_comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value, 'draft': True } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url(course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999", answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid comment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test unmatched comment ids invalid = content.copy() invalid['id'] = self.data.get_answer_comments_by_assignment( self.assignment)[1].uuid rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual( "The feedback's ID does not match the URL, which is required in order to save the feedback.", rv.json['message']) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual( "Please provide content in the text editor and try saving again.", rv.json['message']) # test empty comment_type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized instructor with self.login(self.data.get_authorized_instructor().username): with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # test author with self.login(self.data.get_extra_student(0).username): # test student can not change comment to self-eval / eval invalid = content.copy() invalid['comment_type'] = AnswerCommentType.self_evaluation.value rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual( "Feedback type cannot be changed. Please contact support for assistance.", rv.json['message']) invalid = content.copy() invalid['comment_type'] = AnswerCommentType.evaluation.value rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual( "Feedback type cannot be changed. Please contact support for assistance.", rv.json['message']) with mail.record_messages() as outbox: content['content'] = 'I am the author' rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # ignored setting draft to True when draft is already False with mail.record_messages() as outbox: content['draft'] = True rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # test author with impersonation student = self.data.get_extra_student(0) with self.impersonate(self.data.get_authorized_instructor(), student): content['content'] = 'I am the author' rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) content['draft'] = True rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # test draft author with self.login(self.data.get_extra_student(1).username): with mail.record_messages() as outbox: draft_content['content'] = 'I am the author' rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(draft_content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # can change draft to False when draft is True with mail.record_messages() as outbox: draft_content['draft'] = False rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 1) self.assertEqual( outbox[0].subject, "New Answer Feedback in " + self.data.get_course().name) self.assertEqual( outbox[0].recipients, [self.answers[self.assignment.id][0].user.email]) # test draft author with impersonation student = self.data.get_extra_student(1) with self.impersonate(self.data.get_authorized_instructor(), student): draft_content['content'] = 'I am the author' rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # cant change draft to False draft_content['draft'] = False rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) answer = self.answers[self.assignment.id][0] self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) self_evaluation_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) with self.login(answer.user.username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( answer.user, self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade( self.course, answer.user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user).grade content = { 'id': self_evaluation.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.self_evaluation.value, 'draft': True } # test student can not submit self-eval after self-eval grace period orig_answer_end = self.assignment.answer_end self.assignment.answer_end = datetime.datetime.utcnow( ) - datetime.timedelta(hours=12) self.assignment.self_eval_start = datetime.datetime.utcnow( ) - datetime.timedelta(hours=1) self.assignment.self_eval_end = datetime.datetime.utcnow( ) - datetime.timedelta(minutes=10) db.session.add(self.assignment) db.session.commit() rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertEqual("Self-Evaluation Not Saved", rv.json['title']) self.assertEqual( "Sorry, the self-evaluation deadline has passed and therefore cannot be submitted.", rv.json['message']) self.assignment.answer_end = orig_answer_end self.assignment.self_eval_start = None self.assignment.self_eval_end = None with mail.record_messages() as outbox: self.assignment.answer_end = datetime.datetime.utcnow( ) - datetime.timedelta(hours=12) rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # grades should not change new_course_grade = CourseGrade.get_user_course_grade( self.course, answer.user).grade new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user).grade self.assertEqual(new_course_grade, course_grade) self.assertEqual(new_assignment_grade, assignment_grade) # can change draft to False when draft is True with mail.record_messages() as outbox: content['draft'] = False rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # grades should increase new_course_grade = CourseGrade.get_user_course_grade( self.course, answer.user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [[ lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id ]]) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [[ lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id ]]) mocked_update_course_grades_run.reset_mock() # test self-evaluation with impersonation answers = self.answers[self.assignment.id] for answer in [ a for a in answers if a.user.system_role == SystemRole.student ]: self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) self_evaluation_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) with self.impersonate(self.data.get_authorized_instructor(), answer.user): content = { 'id': self_evaluation.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.self_evaluation.value, 'draft': True } rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # attempt to change draft to False content['draft'] = False rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation'])
def test_create_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) content = { 'comment_type': AnswerCommentType.private.value, 'content': 'great answer' } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid assignment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid="999", answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url(course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test empty comment type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized user with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 1) self.assertEqual( outbox[0].subject, "New Answer Feedback in " + self.data.get_course().name) self.assertEqual( outbox[0].recipients, [self.answers[self.assignment.id][0].user.email]) # test authorized user draft with mail.record_messages() as outbox: draft_content = content.copy() draft_content['draft'] = True rv = self.client.post(url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) # test authorized user draft - empty content with mail.record_messages() as outbox: empty = draft_content.copy() empty['content'] = None rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert200(rv) self.assertEqual(empty['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) with self.login('root'): with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(len(outbox), 1) self.assertIn('fullname', rv.json['user']) with self.login(self.data.get_authorized_student().username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade( self.course, self.data.get_authorized_student()).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, self.data.get_authorized_student()).grade content = { 'comment_type': AnswerCommentType.self_evaluation.value, 'content': 'great answer' } # test student can not submit self-eval after self-eval grace period orig_answer_end = self.assignment.answer_end self.assignment.answer_end = datetime.datetime.utcnow( ) - datetime.timedelta(hours=12) self.assignment.self_eval_start = datetime.datetime.utcnow( ) - datetime.timedelta(hours=1) self.assignment.self_eval_end = datetime.datetime.utcnow( ) - datetime.timedelta(minutes=10) db.session.add(self.assignment) db.session.commit() rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertEqual("Self-Evaluation Not Saved", rv.json['title']) self.assertEqual( "Sorry, the self-evaluation deadline has passed and therefore cannot be submitted.", rv.json['message']) self.assignment.answer_end = orig_answer_end self.assignment.self_eval_start = None self.assignment.self_eval_end = None with mail.record_messages() as outbox: orig_answer_end = self.assignment.answer_end self.assignment.answer_end = datetime.datetime.utcnow( ) - datetime.timedelta(hours=12) rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(len(outbox), 0) self.assertNotIn('fullname', rv.json['user']) # grades should increase new_course_grade = CourseGrade.get_user_course_grade( self.course, self.data.get_authorized_student()) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, self.data.get_authorized_student()) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [[ lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id ]]) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [[ lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id ]]) mocked_update_assignment_grades_run.reset_mock() self.assignment.answer_end = orig_answer_end # test with impersonation student = self.data.get_extra_student(0) with self.impersonate(self.data.get_authorized_instructor(), student): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade( self.course, self.data.get_authorized_student()).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, self.data.get_authorized_student()).grade content = { 'comment_type': AnswerCommentType.self_evaluation.value, 'content': 'great answer' } with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation'])
def test_edit_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): comment = self.data.get_answer_comments_by_assignment( self.assignment)[0] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) content = { 'id': comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value } draft_comment = self.data.get_answer_comments_by_assignment( self.assignment)[2] draft_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=draft_comment.uuid) draft_content = { 'id': draft_comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value, 'draft': True } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url(course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999", answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid comment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test unmatched comment ids invalid = content.copy() invalid['id'] = self.data.get_answer_comments_by_assignment( self.assignment)[1].uuid rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Reply Not Saved", rv.json['title']) self.assertEqual( "The reply's ID does not match the URL, which is required in order to save the reply.", rv.json['message']) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) self.assertEqual("Reply Not Saved", rv.json['title']) self.assertEqual( "Please provide content in the text editor to reply and try saving again.", rv.json['message']) # test empty comment_type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized instructor with self.login(self.data.get_authorized_instructor().username): with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertEqual(len(outbox), 0) # test author with self.login(self.data.get_extra_student(0).username): with mail.record_messages() as outbox: content['content'] = 'I am the author' rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertEqual(len(outbox), 0) # ignored setting draft to True when draft is already False with mail.record_messages() as outbox: content['draft'] = True rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertEqual(len(outbox), 0) # test draft author with self.login(self.data.get_extra_student(1).username): with mail.record_messages() as outbox: draft_content['content'] = 'I am the author' rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(draft_content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) # can change draft to False when draft is True with mail.record_messages() as outbox: draft_content['draft'] = False rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) self.assertEqual(len(outbox), 1) self.assertEqual( outbox[0].subject, "New Answer Feedback in " + self.data.get_course().name) self.assertEqual( outbox[0].recipients, [self.answers[self.assignment.id][0].user.email]) answer = self.answers[self.assignment.id][0] self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) self_evaluation_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) with self.login(answer.user.username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( answer.user, self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade( self.course, answer.user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user).grade content = { 'id': self_evaluation.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.self_evaluation.value, 'draft': True } with mail.record_messages() as outbox: rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) # grades should not change new_course_grade = CourseGrade.get_user_course_grade( self.course, answer.user).grade new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user).grade self.assertEqual(new_course_grade, course_grade) self.assertEqual(new_assignment_grade, assignment_grade) # can change draft to False when draft is True with mail.record_messages() as outbox: content['draft'] = False rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) self.assertEqual(len(outbox), 0) # grades should increase new_course_grade = CourseGrade.get_user_course_grade( self.course, answer.user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, answer.user) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)]) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)]) mocked_update_course_grades_run.reset_mock()
def test_create_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) content = { 'comment_type': AnswerCommentType.private.value, 'content': 'great answer' } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid assignment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid="999", answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url(course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test empty comment type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized user with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertEqual(len(outbox), 1) self.assertEqual( outbox[0].subject, "New Answer Feedback in " + self.data.get_course().name) self.assertEqual( outbox[0].recipients, [self.answers[self.assignment.id][0].user.email]) # test authorized user draft with mail.record_messages() as outbox: draft_content = content.copy() draft_content['draft'] = True rv = self.client.post(url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) # test authorized user draft - empty content with mail.record_messages() as outbox: empty = draft_content.copy() empty['content'] = None rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert200(rv) self.assertEqual(empty['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) with self.login('root'): with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(len(outbox), 1) with self.login(self.data.get_authorized_student().username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade( self.course, self.data.get_authorized_student()).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, self.data.get_authorized_student()).grade content = { 'comment_type': AnswerCommentType.self_evaluation.value, 'content': 'great answer' } with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(len(outbox), 0) # grades should increase new_course_grade = CourseGrade.get_user_course_grade( self.course, self.data.get_authorized_student()) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, self.data.get_authorized_student()) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)]) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)]) mocked_update_assignment_grades_run.reset_mock()
def test_edit_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): comment = self.data.get_answer_comments_by_assignment(self.assignment)[0] url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid ) content = { 'id': comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value } draft_comment = self.data.get_answer_comments_by_assignment(self.assignment)[2] draft_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=draft_comment.uuid) draft_content = { 'id': draft_comment.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.private.value, 'draft': True } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999", answer_comment_uuid=comment.uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid comment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid, answer_comment_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test unmatched comment ids invalid = content.copy() invalid['id'] = self.data.get_answer_comments_by_assignment(self.assignment)[1].uuid rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual("The feedback's ID does not match the URL, which is required in order to save the feedback.", rv.json['message']) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual("Please provide content in the text editor and try saving again.", rv.json['message']) # test empty comment_type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized instructor with self.login(self.data.get_authorized_instructor().username): with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # test author with self.login(self.data.get_extra_student(0).username): # test student can not change comment to self-eval / eval invalid = content.copy() invalid['comment_type'] = AnswerCommentType.self_evaluation.value rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual("Feedback type cannot be changed. Please contact support for assistance.", rv.json['message']) invalid = content.copy() invalid['comment_type'] = AnswerCommentType.evaluation.value rv = self.client.post(url, data=json.dumps(invalid), content_type='application/json') self.assert400(rv) self.assertEqual("Feedback Not Saved", rv.json['title']) self.assertEqual("Feedback type cannot be changed. Please contact support for assistance.", rv.json['message']) with mail.record_messages() as outbox: content['content'] = 'I am the author' rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # ignored setting draft to True when draft is already False with mail.record_messages() as outbox: content['draft'] = True rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # test author with impersonation student = self.data.get_extra_student(0) with self.impersonate(self.data.get_authorized_instructor(), student): content['content'] = 'I am the author' rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) content['draft'] = True rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # test draft author with self.login(self.data.get_extra_student(1).username): with mail.record_messages() as outbox: draft_content['content'] = 'I am the author' rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(draft_content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # can change draft to False when draft is True with mail.record_messages() as outbox: draft_content['draft'] = False rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 1) self.assertEqual(outbox[0].subject, "New Answer Feedback in "+self.data.get_course().name) self.assertEqual(outbox[0].recipients, [self.answers[self.assignment.id][0].user.email]) # test draft author with impersonation student = self.data.get_extra_student(1) with self.impersonate(self.data.get_authorized_instructor(), student): draft_content['content'] = 'I am the author' rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # cant change draft to False draft_content['draft'] = False rv = self.client.post(draft_url, data=json.dumps(draft_content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) answer = self.answers[self.assignment.id][0] self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) self_evaluation_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) with self.login(answer.user.username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( answer.user, self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade(self.course, answer.user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user).grade content = { 'id': self_evaluation.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.self_evaluation.value, 'draft': True } # test student can not submit self-eval after self-eval grace period orig_answer_end = self.assignment.answer_end self.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(hours=12) self.assignment.self_eval_start = datetime.datetime.utcnow() - datetime.timedelta(hours=1) self.assignment.self_eval_end = datetime.datetime.utcnow() - datetime.timedelta(minutes=10) db.session.add(self.assignment) db.session.commit() rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertEqual("Self-Evaluation Not Saved", rv.json['title']) self.assertEqual("Sorry, the self-evaluation deadline has passed and therefore cannot be submitted.", rv.json['message']) self.assignment.answer_end = orig_answer_end self.assignment.self_eval_start = None self.assignment.self_eval_end = None with mail.record_messages() as outbox: self.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(hours=12) rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # grades should not change new_course_grade = CourseGrade.get_user_course_grade(self.course, answer.user).grade new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user).grade self.assertEqual(new_course_grade, course_grade) self.assertEqual(new_assignment_grade, assignment_grade) # can change draft to False when draft is True with mail.record_messages() as outbox: content['draft'] = False rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['draft']) self.assertNotIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 0) # grades should increase new_course_grade = CourseGrade.get_user_course_grade(self.course, answer.user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, answer.user) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock() # test self-evaluation with impersonation answers = self.answers[self.assignment.id] for answer in [a for a in answers if a.user.system_role == SystemRole.student]: self_evaluation = self.data.create_answer_comment( answer.user, answer, comment_type=AnswerCommentType.self_evaluation, draft=True) self_evaluation_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=answer.uuid, answer_comment_uuid=self_evaluation.uuid) with self.impersonate(self.data.get_authorized_instructor(), answer.user): content = { 'id': self_evaluation.uuid, 'content': 'insightful.', 'comment_type': AnswerCommentType.self_evaluation.value, 'draft': True } rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation']) # attempt to change draft to False content['draft'] = False rv = self.client.post(self_evaluation_url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation'])
def test_create_answer_comment(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) content = { 'comment_type': AnswerCommentType.private.value, 'content': 'great answer' } # test login required rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.data.get_authorized_instructor().username): invalid_url = self.get_url( course_uuid="999", assignment_uuid=self.assignment.uuid, answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid assignment id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid="999", answer_uuid=self.answers[self.assignment.id][0].uuid) rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test invalid answer id invalid_url = self.get_url( course_uuid=self.course.uuid, assignment_uuid=self.assignment.uuid, answer_uuid="999") rv = self.client.post(invalid_url, data=json.dumps(content), content_type='application/json') self.assert404(rv) # test empty content empty = content.copy() empty['content'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test empty comment type empty = content.copy() empty['comment_type'] = '' rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert400(rv) # test authorized user with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertFalse(rv.json['draft']) self.assertIn('fullname', rv.json['user']) self.assertEqual(len(outbox), 1) self.assertEqual(outbox[0].subject, "New Answer Feedback in "+self.data.get_course().name) self.assertEqual(outbox[0].recipients, [self.answers[self.assignment.id][0].user.email]) # test authorized user draft with mail.record_messages() as outbox: draft_content = content.copy() draft_content['draft'] = True rv = self.client.post(url, data=json.dumps(draft_content), content_type='application/json') self.assert200(rv) self.assertEqual(content['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) # test authorized user draft - empty content with mail.record_messages() as outbox: empty = draft_content.copy() empty['content'] = None rv = self.client.post(url, data=json.dumps(empty), content_type='application/json') self.assert200(rv) self.assertEqual(empty['content'], rv.json['content']) self.assertTrue(rv.json['draft']) self.assertEqual(len(outbox), 0) with self.login('root'): with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(len(outbox), 1) self.assertIn('fullname', rv.json['user']) with self.login(self.data.get_authorized_student().username): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade(self.course, self.data.get_authorized_student()).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, self.data.get_authorized_student()).grade content = { 'comment_type': AnswerCommentType.self_evaluation.value, 'content': 'great answer' } # test student can not submit self-eval after self-eval grace period orig_answer_end = self.assignment.answer_end self.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(hours=12) self.assignment.self_eval_start = datetime.datetime.utcnow() - datetime.timedelta(hours=1) self.assignment.self_eval_end = datetime.datetime.utcnow() - datetime.timedelta(minutes=10) db.session.add(self.assignment) db.session.commit() rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertEqual("Self-Evaluation Not Saved", rv.json['title']) self.assertEqual("Sorry, the self-evaluation deadline has passed and therefore cannot be submitted.", rv.json['message']) self.assignment.answer_end = orig_answer_end self.assignment.self_eval_start = None self.assignment.self_eval_end = None with mail.record_messages() as outbox: orig_answer_end = self.assignment.answer_end self.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(hours=12) rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert200(rv) self.assertEqual(len(outbox), 0) self.assertNotIn('fullname', rv.json['user']) # grades should increase new_course_grade = CourseGrade.get_user_course_grade(self.course, self.data.get_authorized_student()) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, self.data.get_authorized_student()) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() self.assignment.answer_end = orig_answer_end # test with impersonation student = self.data.get_extra_student(0) with self.impersonate(self.data.get_authorized_instructor(), student): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) course_grade = CourseGrade.get_user_course_grade(self.course, self.data.get_authorized_student()).grade assignment_grade = AssignmentGrade.get_user_assignment_grade(self.assignment, self.data.get_authorized_student()).grade content = { 'comment_type': AnswerCommentType.self_evaluation.value, 'content': 'great answer' } with mail.record_messages() as outbox: rv = self.client.post(url, data=json.dumps(content), content_type='application/json') self.assert403(rv) self.assertTrue(rv.json['disabled_by_impersonation'])
def test_edit_answer(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): assignment_uuid = self.fixtures.assignments[0].uuid answer = self.fixtures.answers[0] expected = {'id': answer.uuid, 'content': 'This is an edit'} draft_answer = self.fixtures.draft_answers[0] draft_expected = {'id': draft_answer.uuid, 'content': 'This is an edit', 'draft': True} # test login required rv = self.client.post( self.base_url + '/' + answer.uuid, data=json.dumps(expected), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.fixtures.students[1].username): rv = self.client.post( self.base_url + '/' + answer.uuid, data=json.dumps(expected), content_type='application/json') self.assert403(rv) # test invalid course id with self.login(self.fixtures.students[0].username): rv = self.client.post( self._build_url("999", assignment_uuid, '/' + answer.uuid), data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test invalid assignment id rv = self.client.post( self._build_url(self.fixtures.course.uuid, "999", '/' + answer.uuid), data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test invalid answer id rv = self.client.post( self.base_url + '/999', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test unmatched answer id with self.login(self.fixtures.students[1].username): rv = self.client.post( self.base_url + '/' + self.fixtures.answers[1].uuid, data=json.dumps(expected), content_type='application/json') self.assert400(rv) with self.login(self.fixtures.draft_student.username): course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.draft_student).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.draft_student).grade lti_consumer = self.lti_data.lti_consumer student = self.fixtures.draft_student (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( student, self.fixtures.course, self.fixtures.assignment) # test edit draft by author rv = self.client.post( self.base_url + '/' + draft_answer.uuid, data=json.dumps(draft_expected), content_type='application/json') self.assert200(rv) self.assertEqual(draft_answer.uuid, rv.json['id']) self.assertEqual('This is an edit', rv.json['content']) self.assertEqual(draft_answer.draft, rv.json['draft']) self.assertTrue(rv.json['draft']) # grades should not change new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.draft_student).grade new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.draft_student).grade self.assertEqual(new_course_grade, course_grade) self.assertEqual(new_assignment_grade, assignment_grade) mocked_update_assignment_grades_run.assert_not_called() mocked_update_course_grades_run.assert_not_called() # set draft to false draft_expected_copy = draft_expected.copy() draft_expected_copy['draft'] = False rv = self.client.post( self.base_url + '/' + draft_answer.uuid, data=json.dumps(draft_expected_copy), content_type='application/json') self.assert200(rv) self.assertEqual(draft_answer.uuid, rv.json['id']) self.assertEqual('This is an edit', rv.json['content']) self.assertEqual(draft_answer.draft, rv.json['draft']) self.assertFalse(rv.json['draft']) # grades should increase new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.draft_student) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.draft_student) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock() # setting draft to true when false should not work rv = self.client.post( self.base_url + '/' + draft_answer.uuid, data=json.dumps(draft_expected), content_type='application/json') self.assert200(rv) self.assertEqual(draft_answer.uuid, rv.json['id']) self.assertEqual('This is an edit', rv.json['content']) self.assertEqual(draft_answer.draft, rv.json['draft']) self.assertFalse(rv.json['draft']) # test edit by author with self.login(self.fixtures.students[0].username): rv = self.client.post( self.base_url + '/' + answer.uuid, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertEqual(answer.uuid, rv.json['id']) self.assertEqual('This is an edit', rv.json['content']) # test edit by user that can manage posts manage_expected = { 'id': answer.uuid, 'content': 'This is another edit' } with self.login(self.fixtures.instructor.username): rv = self.client.post( self.base_url + '/' + answer.uuid, data=json.dumps(manage_expected), content_type='application/json') self.assert200(rv) self.assertEqual(answer.uuid, rv.json['id']) self.assertEqual('This is another edit', rv.json['content']) # test edit by author with self.login(self.fixtures.students[0].username): # test student can not submit answers after answer grace period self.fixtures.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(minutes=10) db.session.add(self.fixtures.assignment) db.session.commit() rv = self.client.post( self.base_url + '/' + answer.uuid, data=json.dumps(expected), content_type='application/json') self.assert403(rv) self.assertEqual("Answer Not Submitted", rv.json['title']) self.assertEqual("Sorry, the answer deadline has passed. No answers can be submitted after the deadline unless the instructor submits the answer for you.", rv.json['message']) # test student can submit answers within answer grace period self.fixtures.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(seconds=15) db.session.add(self.fixtures.assignment) db.session.commit() rv = self.client.post( self.base_url + '/' + answer.uuid, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertEqual(answer.uuid, rv.json['id']) self.assertEqual('This is an edit', rv.json['content'])
def test_create_answer(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): # test login required expected_answer = {'content': 'this is some answer content'} rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert401(rv) # test unauthorized users with self.login(self.fixtures.unauthorized_student.username): rv = self.client.post(self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert403(rv) with self.login(self.fixtures.unauthorized_instructor.username): rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert403(rv) # test invalid format with self.login(self.fixtures.students[0].username): invalid_answer = {'post': {'blah': 'blah'}} rv = self.client.post( self.base_url, data=json.dumps(invalid_answer), content_type='application/json') self.assert400(rv) # test invalid assignment rv = self.client.post( self._build_url(self.fixtures.course.uuid, "9392402"), data=json.dumps(expected_answer), content_type='application/json') self.assert404(rv) # test invalid course rv = self.client.post( self._build_url("9392402", self.fixtures.assignment.uuid), data=json.dumps(expected_answer), content_type='application/json') self.assert404(rv) # test create successful with self.login(self.fixtures.instructor.username): rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) # retrieve again and verify actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # user should not have grades new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.instructor) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.fixtures.assignment, self.fixtures.instructor) self.assertIsNone(new_course_grade) self.assertIsNone(new_assignment_grade) # test instructor could submit multiple answers for his/her own rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # user should not have grades new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.instructor) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.fixtures.assignment, self.fixtures.instructor) self.assertIsNone(new_course_grade) self.assertIsNone(new_assignment_grade) # test instructor could submit multiple answers for his/her own expected_answer.update({'user_id': self.fixtures.instructor.uuid}) rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # user should not have grades new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.instructor) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.fixtures.assignment, self.fixtures.instructor) self.assertIsNone(new_course_grade) self.assertIsNone(new_assignment_grade) # test instructor could submit on behave of a student self.fixtures.add_students(1) expected_answer.update({'user_id': self.fixtures.students[-1].uuid}) rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # user should have grades new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.students[-1]) new_assignment_grade = AssignmentGrade.get_user_assignment_grade(self.fixtures.assignment, self.fixtures.students[-1]) self.assertIsNotNone(new_course_grade) self.assertIsNotNone(new_assignment_grade) # test instructor can not submit additional answers for a student expected_answer.update({'user_id': self.fixtures.students[0].uuid}) rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert400(rv) self.assertEqual(rv.json['title'], "Answer Not Submitted") self.assertEqual(rv.json['message'], "An answer has already been submitted for this assignment by you or on your behalf.") self.fixtures.add_students(1) self.fixtures.course.calculate_grade(self.fixtures.students[-1]) self.fixtures.assignment.calculate_grade(self.fixtures.students[-1]) expected_answer = {'content': 'this is some answer content', 'draft': True} with self.login(self.fixtures.students[-1].username): course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.students[-1]).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.students[-1]).grade # test create draft successful rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) self.assertEqual(expected_answer['draft'], actual_answer.draft) # grades should not change new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.draft_student).grade new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignments[0], self.fixtures.draft_student).grade self.assertEqual(new_course_grade, course_grade) self.assertEqual(new_assignment_grade, assignment_grade) with self.login(self.fixtures.instructor.username): # test instructor can submit outside of grace period self.fixtures.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(minutes=2) db.session.add(self.fixtures.assignment) db.session.commit() self.fixtures.add_students(1) expected_answer.update({'user_id': self.fixtures.students[-1].uuid}) rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # test create successful self.fixtures.add_students(1) self.fixtures.course.calculate_grade(self.fixtures.students[-1]) self.fixtures.assignment.calculate_grade(self.fixtures.students[-1]) expected_answer = {'content': 'this is some answer content'} with self.login(self.fixtures.students[-1].username): # test student can not submit answers after answer grace period self.fixtures.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(minutes=2) db.session.add(self.fixtures.assignment) db.session.commit() rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert403(rv) self.assertEqual("Answer Not Submitted", rv.json['title']) self.assertEqual("Sorry, the answer deadline has passed. No answers can be submitted after the deadline unless the instructor submits the answer for you.", rv.json['message']) # test student can submit answers within answer grace period self.fixtures.assignment.answer_end = datetime.datetime.utcnow() - datetime.timedelta(seconds=15) db.session.add(self.fixtures.assignment) db.session.commit() course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.students[-1]).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignment, self.fixtures.students[-1]).grade lti_consumer = self.lti_data.lti_consumer student = self.fixtures.students[-1] (lti_user_resource_link1, lti_user_resource_link2) = self.lti_data.setup_student_user_resource_links( student, self.fixtures.course, self.fixtures.assignment) rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # grades should increase new_course_grade = CourseGrade.get_user_course_grade( self.fixtures.course, self.fixtures.students[-1]) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.fixtures.assignment, self.fixtures.students[-1]) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)] ) mocked_update_assignment_grades_run.reset_mock() mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)] ) mocked_update_course_grades_run.reset_mock() # test create successful for system admin with self.login('root'): rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) # retrieve again and verify actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content) # test system admin could submit multiple answers for his/her own rv = self.client.post( self.base_url, data=json.dumps(expected_answer), content_type='application/json') self.assert200(rv) actual_answer = Answer.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(expected_answer['content'], actual_answer.content)
def test_get_and_submit_comparison(self, mocked_update_assignment_grades_run, mocked_update_course_grades_run): lti_consumer = self.lti_data.lti_consumer (lti_user_resource_link1, lti_user_resource_link2 ) = self.lti_data.setup_student_user_resource_links( self.data.get_authorized_student(), self.course, self.assignment) users = [ self.data.get_authorized_student(), self.data.get_authorized_instructor(), self.data.get_authorized_ta() ] for user in users: max_comparisons = 0 other_student_answers = 0 valid_answer_uuids = set() for answer in self.data.get_student_answers(): if answer.assignment.id == self.assignment.id and answer.user_id != user.id: other_student_answers += 1 valid_answer_uuids.add(answer.uuid) max_comparisons = int(other_student_answers * (other_student_answers - 1) / 2) if user.id == self.data.get_authorized_student().id: for comparison_example in self.data.comparisons_examples: if comparison_example.assignment_id == self.assignment.id: max_comparisons += 1 valid_answer_uuids.add(comparison_example.answer1_uuid) valid_answer_uuids.add(comparison_example.answer2_uuid) with self.login(user.username): if user.id in [ self.data.get_authorized_instructor().id, self.data.get_authorized_ta().id ]: self.assignment.educators_can_compare = False db.session.commit() # cannot compare answers unless educators_can_compare is set for assignment rv = self.client.get(self.base_url) self.assert403(rv) self.assignment.educators_can_compare = True db.session.commit() current = 0 while current < max_comparisons: current += 1 if user.id == self.data.get_authorized_student().id: course_grade = CourseGrade.get_user_course_grade( self.course, user).grade assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, user).grade # establish expected data by first getting an answer pair rv = self.client.get(self.base_url) self.assert200(rv) actual_answer1_uuid = rv.json['comparison']['answer1_id'] actual_answer2_uuid = rv.json['comparison']['answer2_id'] self.assertIn(actual_answer1_uuid, valid_answer_uuids) self.assertIn(actual_answer2_uuid, valid_answer_uuids) self.assertNotEqual(actual_answer1_uuid, actual_answer2_uuid) self.assertTrue(rv.json['new_pair']) self.assertEqual(rv.json['current'], current) # fetch again rv = self.client.get(self.base_url) self.assert200(rv) expected_comparison = rv.json['comparison'] self.assertEqual(actual_answer1_uuid, rv.json['comparison']['answer1_id']) self.assertEqual(actual_answer2_uuid, rv.json['comparison']['answer2_id']) self.assertFalse(rv.json['new_pair']) self.assertEqual(rv.json['current'], current) # test draft post comparison_submit = self._build_comparison_submit( WinningAnswer.answer1.value, True) rv = self.client.post(self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert200(rv) actual_comparison = rv.json['comparison'] self._validate_comparison_submit(comparison_submit, actual_comparison, expected_comparison) # test draft post (no winner) comparison_submit = self._build_comparison_submit(None) rv = self.client.post(self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert200(rv) actual_comparison = rv.json['comparison'] self._validate_comparison_submit(comparison_submit, actual_comparison, expected_comparison) # test normal post comparison_submit = self._build_comparison_submit( WinningAnswer.answer1.value) rv = self.client.post(self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert200(rv) actual_comparison = rv.json['comparison'] self._validate_comparison_submit(comparison_submit, actual_comparison, expected_comparison) # grades should increase for every comparison if user.id == self.data.get_authorized_student().id: new_course_grade = CourseGrade.get_user_course_grade( self.course, user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, user) self.assertGreater(new_course_grade.grade, course_grade) self.assertGreater(new_assignment_grade.grade, assignment_grade) mocked_update_course_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link1.lis_result_sourcedid, new_course_grade.id)]) mocked_update_course_grades_run.reset_mock() mocked_update_assignment_grades_run.assert_called_once_with( lti_consumer.id, [(lti_user_resource_link2.lis_result_sourcedid, new_assignment_grade.id)]) mocked_update_assignment_grades_run.reset_mock() else: new_course_grade = CourseGrade.get_user_course_grade( self.course, user) new_assignment_grade = AssignmentGrade.get_user_assignment_grade( self.assignment, user) self.assertIsNone(new_course_grade) self.assertIsNone(new_assignment_grade) mocked_update_assignment_grades_run.assert_not_called() mocked_update_course_grades_run.assert_not_called() # Resubmit of same comparison should fail rv = self.client.post(self.base_url, data=json.dumps(comparison_submit), content_type='application/json') self.assert400(rv) # all answers has been compared by the user, errors out when trying to get another pair rv = self.client.get(self.base_url) self.assert400(rv)