def setUp(self): super().setUp() dump_random_state() self.user = UserFactory(username=self.MOCK_USER_DATA["username"]) self.client.login(username=self.user.username, password=USER_PASSWORD) self.course = CourseFactory(site=self.site) self.course_runs = CourseRunFactory.create_batch(3, course=self.course) self.user_grade_low = UserGradeFactory( username=self.MOCK_USER_DATA["username"], course_run=self.course_runs[0], letter_grade="A", percent_grade=0.70, ) self.user_grade_high = UserGradeFactory( username=self.MOCK_USER_DATA["username"], course_run=self.course_runs[1], letter_grade="C", percent_grade=1.00, ) self.user_grade_revoked_cert = UserGradeFactory( username=self.MOCK_USER_DATA["username"], course_run=self.course_runs[2], letter_grade="B", percent_grade=0.80, ) self.course_certs = [ CourseCertificateFactory(course_id=course_run.key, site=self.site) for course_run in self.course_runs ] self.credential_content_type = ContentType.objects.get(app_label="credentials", model="coursecertificate") self.program_cert = ProgramCertificateFactory(site=self.site) self.program_content_type = ContentType.objects.get(app_label="credentials", model="programcertificate") self.user_credentials = [ UserCredentialFactory( username=self.MOCK_USER_DATA["username"], credential_content_type=self.credential_content_type, credential=course_cert, ) for course_cert in self.course_certs ] self.user_credentials[2].status = UserCredential.REVOKED self.user_credentials[2].save() self.org_names = ["CCC", "AAA", "BBB"] self.orgs = [OrganizationFactory(name=name, site=self.site) for name in self.org_names] self.program = ProgramFactory( course_runs=self.course_runs, authoring_organizations=self.orgs, site=self.site, uuid=self.program_cert.program_uuid, ) self.pcr = ProgramCertRecordFactory(program=self.program, user=self.user) self.pathway = PathwayFactory(site=self.site) self.pathway.programs.set([self.program])
def setUp(self): super().setUp() self.user = UserFactory(username=self.MOCK_USER_DATA['username']) self.client.login(username=self.user.username, password=USER_PASSWORD) self.course = CourseFactory(site=self.site) self.course_runs = [ CourseRunFactory(course=self.course) for _ in range(3) ] self.user_grade_low = UserGradeFactory( username=self.MOCK_USER_DATA['username'], course_run=self.course_runs[0], letter_grade='A', percent_grade=0.70) self.user_grade_high = UserGradeFactory( username=self.MOCK_USER_DATA['username'], course_run=self.course_runs[1], letter_grade='C', percent_grade=1.00) self.user_grade_revoked_cert = UserGradeFactory( username=self.MOCK_USER_DATA['username'], course_run=self.course_runs[2], letter_grade='B', percent_grade=.80) self.course_certs = [ CourseCertificateFactory(course_id=course_run.key, site=self.site) for course_run in self.course_runs ] self.credential_content_type = ContentType.objects.get( app_label='credentials', model='coursecertificate') self.user_credentials = [ UserCredentialFactory( username=self.MOCK_USER_DATA['username'], credential_content_type=self.credential_content_type, credential=course_cert) for course_cert in self.course_certs ] self.user_credentials[2].status = UserCredential.REVOKED self.org_names = ['CCC', 'AAA', 'BBB'] self.orgs = [ OrganizationFactory(name=name, site=self.site) for name in self.org_names ] self.program = ProgramFactory(course_runs=self.course_runs, authoring_organizations=self.orgs, site=self.site)
def test_grade_view_throttling(self): """ Verify requests are throttled and a message is logged after limit. """ with LogCapture() as log: course = CourseFactory(site=self.site) course_run = CourseRunFactory(course=course) data = { 'username': '******', 'course_run': course_run.key, 'letter_grade': 'A', 'percent_grade': 0.9, 'verified': True, } grade = UserGradeFactory( course_run=course_run, username=self.user.username, letter_grade='C', ) path = reverse('api:v2:grades-detail', kwargs={'pk': grade.id}) self.authenticate_user(self.user) self.add_user_permission(self.user, 'change_usergrade') # All requests up to limit should be acceptable self.hit_rate_limit('grade_view', path, data) # Request after limit should NOT be acceptable response = getattr(self.client, 'put')(path, data=data) self.assertEqual(response.status_code, 429) assert_log_correct( log, 'credentials.apps.api.v2.views', 'WARNING', 'Credentials API endpoint GradeViewSet is being throttled.', )
def test_course_run_order(self): """ Test that the course_runs are returned in the program order """ new_course_run = CourseRunFactory() self.program.course_runs.add(new_course_run) # pylint: disable=no-member UserGradeFactory(username=self.MOCK_USER_DATA['username'], course_run=new_course_run, letter_grade='C', percent_grade=.70) new_course_cert = CourseCertificateFactory( course_id=new_course_run.key, site=self.site) UserCredentialFactory( username=self.MOCK_USER_DATA['username'], credential_content_type=self.credential_content_type, credential=new_course_cert) response = self.client.get( reverse('records:programs', kwargs={'uuid': self.program.uuid.hex})) grades = json.loads(response.context_data['record'])['grades'] expected_course_run_keys = [ course_run.key for course_run in [self.course_runs[1], new_course_run] ] actual_course_run_keys = [grade['course_id'] for grade in grades] self.assertEqual(expected_course_run_keys, actual_course_run_keys)
def test_create_with_existing_user_grade(self): """ Verify that, if a user has already been issued a grade, further attempts to issue the same grade will NOT create a new grade, but update the fields of the existing grade. """ grade = UserGradeFactory(course_run=self.course_run) self.authenticate_user(self.user) self.add_user_permission(self.user, 'add_usergrade') # POSTing with modified data should update the existing UserGrade data = self.serialize_user_grade(grade) data['letter_grade'] = 'B' response = self.client.post(self.list_path, data=JSONRenderer().render(data), content_type=JSON_CONTENT_TYPE) self.assertEqual(response.status_code, 201) grade.refresh_from_db() self.assertEqual(grade.letter_grade, 'B') self.assertDictEqual(response.data, self.serialize_user_grade(grade))
def test_update(self, method): """ Verify the endpoint supports updating the status of a UserGrade, but no other fields. """ grade = UserGradeFactory( course_run=self.course_run, username=self.user.username, letter_grade='C', ) path = reverse('api:v2:grades-detail', kwargs={'pk': grade.id}) # Verify users without the change permission are denied access self.assert_access_denied(self.user, method, path, data=self.data) self.authenticate_user(self.user) self.add_user_permission(self.user, 'change_usergrade') response = getattr(self.client, method)(path, data=self.data) grade.refresh_from_db() self.assertEqual(response.status_code, 200) self.assertEqual(grade.letter_grade, self.data['letter_grade']) self.assertDictEqual(response.data, self.serialize_user_grade(grade))
def test_course_run_no_credential(self): """ Adds a course run with no credential and tests that it does appear in the results """ new_course_run = CourseRunFactory() self.program.course_runs.add(new_course_run) UserGradeFactory( username=self.MOCK_USER_DATA["username"], course_run=new_course_run, letter_grade="F", percent_grade=0.05 ) CourseCertificateFactory(course_id=new_course_run.key, site=self.site) response = self.client.get(reverse("records:private_programs", kwargs={"uuid": self.program.uuid.hex})) grades = json.loads(response.context_data["record"])["grades"] self.assertEqual(len(grades), 2) self.assertEqual(new_course_run.course.title, grades[1]["name"])
def test_to_representation(self): grade = UserGradeFactory() expected = { 'id': grade.id, 'username': grade.username, 'course_run': grade.course_run.key, 'letter_grade': grade.letter_grade, 'percent_grade': str(grade.percent_grade), 'verified': grade.verified, 'created': grade.created.strftime(api_settings.DATETIME_FORMAT), 'modified': grade.modified.strftime(api_settings.DATETIME_FORMAT), } actual = UserGradeSerializer(grade).data self.assertDictEqual(actual, expected)
def test_to_representation(self): grade = UserGradeFactory() expected = { "id": grade.id, "username": grade.username, "course_run": grade.course_run.key, "letter_grade": grade.letter_grade, "percent_grade": str(grade.percent_grade), "verified": grade.verified, "created": grade.created.strftime(api_settings.DATETIME_FORMAT), "modified": grade.modified.strftime(api_settings.DATETIME_FORMAT), } actual = UserGradeSerializer(grade).data self.assertDictEqual(actual, expected)
def course_run_no_credential(self): """ Adds a course run with no credential and tests that it doesn't appear in the results """ new_course_run = CourseRunFactory() self.program.course_runs.add(new_course_run) # pylint: disable=no-member UserGradeFactory(username=self.MOCK_USER_DATA['username'], course_run=new_course_run, letter_grade='F', percent_grade=.05) CourseCertificateFactory(course_id=new_course_run.key, site=self.site) response = self.client.get( reverse('records:programs', kwargs={'uuid': self.program.uuid.hex})) grades = json.loads(response.context_data['record'])['grades'] self.assertEqual(len(grades), 1) self.assertEqual(self.course_runs[1].key, grades[0]['course_id'])
def test_multiple_attempts_no_cert(self): """ Adds a course with two failed course_run attempts (no cert) and verifies that the course only shows up once """ # Only superusers can view an empty program (we could add a real cert too here, but this is a more direct test) self.user.is_superuser = True self.user.save() new_course = CourseFactory(site=self.site) new_course_runs = CourseRunFactory.create_batch(2, course=new_course) _ = [UserGradeFactory(username=self.MOCK_USER_DATA['username'], course_run=course_run, letter_grade='F', percent_grade=0.20) for course_run in new_course_runs] self.program.course_runs = new_course_runs response = self.client.get(reverse('records:private_programs', kwargs={'uuid': self.program.uuid.hex})) grades = json.loads(response.context_data['record'])['grades'] self.assertEqual(len(grades), 1) self.assertEqual(new_course.title, grades[0]['name'])
def test_course_run_order(self): """ Test that the course_runs are returned in the program order """ new_course_run = CourseRunFactory() self.program.course_runs.add(new_course_run) UserGradeFactory( username=self.MOCK_USER_DATA["username"], course_run=new_course_run, letter_grade="C", percent_grade=0.70 ) new_course_cert = CourseCertificateFactory(course_id=new_course_run.key, site=self.site) UserCredentialFactory( username=self.MOCK_USER_DATA["username"], credential_content_type=self.credential_content_type, credential=new_course_cert, ) response = self.client.get(reverse("records:private_programs", kwargs={"uuid": self.program.uuid.hex})) grades = json.loads(response.context_data["record"])["grades"] expected_course_run_keys = [course_run.key for course_run in [self.course_runs[1], new_course_run]] actual_course_run_keys = [grade["course_id"] for grade in grades] self.assertEqual(expected_course_run_keys, actual_course_run_keys)
def test_protected_deletion(self): course_run = CourseRunFactory() UserGradeFactory(course_run=course_run) with self.assertRaises(ProtectedError): course_run.delete()