def test_list_program_uuid_filtering(self): """ Verify the endpoint returns data for all UserCredentials in the given program. """ # Course run 1 is in a program, course run 2 is not course1_run = CourseRunFactory() course2_run = CourseRunFactory() program = ProgramFactory(course_runs=[course1_run]) program_certificate = ProgramCertificateFactory( site=self.site, program_uuid=program.uuid) course1_certificate = CourseCertificateFactory( site=self.site, course_id=course1_run.key) course2_certificate = CourseCertificateFactory( site=self.site, course_id=course2_run.key) # Create some credentials related to the program course1_cred = UserCredentialFactory(credential=course1_certificate) program_creds = UserCredentialFactory.create_batch( 3, credential=program_certificate) expected = [course1_cred] + program_creds # Create some more credentials that we don't expect to see returned UserCredentialFactory.create_batch(3) UserCredentialFactory(credential=course2_certificate) self.authenticate_user(self.user) self.add_user_permission(self.user, 'view_usercredential') response = self.client.get(self.list_path + '?program_uuid={}'.format(program.uuid)) self.assertEqual(response.status_code, 200) self.assertEqual(response.data['results'], self.serialize_user_credential(expected, many=True))
def setUp(self): super(CredentialFieldTests, self).setUp() self.program_certificate = ProgramCertificateFactory(site=self.site) self.course_certificate = CourseCertificateFactory( site=self.site, certificate_type='verified') self.field_instance = CredentialField() self.field_instance.context['request'] = namedtuple( 'HttpRequest', ['site'])(self.site)
def setUp(self): super(CredentialFieldTests, self).setUp() self.program_certificate = ProgramCertificateFactory(site=self.site) self.course_certificate = CourseCertificateFactory(site=self.site, certificate_type='verified') self.field_instance = CredentialField() # see: https://github.com/encode/django-rest-framework/blob/3.9.x/rest_framework/fields.py#L610 # pylint: disable=protected-access self.field_instance._context = { 'request': namedtuple('HttpRequest', ['site'])(self.site), }
def test_course_credential(self): request = APIRequestFactory().get('/') course_certificate = CourseCertificateFactory() user_credential = UserCredentialFactory(credential=course_certificate) user_credential_attribute = UserCredentialAttributeFactory(user_credential=user_credential) expected_url = 'http://testserver{}'.format( reverse('credentials:render', kwargs={'uuid': user_credential.uuid.hex})) expected = { 'username': user_credential.username, 'uuid': str(user_credential.uuid), 'credential': { 'type': 'course-run', 'course_run_key': course_certificate.course_id, 'mode': course_certificate.certificate_type, }, 'download_url': user_credential.download_url, 'status': user_credential.status, 'attributes': [ { 'name': user_credential_attribute.name, 'value': user_credential_attribute.value } ], 'created': user_credential.created.strftime(api_settings.DATETIME_FORMAT), 'modified': user_credential.modified.strftime(api_settings.DATETIME_FORMAT), 'certificate_url': expected_url } actual = UserCredentialSerializer(user_credential, context={'request': request}).data self.assertEqual(actual, expected)
def test_course_credential(self): request = APIRequestFactory().get("/") course_certificate = CourseCertificateFactory() user_credential = UserCredentialFactory(credential=course_certificate) user_credential_attribute = UserCredentialAttributeFactory(user_credential=user_credential) expected_url = "http://testserver{}".format( reverse("credentials:render", kwargs={"uuid": user_credential.uuid.hex}) ) expected = { "username": user_credential.username, "uuid": str(user_credential.uuid), "credential": { "type": "course-run", "course_run_key": course_certificate.course_id, "mode": course_certificate.certificate_type, }, "download_url": user_credential.download_url, "status": user_credential.status, "attributes": [{"name": user_credential_attribute.name, "value": user_credential_attribute.value}], "created": user_credential.created.strftime(api_settings.DATETIME_FORMAT), "modified": user_credential.modified.strftime(api_settings.DATETIME_FORMAT), "certificate_url": expected_url, } actual = UserCredentialSerializer(user_credential, context={"request": request}).data self.assertEqual(actual, expected)
def test_list_visible_filtering(self): """ Verify the endpoint can filter by visible date. """ program_certificate = ProgramCertificateFactory(site=self.site) course_certificate = CourseCertificateFactory(site=self.site) course_cred = UserCredentialFactory(credential=course_certificate) program_cred = UserCredentialFactory(credential=program_certificate) UserCredentialAttributeFactory( user_credential=program_cred, name="visible_date", value="9999-01-01T01:01:01Z", ) self.authenticate_user(self.user) self.add_user_permission(self.user, "view_usercredential") both = [course_cred, program_cred] response = self.client.get(self.list_path) self.assertEqual(response.status_code, 200) self.assertEqual(response.data["results"], self.serialize_user_credential(both, many=True)) response = self.client.get(self.list_path + "?only_visible=True") self.assertEqual(response.status_code, 200) self.assertEqual( response.data["results"], self.serialize_user_credential([course_cred], many=True)) response = self.client.get(self.list_path + "?only_visible=False") self.assertEqual(response.status_code, 200) self.assertEqual(response.data["results"], self.serialize_user_credential(both, many=True))
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_issue_credential_with_invalid_type(self): """ Verify the method raises an error for attempts to issue an unsupported credential type. """ accreditor = Accreditor(issuers=[ProgramCertificateIssuer()]) course_cert = CourseCertificateFactory() with self.assertRaises(UnsupportedCredentialTypeError): accreditor.issue_credential(course_cert, 'tester', attributes=self.attributes)
def test_to_internal_value_with_valid_course_credential(self): """Verify that it will return credential object if course-id and certificate type found in db.""" course_cert = CourseCertificateFactory() self.assertEqual( course_cert, self.field_instance.to_internal_value({"course_id": course_cert.course_id, "certificate_type": "honor"}) )
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(UserCredentialSerializerTests, self).setUp() self.program_cert = ProgramCertificateFactory() self.program_credential = UserCredentialFactory(credential=self.program_cert) self.program_cert_attr = UserCredentialAttributeFactory(user_credential=self.program_credential) self.course_cert = CourseCertificateFactory.create() self.course_credential = UserCredentialFactory.create(credential=self.course_cert) self.course_cert_attr = UserCredentialAttributeFactory(user_credential=self.course_credential) self.request = APIRequestFactory().get('/')
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_missing_course_run(self): # We should be able to create an entry without a course run course_certificate = CourseCertificateFactory(course_run=None) actual = CourseCertificateSerializer(course_certificate).data expected = { "id": course_certificate.id, "course_id": course_certificate.course_id, "course_run": None, "certificate_type": course_certificate.certificate_type, "certificate_available_date": course_certificate.certificate_available_date, "is_active": course_certificate.is_active, } self.assertEqual(actual, expected)
def test_create_course_certificate(self): course_run = CourseRunFactory() course_certificate = CourseCertificateFactory(course_run=course_run) actual = CourseCertificateSerializer(course_certificate).data expected = { "id": course_certificate.id, "course_id": course_certificate.course_id, "course_run": course_certificate.course_run.key, "certificate_type": course_certificate.certificate_type, "certificate_available_date": course_certificate.certificate_available_date, "is_active": course_certificate.is_active, } self.assertEqual(actual, expected)
def test_multiple_programs(self): """ Test that multiple programs can appear, in progress and completed """ # Create a second program, and delete the first one's certificate new_course = CourseFactory.create(site=self.site) new_course_run = CourseRunFactory.create(course=new_course) new_program = ProgramFactory.create(title='ZTestProgram', course_runs=[new_course_run], authoring_organizations=self.orgs, site=self.site) new_course_cert = CourseCertificateFactory.create(course_id=new_course_run.key, site=self.site) new_program_cert = ProgramCertificateFactory.create(program_uuid=new_program.uuid, site=self.site) # Make a new user credential UserCredentialFactory.create( username=self.user.username, credential_content_type=self.program_credential_content_type, credential=new_course_cert ) # Make a new program credential UserCredentialFactory.create( username=self.user.username, credential_content_type=self.program_credential_content_type, credential=new_program_cert ) self.program_user_credential.delete() response = self.client.get(reverse('records:index')) self.assertEqual(response.status_code, 200) program_data = json.loads(response.context_data['programs']) expected_program_data = [ { 'name': self.program.title, 'partner': 'TestOrg1, TestOrg2', 'uuid': self.program.uuid.hex, 'type': slugify(self.program.type), 'completed': False, 'empty': False, }, { 'name': new_program.title, 'partner': 'TestOrg1, TestOrg2', 'uuid': new_program.uuid.hex, 'type': slugify(new_program.type), 'completed': True, 'empty': False, } ] self.assertEqual(program_data, expected_program_data)
def test_to_representation_with_course(self): """Verify that it will return course certificate credential object in dict format.""" course_cert = CourseCertificateFactory() expected_data = { "course_id": course_cert.course_id, "credential_id": course_cert.id, "certificate_type": course_cert.certificate_type } self.assertEqual( expected_data, self.field_instance.to_representation( course_cert ) )
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 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_list_type_filtering(self): """ Verify the endpoint returns data for all UserCredentials for the given type. """ program_certificate = ProgramCertificateFactory(site=self.site) course_certificate = CourseCertificateFactory(site=self.site) course_cred = UserCredentialFactory(credential=course_certificate) program_cred = UserCredentialFactory(credential=program_certificate) self.authenticate_user(self.user) self.add_user_permission(self.user, 'view_usercredential') response = self.client.get(self.list_path + '?type=course-run') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['results'], self.serialize_user_credential([course_cred], many=True)) response = self.client.get(self.list_path + '?type=program') self.assertEqual(response.status_code, 200) self.assertEqual(response.data['results'], self.serialize_user_credential([program_cred], many=True))
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 setUp(self): super().setUp() dump_random_state() self.user = UserFactory(username=self.MOCK_USER_DATA["username"]) self.orgs = [OrganizationFactory.create(name=name, site=self.site) for name in ["TestOrg1", "TestOrg2"]] self.course = CourseFactory.create(site=self.site) self.course_runs = CourseRunFactory.create_batch(2, course=self.course) self.program = ProgramFactory( title="TestProgram1", course_runs=self.course_runs, authoring_organizations=self.orgs, site=self.site ) self.course_certs = [ CourseCertificateFactory.create( course_id=course_run.key, site=self.site, ) for course_run in self.course_runs ] self.program_cert = ProgramCertificateFactory.create(program_uuid=self.program.uuid, site=self.site) self.course_credential_content_type = ContentType.objects.get( app_label="credentials", model="coursecertificate" ) self.program_credential_content_type = ContentType.objects.get( app_label="credentials", model="programcertificate" ) self.course_user_credentials = [ UserCredentialFactory.create( username=self.user.username, credential_content_type=self.course_credential_content_type, credential=course_cert, ) for course_cert in self.course_certs ] self.program_user_credential = UserCredentialFactory.create( username=self.user.username, credential_content_type=self.program_credential_content_type, credential=self.program_cert, ) self.client.login(username=self.user.username, password=USER_PASSWORD)
def setUp(self): super().setUp() self.user = UserFactory(username=self.MOCK_USER_DATA['username']) self.orgs = [ OrganizationFactory.create(name=name, site=self.site) for name in ['TestOrg1', 'TestOrg2'] ] self.course = CourseFactory.create(site=self.site) self.course_runs = [ CourseRunFactory.create(course=self.course) for _ in range(2) ] self.program = ProgramFactory(course_runs=self.course_runs, authoring_organizations=self.orgs, site=self.site) self.course_certs = [ CourseCertificateFactory.create( course_id=course_run.key, site=self.site, ) for course_run in self.course_runs ] self.program_cert = ProgramCertificateFactory.create( program_uuid=self.program.uuid, site=self.site) self.course_credential_content_type = ContentType.objects.get( app_label='credentials', model='coursecertificate') self.program_credential_content_type = ContentType.objects.get( app_label='credentials', model='programcertificate') self.course_user_credentials = [ UserCredentialFactory.create( username=self.user.username, credential_content_type=self.course_credential_content_type, credential=course_cert) for course_cert in self.course_certs ] self.program_user_credentials = UserCredentialFactory.create( username=self.user.username, credential_content_type=self.program_credential_content_type, credential=self.program_cert) self.client.login(username=self.user.username, password=USER_PASSWORD)
class CredentialFieldTests(SiteMixin, TestCase): def setUp(self): super().setUp() self.program_certificate = ProgramCertificateFactory(site=self.site) self.course_certificate = CourseCertificateFactory( site=self.site, certificate_type='verified') self.field_instance = CredentialField() # see: https://github.com/encode/django-rest-framework/blob/3.9.x/rest_framework/fields.py#L610 # pylint: disable=protected-access self.field_instance._context = { 'request': namedtuple('HttpRequest', ['site'])(self.site), } def assert_program_uuid_validation_error_raised(self, program_uuid): try: self.field_instance.to_internal_value( {'program_uuid': program_uuid}) except ValidationError as ex: expected = { 'program_uuid': f'No active ProgramCertificate exists for program [{program_uuid}]' } self.assertEqual(ex.detail, expected) def assert_course_run_key_validation_error_raised(self, course_run_key): try: self.field_instance.to_internal_value({ 'course_run_key': course_run_key, 'mode': 'verified' }) except ValidationError as ex: expected = { 'course_run_key': f'No active CourseCertificate exists for course run [{course_run_key}]' } self.assertEqual(ex.detail, expected) def test_to_internal_value_with_empty_program_uuid(self): """ Verify an error is raised if no program UUID is provided. """ with self.assertRaisesMessage(ValidationError, 'Credential identifier is missing'): self.field_instance.to_internal_value({'program_uuid': ''}) def test_to_internal_value_with_invalid_program_uuid(self, ): """ Verify the method raises a ValidationError if the passed program UUID does not correspond to a ProgramCertificate. """ self.assert_program_uuid_validation_error_raised(uuid4()) def test_to_internal_value_with_invalid_site(self, ): """ Verify the method raises a ValidationError if the passed program UUID belongs to a different site. """ certificate = ProgramCertificateFactory( ) # without setting site=self.site self.assert_program_uuid_validation_error_raised( certificate.program_uuid) def test_to_internal_value_with_inactive_program_certificate(self, ): """ Verify the method raises a ValidationError if the ProgramCertificate is NOT active. """ self.program_certificate.is_active = False self.program_certificate.save() self.assert_program_uuid_validation_error_raised( self.program_certificate.program_uuid) def test_to_internal_value_with_valid_program_credential(self): """ Verify the method returns the ProgramCertificate corresponding to the specified UUID. """ self.assertEqual( self.field_instance.to_internal_value( {'program_uuid': self.program_certificate.program_uuid}), self.program_certificate) def test_to_internal_value_with_created_course_credential(self): """ Verify the method creates a course credential if needed. """ credential = self.field_instance.to_internal_value({ 'course_run_key': 'create-me', 'mode': 'verified' }) self.assertEqual(credential, CourseCertificate.objects.get(course_id='create-me')) def test_to_internal_value_with_created_course_credential_read_only(self): """ Verify the method refuses to create a course credential when read-only. """ self.field_instance.read_only = True self.assert_course_run_key_validation_error_raised('create-me') def test_to_internal_value_with_created_course_credential_no_type_change( self): """ Verify the method won't update cert information when creating a course credential. """ credential = self.field_instance.to_internal_value({ 'course_run_key': self.course_certificate.course_id, 'mode': 'honor' }) credential.refresh_from_db() # just in case self.assertEqual(credential.certificate_type, 'verified') def test_to_internal_value_with_inactive_course_credential(self): """ Verify the method raises a ValidationError if the CourseCertificate is NOT active. """ self.course_certificate.is_active = False self.course_certificate.save() self.assert_course_run_key_validation_error_raised( self.course_certificate.course_id) def test_to_internal_value_with_valid_course_credential(self): """ Verify the method serializes the course credential details to a dict. """ self.assertEqual( self.field_instance.to_internal_value({ 'course_run_key': self.course_certificate.course_id, 'mode': 'verified' }), self.course_certificate) def test_to_representation_data_with_program(self): """ Verify the method serializes the credential details to a dict. """ expected = { 'type': 'program', 'program_uuid': self.program_certificate.program_uuid, 'credential_id': self.program_certificate.id } self.assertEqual( self.field_instance.to_representation(self.program_certificate), expected) def test_to_representation_data_with_course(self): """ Verify the method serializes the credential details to a dict. """ expected = { 'type': 'course-run', 'course_run_key': self.course_certificate.course_id, 'mode': self.course_certificate.certificate_type, } self.assertEqual( self.field_instance.to_representation(self.course_certificate), expected)