diff --git a/lms/djangoapps/learner_dashboard/tests/test_programs.py b/lms/djangoapps/learner_dashboard/tests/test_programs.py index 23d0301362..27c3ad024e 100644 --- a/lms/djangoapps/learner_dashboard/tests/test_programs.py +++ b/lms/djangoapps/learner_dashboard/tests/test_programs.py @@ -4,7 +4,6 @@ Unit tests covering the program listing and detail pages. """ import json import re -import unittest from urlparse import urljoin from uuid import uuid4 @@ -16,8 +15,6 @@ import mock from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, CourseFactory, CourseRunFactory from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin -from openedx.core.djangoapps.credentials.tests.factories import UserCredential, ProgramCredential -from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory, CourseEnrollmentFactory @@ -25,15 +22,13 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory as ModuleStoreCourseFactory -CATALOG_UTILS_MODULE = 'openedx.core.djangoapps.catalog.utils' -CREDENTIALS_UTILS_MODULE = 'openedx.core.djangoapps.credentials.utils' PROGRAMS_UTILS_MODULE = 'openedx.core.djangoapps.programs.utils' @skip_unless_lms @override_settings(MKTG_URLS={'ROOT': 'https://www.example.com'}) @mock.patch(PROGRAMS_UTILS_MODULE + '.get_programs') -class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, SharedModuleStoreTestCase): +class TestProgramListing(ProgramsApiConfigMixin, SharedModuleStoreTestCase): """Unit tests for the program listing page.""" maxDiff = None password = 'test' @@ -65,15 +60,6 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar """ return program['title'] - def credential_sort_key(self, credential): - """ - Helper function used to sort dictionaries representing credentials. - """ - try: - return credential['certificate_url'] - except KeyError: - return credential['credential_url'] - def load_serialized_data(self, response, key): """ Extract and deserialize serialized data from the response. @@ -184,48 +170,6 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar expected_url = reverse('program_details_view', kwargs={'program_uuid': expected_program['uuid']}) self.assertEqual(actual_program['detail_url'], expected_url) - @mock.patch(CREDENTIALS_UTILS_MODULE + '.get_credentials') - @mock.patch(CREDENTIALS_UTILS_MODULE + '.get_programs') - def test_certificates_listed(self, mock_get_programs, mock_get_credentials, __): - """ - Verify that the response contains accurate certificate data when certificates are available. - """ - self.create_programs_config() - self.create_credentials_config(is_learner_issuance_enabled=True) - - mock_get_programs.return_value = self.data - - first_credential = UserCredential( - username=self.user.username, - credential=ProgramCredential( - program_uuid=self.first_program['uuid'] - ) - ) - second_credential = UserCredential( - username=self.user.username, - credential=ProgramCredential( - program_uuid=self.second_program['uuid'] - ) - ) - - credentials_data = sorted([first_credential, second_credential], key=self.credential_sort_key) - mock_get_credentials.return_value = credentials_data - - response = self.client.get(self.url) - actual = self.load_serialized_data(response, 'certificatesData') - actual = sorted(actual, key=self.credential_sort_key) - - self.assertEqual(len(actual), len(credentials_data)) - for index, actual_credential in enumerate(actual): - expected_credential = credentials_data[index] - - self.assertEqual( - # TODO: certificate_url is needlessly transformed to credential_url. (╯°□°)╯︵ ┻━┻ - # Clean this up! - actual_credential['credential_url'], - expected_credential['certificate_url'] - ) - @skip_unless_lms @mock.patch(PROGRAMS_UTILS_MODULE + '.get_programs') diff --git a/lms/djangoapps/learner_dashboard/views.py b/lms/djangoapps/learner_dashboard/views.py index d8d8207705..f8411ca5df 100644 --- a/lms/djangoapps/learner_dashboard/views.py +++ b/lms/djangoapps/learner_dashboard/views.py @@ -7,7 +7,6 @@ from django.views.decorators.http import require_GET from edxmako.shortcuts import render_to_response from lms.djangoapps.learner_dashboard.utils import strip_course_id, FAKE_COURSE_KEY from openedx.core.djangoapps.catalog.utils import get_programs -from openedx.core.djangoapps.credentials.utils import get_programs_credentials from openedx.core.djangoapps.programs.models import ProgramsApiConfig from openedx.core.djangoapps.programs.utils import ( get_program_marketing_url, @@ -29,7 +28,6 @@ def program_listing(request): meter = ProgramProgressMeter(request.user) context = { - 'credentials': get_programs_credentials(request.user), 'disable_courseware_js': True, 'marketing_url': get_program_marketing_url(programs_config), 'nav_hidden': True, diff --git a/lms/static/js/learner_dashboard/views/certificate_view.js b/lms/static/js/learner_dashboard/views/certificate_view.js deleted file mode 100644 index 6cb4ad3d36..0000000000 --- a/lms/static/js/learner_dashboard/views/certificate_view.js +++ /dev/null @@ -1,39 +0,0 @@ -(function(define) { - 'use strict'; - define(['backbone', - 'jquery', - 'underscore', - 'gettext', - 'text!../../../templates/learner_dashboard/certificate.underscore' - ], - function( - Backbone, - $, - _, - gettext, - certificateTpl - ) { - return Backbone.View.extend({ - el: '.certificates-list', - tpl: _.template(certificateTpl), - initialize: function(data) { - this.context = data.context; - this.render(); - }, - render: function() { - var certificatesData = this.context.certificatesData || []; - - if (certificatesData.length) { - this.$el.html(this.tpl(this.context)); - } else { - /** - * If not rendering remove el because - * styles are applied to it - */ - this.remove(); - } - } - }); - } - ); -}).call(this, define || RequireJS.define); diff --git a/lms/static/js/learner_dashboard/views/sidebar_view.js b/lms/static/js/learner_dashboard/views/sidebar_view.js index c7bd481dbc..77340e4fd1 100644 --- a/lms/static/js/learner_dashboard/views/sidebar_view.js +++ b/lms/static/js/learner_dashboard/views/sidebar_view.js @@ -6,7 +6,6 @@ 'underscore', 'gettext', 'js/learner_dashboard/views/explore_new_programs_view', - 'js/learner_dashboard/views/certificate_view', 'text!../../../templates/learner_dashboard/sidebar.underscore' ], function( @@ -15,7 +14,6 @@ _, gettext, NewProgramsView, - CertificateView, sidebarTpl ) { return Backbone.View.extend({ @@ -36,10 +34,6 @@ this.newProgramsView = new NewProgramsView({ context: this.context }); - - this.newCertificateView = new CertificateView({ - context: this.context - }); } }); } diff --git a/lms/static/js/spec/learner_dashboard/certificate_view_spec.js b/lms/static/js/spec/learner_dashboard/certificate_view_spec.js deleted file mode 100644 index 2f2ed20fc9..0000000000 --- a/lms/static/js/spec/learner_dashboard/certificate_view_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -define([ - 'backbone', - 'jquery', - 'js/learner_dashboard/views/certificate_view' -], function(Backbone, $, CertificateView) { - 'use strict'; - describe('Certificate View', function() { - var view = null, - data = { - context: { - certificatesData: [ - { - 'display_name': 'Testing', - 'credential_url': 'https://credentials.stage.edx.org/credentials/dummy-uuid-1/' - }, - { - 'display_name': 'Testing2', - 'credential_url': 'https://credentials.stage.edx.org/credentials/dummy-uuid-2/' - } - ], - sampleCertImageSrc: '/images/programs/sample-cert.png' - } - }; - - beforeEach(function() { - setFixtures('
'); - view = new CertificateView(data); - view.render(); - }); - - afterEach(function() { - view.remove(); - }); - - it('should exist', function() { - expect(view).toBeDefined(); - }); - - it('should load the certificates based on passed in certificates list', function() { - var $certificates = view.$el.find('.certificate-link'); - expect($certificates.length).toBe(2); - - $certificates.each(function(index, el) { - expect($(el).html().trim()).toEqual(data.context.certificatesData[index].display_name); - expect($(el).attr('href')).toEqual(data.context.certificatesData[index].credential_url); - }); - expect(view.$el.find('.hd-6').html().trim()).toEqual('Program Certificates'); - expect(view.$el.find('img').attr('src')).toEqual(data.context.sampleCertImageSrc); - }); - - it('should display no certificate box if certificates list is empty', function() { - view.remove(); - setFixtures('
'); - view = new CertificateView({ - context: {certificatesData: []} - }); - view.render(); - expect(view.$('.certificates-list').length).toBe(0); - }); - }); -} -); diff --git a/lms/static/js/spec/learner_dashboard/sidebar_view_spec.js b/lms/static/js/spec/learner_dashboard/sidebar_view_spec.js index 7bc6e9fead..67368ccaf5 100644 --- a/lms/static/js/spec/learner_dashboard/sidebar_view_spec.js +++ b/lms/static/js/spec/learner_dashboard/sidebar_view_spec.js @@ -9,14 +9,7 @@ define([ describe('Sidebar View', function() { var view = null, context = { - marketingUrl: 'https://www.example.org/programs', - certificatesData: [ - { - 'display_name': 'Testing', - 'credential_url': 'https://credentials.example.com/credentials/uuid/' - } - ], - sampleCertImageSrc: '/images/programs/sample-cert.png' + marketingUrl: 'https://www.example.org/programs' }; beforeEach(function() { @@ -44,21 +37,16 @@ define([ expect($sidebar.find('.program-advertise .ad-link a').attr('href')).toEqual(context.marketingUrl); }); - it('should load the certificates based on passed in certificates list', function() { - expect(view.$('.certificate-link').length).toBe(1); - }); - it('should not load the advertising panel if no marketing URL is provided', function() { var $ad; view.remove(); view = new SidebarView({ el: '.sidebar', - context: {certificatesData: []} + context: {} }); view.render(); $ad = view.$el.find('.program-advertise'); expect($ad.length).toBe(0); - expect(view.$('.certificate-link').length).toBe(0); }); }); } diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js index 11ff778593..b6f4374cfe 100644 --- a/lms/static/lms/js/spec/main.js +++ b/lms/static/lms/js/spec/main.js @@ -737,7 +737,6 @@ 'js/spec/instructor_dashboard/certificates_spec.js', 'js/spec/instructor_dashboard/ecommerce_spec.js', 'js/spec/instructor_dashboard/student_admin_spec.js', - 'js/spec/learner_dashboard/certificate_view_spec.js', 'js/spec/learner_dashboard/collection_list_view_spec.js', 'js/spec/learner_dashboard/program_card_view_spec.js', 'js/spec/learner_dashboard/sidebar_view_spec.js', diff --git a/lms/static/sass/views/_program-list.scss b/lms/static/sass/views/_program-list.scss index 1f552a98b4..17dd16f0f7 100644 --- a/lms/static/sass/views/_program-list.scss +++ b/lms/static/sass/views/_program-list.scss @@ -43,27 +43,6 @@ margin-bottom: $baseline; } } - - .certificate-container { - .hd-6 { - color: palette(grayscale, dark); - font-weight: font-weight(normal); - margin-bottom: $baseline; - } - - .certificate-link { - padding-top: $baseline; - color: palette(primary, base); - display: block; - - &:active, - &:focus, - &:hover { - color: $link-hover; - text-decoration: underline; - } - } - } } .empty-programs-message { diff --git a/lms/templates/learner_dashboard/certificate.underscore b/lms/templates/learner_dashboard/certificate.underscore deleted file mode 100644 index f7b85ddf90..0000000000 --- a/lms/templates/learner_dashboard/certificate.underscore +++ /dev/null @@ -1,7 +0,0 @@ -
-

<%- gettext('Program Certificates') %>

- - <% _.each(certificatesData, function(certificate){ %> - <%- gettext(certificate.display_name) %> - <% }); %> -
diff --git a/lms/templates/learner_dashboard/programs.html b/lms/templates/learner_dashboard/programs.html index 062be9930b..1184eb827d 100644 --- a/lms/templates/learner_dashboard/programs.html +++ b/lms/templates/learner_dashboard/programs.html @@ -15,10 +15,8 @@ from openedx.core.djangolib.js_utils import ( <%block name="js_extra"> <%static:require_module module_name="js/learner_dashboard/program_list_factory" class_name="ProgramListFactory"> ProgramListFactory({ - certificatesData: ${credentials | n, dump_js_escaped_json}, marketingUrl: '${marketing_url | n, js_escaped_string}', programsData: ${programs | n, dump_js_escaped_json}, - sampleCertImageSrc: '${static.url('images/programs/sample-cert.png') | n, js_escaped_string}', userProgress: ${progress | n, dump_js_escaped_json} }); diff --git a/lms/templates/learner_dashboard/sidebar.underscore b/lms/templates/learner_dashboard/sidebar.underscore index 8ea51b42a8..4dad7961fc 100644 --- a/lms/templates/learner_dashboard/sidebar.underscore +++ b/lms/templates/learner_dashboard/sidebar.underscore @@ -1,2 +1 @@ - diff --git a/openedx/core/djangoapps/catalog/tests/factories.py b/openedx/core/djangoapps/catalog/tests/factories.py index ae916b92e9..5f4dabf9c4 100644 --- a/openedx/core/djangoapps/catalog/tests/factories.py +++ b/openedx/core/djangoapps/catalog/tests/factories.py @@ -35,6 +35,10 @@ def generate_zulu_datetime(): class DictFactoryBase(factory.Factory): + """ + Subclass this to make factories that can be used to produce fake API response + bodies for testing. + """ class Meta(object): model = dict diff --git a/openedx/core/djangoapps/credentials/tests/factories.py b/openedx/core/djangoapps/credentials/tests/factories.py index 51ddf2324c..87a2cbed80 100644 --- a/openedx/core/djangoapps/credentials/tests/factories.py +++ b/openedx/core/djangoapps/credentials/tests/factories.py @@ -1,43 +1,31 @@ """Factories for generating fake credentials-related data.""" -import uuid +# pylint: disable=missing-docstring, invalid-name +from functools import partial import factory -from factory.fuzzy import FuzzyText + +from openedx.core.djangoapps.catalog.tests.factories import ( + generate_instances, + generate_course_run_key, + DictFactoryBase, +) -class UserCredential(factory.Factory): - """Factory for stubbing user credentials resources from the User Credentials - API (v1). - """ - class Meta(object): - model = dict - - id = factory.Sequence(lambda n: n) # pylint: disable=invalid-name - username = FuzzyText(prefix='user_') - status = 'awarded' - uuid = FuzzyText(prefix='uuid_') - certificate_url = FuzzyText(prefix='https://www.example.com/credentials/') - credential = {} +class ProgramCredential(DictFactoryBase): + credential_id = factory.Faker('random_int') + program_uuid = factory.Faker('uuid4') -class ProgramCredential(factory.Factory): - """Factory for stubbing program credentials resources from the Program - Credentials API (v1). - """ - class Meta(object): - model = dict - - credential_id = factory.Sequence(lambda n: n) - program_uuid = factory.LazyAttribute(lambda obj: str(uuid.uuid4())) - - -class CourseCredential(factory.Factory): - """Factory for stubbing course credentials resources from the Course - Credentials API (v1). - """ - class Meta(object): - model = dict - - course_id = 'edx/test01/2015' - credential_id = factory.Sequence(lambda n: n) +class CourseCredential(DictFactoryBase): + credential_id = factory.Faker('random_int') + course_id = factory.LazyFunction(generate_course_run_key) certificate_type = 'verified' + + +class UserCredential(DictFactoryBase): + id = factory.Faker('random_int') + username = factory.Faker('word') + status = 'awarded' + uuid = factory.Faker('uuid4') + certificate_url = factory.Faker('url') + credential = factory.LazyFunction(partial(generate_instances, ProgramCredential, count=1)) diff --git a/openedx/core/djangoapps/credentials/tests/mixins.py b/openedx/core/djangoapps/credentials/tests/mixins.py index 0ba9e7e9a8..8472153fb7 100644 --- a/openedx/core/djangoapps/credentials/tests/mixins.py +++ b/openedx/core/djangoapps/credentials/tests/mixins.py @@ -27,94 +27,3 @@ class CredentialsApiConfigMixin(object): CredentialsApiConfig(**fields).save() return CredentialsApiConfig.current() - - -class CredentialsDataMixin(object): - """Mixin mocking Credentials API URLs and providing fake data for testing.""" - CREDENTIALS_API_RESPONSE = { - "next": None, - "results": [ - factories.UserCredential( - id=1, - username='test', - credential=factories.ProgramCredential() - ), - factories.UserCredential( - id=2, - username='test', - credential=factories.ProgramCredential() - ), - factories.UserCredential( - id=3, - status='revoked', - username='test', - credential=factories.ProgramCredential() - ), - factories.UserCredential( - id=4, - username='test', - credential=factories.CourseCredential( - certificate_type='honor' - ) - ), - factories.UserCredential( - id=5, - username='test', - credential=factories.CourseCredential( - course_id='edx/test02/2015' - ) - ), - factories.UserCredential( - id=6, - username='test', - credential=factories.CourseCredential( - course_id='edx/test02/2015' - ) - ), - ] - } - - CREDENTIALS_NEXT_API_RESPONSE = { - "next": None, - "results": [ - factories.UserCredential( - id=7, - username='test', - credential=factories.ProgramCredential() - ), - factories.UserCredential( - id=8, - username='test', - credential=factories.ProgramCredential() - ) - ] - } - - def mock_credentials_api(self, user, data=None, status_code=200, reset_url=True, is_next_page=False): - """Utility for mocking out Credentials API URLs.""" - self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Credentials API calls.') - internal_api_url = CredentialsApiConfig.current().internal_api_url.strip('/') - - url = internal_api_url + '/credentials/?status=awarded&username=' + user.username - if reset_url: - httpretty.reset() - - if data is None: - data = self.CREDENTIALS_API_RESPONSE - - body = json.dumps(data) - - if is_next_page: - next_page_url = internal_api_url + '/credentials/?page=2&status=awarded&username=' + user.username - self.CREDENTIALS_NEXT_API_RESPONSE['next'] = next_page_url - next_page_body = json.dumps(self.CREDENTIALS_NEXT_API_RESPONSE) - httpretty.register_uri( - httpretty.GET, next_page_url, body=body, content_type='application/json', status=status_code - ) - httpretty.register_uri( - httpretty.GET, url, body=next_page_body, content_type='application/json', status=status_code - ) - else: - httpretty.register_uri( - httpretty.GET, url, body=body, content_type='application/json', status=status_code - ) diff --git a/openedx/core/djangoapps/credentials/tests/test_utils.py b/openedx/core/djangoapps/credentials/tests/test_utils.py index 17c1812091..334d881688 100644 --- a/openedx/core/djangoapps/credentials/tests/test_utils.py +++ b/openedx/core/djangoapps/credentials/tests/test_utils.py @@ -1,218 +1,70 @@ """Tests covering Credentials utilities.""" import uuid -from django.core.cache import cache from edx_oauth2_provider.tests.factories import ClientFactory -import httpretty import mock from nose.plugins.attrib import attr from provider.constants import CONFIDENTIAL -from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory from openedx.core.djangoapps.credentials.models import CredentialsApiConfig -from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin -from openedx.core.djangoapps.credentials.utils import ( - get_credentials, - get_user_program_credentials, - get_programs_credentials, - get_programs_for_credentials -) +from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin +from openedx.core.djangoapps.credentials.utils import get_credentials from openedx.core.djangoapps.credentials.tests import factories from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms from student.tests.factories import UserFactory +UTILS_MODULE = 'openedx.core.djangoapps.credentials.utils' + + @skip_unless_lms @attr(shard=2) -class TestCredentialsRetrieval(CredentialsApiConfigMixin, CredentialsDataMixin, CacheIsolationTestCase): - """ Tests covering the retrieval of user credentials from the Credentials - service. - """ - +@mock.patch(UTILS_MODULE + '.get_edx_api_data') +class TestGetCredentials(CredentialsApiConfigMixin, CacheIsolationTestCase): ENABLED_CACHES = ['default'] def setUp(self): - super(TestCredentialsRetrieval, self).setUp() + super(TestGetCredentials, self).setUp() ClientFactory(name=CredentialsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL) - self.user = UserFactory() - self.primary_uuid = str(uuid.uuid4()) - self.alternate_uuid = str(uuid.uuid4()) - cache.clear() - - def expected_credentials_display_data(self, programs): - """ Returns expected credentials data to be represented. """ - return [ - { - 'display_name': programs[0]['title'], - 'subtitle': programs[0]['subtitle'], - 'credential_url': programs[0]['credential_url'] - }, - { - 'display_name': programs[1]['title'], - 'subtitle': programs[1]['subtitle'], - 'credential_url': programs[1]['credential_url'] - } - ] - - @httpretty.activate - def test_get_credentials(self): - """Verify user credentials data can be retrieve.""" self.create_credentials_config() - self.mock_credentials_api(self.user) + self.user = UserFactory() + + def test_get_many(self, mock_get_edx_api_data): + expected = factories.UserCredential.create_batch(3) + mock_get_edx_api_data.return_value = expected actual = get_credentials(self.user) - self.assertEqual(actual, self.CREDENTIALS_API_RESPONSE['results']) - @httpretty.activate - def test_get_credentials_caching(self): - """Verify that when enabled, the cache is used for non-staff users.""" - self.create_credentials_config(cache_ttl=1) - self.mock_credentials_api(self.user) + mock_get_edx_api_data.assert_called_once() + call = mock_get_edx_api_data.mock_calls[0] + __, __, kwargs = call - # Warm up the cache. - get_credentials(self.user) - - # Hit the cache. - get_credentials(self.user) - - # Verify only one request was made. - self.assertEqual(len(httpretty.httpretty.latest_requests), 1) - - staff_user = UserFactory(is_staff=True) - - # Hit the Credentials API twice. - for _ in range(2): - get_credentials(staff_user) - - # Verify that three requests have been made (one for student, two for staff). - self.assertEqual(len(httpretty.httpretty.latest_requests), 3) - - def test_get_user_program_credentials_issuance_disable(self): - """Verify that user program credentials cannot be retrieved if issuance is disabled.""" - self.create_credentials_config(enable_learner_issuance=False) - actual = get_user_program_credentials(self.user) - self.assertEqual(actual, []) - - @httpretty.activate - def test_get_user_program_credentials_no_credential(self): - """Verify behavior if no credential exist.""" - self.create_credentials_config() - self.mock_credentials_api(self.user, data={'results': []}) - actual = get_user_program_credentials(self.user) - self.assertEqual(actual, []) - - @httpretty.activate - def test_get_user_programs_credentials(self): - """Verify program credentials data can be retrieved and parsed correctly.""" - # create credentials and program configuration - self.create_credentials_config() - - # Mocking the API responses from programs and credentials - primary_uuid, alternate_uuid = str(uuid.uuid4()), str(uuid.uuid4()) - credentials_api_response = { - "next": None, - "results": [ - factories.UserCredential( - username='test', - credential=factories.ProgramCredential(program_uuid=primary_uuid) - ), - factories.UserCredential( - username='test', - credential=factories.ProgramCredential(program_uuid=alternate_uuid) - ) - ] + querystring = { + 'username': self.user.username, + 'status': 'awarded', } - self.mock_credentials_api(self.user, data=credentials_api_response, reset_url=False) - programs = [ - ProgramFactory(uuid=primary_uuid), ProgramFactory(uuid=alternate_uuid) - ] + self.assertEqual(kwargs['querystring'], querystring) - with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs_for_credentials") as mock_get_programs: - mock_get_programs.return_value = programs - actual = get_user_program_credentials(self.user) + self.assertEqual(actual, expected) - # checking response from API is as expected - self.assertEqual(len(actual), 2) - self.assertEqual(actual, programs) + def test_get_one(self, mock_get_edx_api_data): + expected = factories.UserCredential() + mock_get_edx_api_data.return_value = expected - @httpretty.activate - def test_get_programs_credentials(self): - """ Verify that the program credentials data required for display can - be retrieved. - """ - # create credentials and program configuration - self.create_credentials_config() + program_uuid = str(uuid.uuid4()) + actual = get_credentials(self.user, program_uuid=program_uuid) - # Mocking the API responses from programs and credentials - primary_uuid, alternate_uuid = str(uuid.uuid4()), str(uuid.uuid4()) - credentials_api_response = { - "next": None, - "results": [ - factories.UserCredential( - username='test', - credential=factories.ProgramCredential(program_uuid=primary_uuid) - ), - factories.UserCredential( - username='test', - credential=factories.ProgramCredential(program_uuid=alternate_uuid) - ) - ] + mock_get_edx_api_data.assert_called_once() + call = mock_get_edx_api_data.mock_calls[0] + __, __, kwargs = call + + querystring = { + 'username': self.user.username, + 'status': 'awarded', + 'program_uuid': program_uuid, } - self.mock_credentials_api(self.user, data=credentials_api_response, reset_url=False) - programs = [ - ProgramFactory(uuid=primary_uuid), ProgramFactory(uuid=alternate_uuid) - ] + self.assertEqual(kwargs['querystring'], querystring) - with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs") as mock_get_programs: - mock_get_programs.return_value = programs - actual = get_programs_credentials(self.user) - expected = self.expected_credentials_display_data(programs) - - # Checking result is as expected - self.assertEqual(len(actual), 2) - self.assertEqual(actual, expected) - - def _expected_program_credentials_data(self): - """ - Dry method for getting expected program credentials response data. - """ - return [ - factories.UserCredential( - username='test', - credential=factories.ProgramCredential( - program_uuid=self.primary_uuid - ) - ), - factories.UserCredential( - username='test', - credential=factories.ProgramCredential( - program_uuid=self.alternate_uuid - ) - ) - ] - - def test_get_program_for_certificates(self): - """Verify programs data can be retrieved and parsed correctly for certificates.""" - programs = [ - ProgramFactory(uuid=self.primary_uuid), - ProgramFactory(uuid=self.alternate_uuid) - ] - - program_credentials_data = self._expected_program_credentials_data() - with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs") as patched_get_programs: - patched_get_programs.return_value = programs - actual = get_programs_for_credentials(program_credentials_data) - - self.assertEqual(len(actual), 2) - self.assertEqual(actual, programs) - - def test_get_program_for_certificates_no_data(self): - """Verify behavior when no programs data is found for the user.""" - program_credentials_data = self._expected_program_credentials_data() - with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs") as patched_get_programs: - patched_get_programs.return_value = [] - actual = get_programs_for_credentials(program_credentials_data) - - self.assertEqual(actual, []) + self.assertEqual(actual, expected) diff --git a/openedx/core/djangoapps/credentials/utils.py b/openedx/core/djangoapps/credentials/utils.py index 38eced303c..45348afdef 100644 --- a/openedx/core/djangoapps/credentials/utils.py +++ b/openedx/core/djangoapps/credentials/utils.py @@ -1,18 +1,13 @@ """Helper functions for working with Credentials.""" from __future__ import unicode_literals -import logging - from django.conf import settings from edx_rest_api_client.client import EdxRestApiClient -from openedx.core.djangoapps.catalog.utils import get_programs from openedx.core.djangoapps.credentials.models import CredentialsApiConfig from openedx.core.lib.edx_api_utils import get_edx_api_data from openedx.core.lib.token_utils import JwtBuilder -log = logging.getLogger(__name__) - def get_credentials_api_client(user): """ Returns an authenticated Credentials API client. """ @@ -53,89 +48,3 @@ def get_credentials(user, program_uuid=None): return get_edx_api_data( credential_configuration, 'credentials', api=api, querystring=querystring, cache_key=cache_key ) - - -def get_programs_for_credentials(programs_credentials): - """ Given a user and an iterable of credentials, get corresponding programs - data and return it as a list of dictionaries. - - Arguments: - programs_credentials (list): List of credentials awarded to the user - for completion of a program. - - Returns: - list, containing programs dictionaries. - """ - certified_programs = [] - programs = get_programs() - for program in programs: - for credential in programs_credentials: - if program['uuid'] == credential['credential']['program_uuid']: - program['credential_url'] = credential['certificate_url'] - certified_programs.append(program) - - return certified_programs - - -def get_user_program_credentials(user): - """Given a user, get the list of all program credentials earned and returns - list of dictionaries containing related programs data. - - Arguments: - user (User): The user object for getting programs credentials. - - Returns: - list, containing programs dictionaries. - """ - programs_credentials_data = [] - credential_configuration = CredentialsApiConfig.current() - if not credential_configuration.is_learner_issuance_enabled: - log.debug('Display of certificates for programs is disabled.') - return programs_credentials_data - - credentials = get_credentials(user) - if not credentials: - log.info('No credential earned by the given user.') - return programs_credentials_data - - programs_credentials = [] - for credential in credentials: - try: - if 'program_uuid' in credential['credential']: - programs_credentials.append(credential) - except KeyError: - log.exception('Invalid credential structure: %r', credential) - - if programs_credentials: - programs_credentials_data = get_programs_for_credentials(programs_credentials) - - return programs_credentials_data - - -def get_programs_credentials(user): - """Return program credentials data required for display. - - Given a user, find all programs for which certificates have been earned - and return list of dictionaries of required program data. - - Arguments: - user (User): user object for getting programs credentials. - - Returns: - list of dict, containing data corresponding to the programs for which - the user has been awarded a credential. - """ - programs_credentials = get_user_program_credentials(user) - credentials_data = [] - for program in programs_credentials: - try: - program_data = { - 'display_name': program['title'], - 'subtitle': program['subtitle'], - 'credential_url': program['credential_url'], - } - credentials_data.append(program_data) - except KeyError: - log.warning('Program structure is invalid: %r', program) - - return credentials_data diff --git a/openedx/core/djangolib/testing/utils.py b/openedx/core/djangolib/testing/utils.py index 3aca971047..10d106c403 100644 --- a/openedx/core/djangolib/testing/utils.py +++ b/openedx/core/djangolib/testing/utils.py @@ -19,7 +19,6 @@ from django.test import RequestFactory, TestCase, override_settings from django.conf import settings from django.contrib import sites from nose.plugins import Plugin -from waffle.models import Switch from request_cache.middleware import RequestCache