Commit aaad66d3 by Jesse Shapiro Committed by GitHub

Merge pull request #15706 from open-craft/haikuginger/new-consent-api

[ENT-494] Move to new consent API
parents b649d5a2 7a31441e
...@@ -355,6 +355,7 @@ AUTHENTICATION_BACKENDS = ( ...@@ -355,6 +355,7 @@ AUTHENTICATION_BACKENDS = (
LMS_BASE = None LMS_BASE = None
LMS_ROOT_URL = "http://localhost:8000" LMS_ROOT_URL = "http://localhost:8000"
ENTERPRISE_API_URL = LMS_ROOT_URL + '/enterprise/api/v1/' ENTERPRISE_API_URL = LMS_ROOT_URL + '/enterprise/api/v1/'
ENTERPRISE_CONSENT_API_URL = LMS_ROOT_URL + '/consent/api/v1/'
# These are standard regexes for pulling out info like course_ids, usage_ids, etc. # These are standard regexes for pulling out info like course_ids, usage_ids, etc.
# They are used so that URLs with deprecated-format strings still work. # They are used so that URLs with deprecated-format strings still work.
...@@ -1177,6 +1178,7 @@ OPTIONAL_APPS = ( ...@@ -1177,6 +1178,7 @@ OPTIONAL_APPS = (
# Enterprise App (http://github.com/edx/edx-enterprise) # Enterprise App (http://github.com/edx/edx-enterprise)
('enterprise', None), ('enterprise', None),
('consent', None),
) )
......
...@@ -21,7 +21,6 @@ from lms.djangoapps.commerce.tests import test_utils as ecomm_test_utils ...@@ -21,7 +21,6 @@ from lms.djangoapps.commerce.tests import test_utils as ecomm_test_utils
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.djangoapps.embargo.test_utils import restrict_course
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseServiceMockMixin
from student.models import CourseEnrollment from student.models import CourseEnrollment
from student.tests.factories import CourseEnrollmentFactory, UserFactory from student.tests.factories import CourseEnrollmentFactory, UserFactory
from util import organizations_helpers as organizations_api from util import organizations_helpers as organizations_api
...@@ -35,7 +34,7 @@ from xmodule.modulestore.tests.factories import CourseFactory ...@@ -35,7 +34,7 @@ from xmodule.modulestore.tests.factories import CourseFactory
@attr(shard=3) @attr(shard=3)
@ddt.ddt @ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTestCase, EnterpriseServiceMockMixin, CourseCatalogServiceMockMixin): class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTestCase, CourseCatalogServiceMockMixin):
""" """
Course Mode View tests Course Mode View tests
""" """
...@@ -48,13 +47,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -48,13 +47,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx") self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
self.client.login(username=self.user.username, password="edx") self.client.login(username=self.user.username, password="edx")
# Create a service user, because the track selection page depends on it
UserFactory.create(
username='enterprise_worker',
email="enterprise_worker@example.com",
password="edx",
)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@httpretty.activate @httpretty.activate
@ddt.data( @ddt.data(
...@@ -82,8 +74,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -82,8 +74,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
user=self.user user=self.user
) )
self.mock_enterprise_learner_api()
# Configure whether we're upgrading or not # Configure whether we're upgrading or not
url = reverse('course_modes_choose', args=[unicode(self.course.id)]) url = reverse('course_modes_choose', args=[unicode(self.course.id)])
response = self.client.get(url) response = self.client.get(url)
...@@ -133,109 +123,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -133,109 +123,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
self.assertRedirects(response, 'http://testserver/basket/add/?sku=TEST', fetch_redirect_response=False) self.assertRedirects(response, 'http://testserver/basket/add/?sku=TEST', fetch_redirect_response=False)
ecomm_test_utils.update_commerce_config(enabled=False) ecomm_test_utils.update_commerce_config(enabled=False)
def _generate_enterprise_learner_context(self, enable_audit_enrollment=False):
"""
Internal helper to support common pieces of test case variations
"""
# Create the course modes
for mode in ('audit', 'honor', 'verified'):
CourseModeFactory.create(mode_slug=mode, course_id=self.course.id)
catalog_integration = self.create_catalog_integration()
UserFactory(username=catalog_integration.service_username)
self.mock_course_discovery_api_for_catalog_contains(
catalog_id=1, course_run_ids=[str(self.course.id)]
)
self.mock_enterprise_learner_api(enable_audit_enrollment=enable_audit_enrollment)
return reverse('course_modes_choose', args=[unicode(self.course.id)])
@httpretty.activate
def test_no_enrollment(self):
url = self._generate_enterprise_learner_context()
response = self.client.get(url)
self.assertEquals(response.status_code, 200)
@httpretty.activate
@waffle.testutils.override_switch("populate-multitenant-programs", True)
def test_enterprise_learner_context(self):
"""
Test: Track selection page should show the enterprise context message if user belongs to the Enterprise.
"""
url = self._generate_enterprise_learner_context()
# User visits the track selection page directly without ever enrolling
response = self.client.get(url)
self.assertEquals(response.status_code, 200)
self.assertContains(
response,
'Welcome, {username}! You are about to enroll in {course_name}, from {partner_names}, '
'sponsored by TestShib. Please select your enrollment information below.'.format(
username=self.user.username,
course_name=self.course.display_name_with_default_escaped,
partner_names=self.course.org
)
)
@httpretty.activate
@waffle.testutils.override_switch("populate-multitenant-programs", True)
def test_enterprise_learner_context_with_multiple_organizations(self):
"""
Test: Track selection page should show the enterprise context message with multiple organization names
if user belongs to the Enterprise.
"""
url = self._generate_enterprise_learner_context()
# Creating organization
for i in xrange(2):
test_organization_data = {
'name': 'test organization ' + str(i),
'short_name': 'test_organization_' + str(i),
'description': 'Test Organization Description',
'active': True,
'logo': '/logo_test1.png/'
}
test_org = organizations_api.add_organization(organization_data=test_organization_data)
organizations_api.add_organization_course(organization_data=test_org, course_id=unicode(self.course.id))
# User visits the track selection page directly without ever enrolling
response = self.client.get(url)
self.assertEquals(response.status_code, 200)
self.assertContains(
response,
'Welcome, {username}! You are about to enroll in {course_name}, from test organization 0 and '
'test organization 1, sponsored by TestShib. Please select your enrollment information below.'.format(
username=self.user.username,
course_name=self.course.display_name_with_default_escaped
)
)
@httpretty.activate
@waffle.testutils.override_switch("populate-multitenant-programs", True)
def test_enterprise_learner_context_audit_disabled(self):
"""
Track selection page should hide the audit choice by default in an Enterprise Customer/Learner context
"""
# User visits the track selection page directly without ever enrolling, sees only Verified track choice
url = self._generate_enterprise_learner_context()
response = self.client.get(url)
self.assertContains(response, 'Pursue a Verified Certificate')
self.assertNotContains(response, 'Audit This Course')
@httpretty.activate
def test_enterprise_learner_context_audit_enabled(self):
"""
Track selection page should display Audit choice when specified for an Enterprise Customer
"""
# User visits the track selection page directly without ever enrolling, sees both Verified and Audit choices
url = self._generate_enterprise_learner_context(enable_audit_enrollment=True)
response = self.client.get(url)
self.assertContains(response, 'Pursue a Verified Certificate')
self.assertContains(response, 'Audit This Course')
@httpretty.activate @httpretty.activate
@ddt.data( @ddt.data(
'', '',
...@@ -263,8 +150,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -263,8 +150,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
user=self.user user=self.user
) )
self.mock_enterprise_learner_api()
# Verify that the prices render correctly # Verify that the prices render correctly
response = self.client.get( response = self.client.get(
reverse('course_modes_choose', args=[unicode(self.course.id)]), reverse('course_modes_choose', args=[unicode(self.course.id)]),
...@@ -286,8 +171,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -286,8 +171,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
for mode in available_modes: for mode in available_modes:
CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) CourseModeFactory.create(mode_slug=mode, course_id=self.course.id)
self.mock_enterprise_learner_api()
# Check whether credit upsell is shown on the page # Check whether credit upsell is shown on the page
# This should *only* be shown when a credit mode is available # This should *only* be shown when a credit mode is available
url = reverse('course_modes_choose', args=[unicode(self.course.id)]) url = reverse('course_modes_choose', args=[unicode(self.course.id)])
...@@ -530,8 +413,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -530,8 +413,6 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
for mode in ["honor", "verified"]: for mode in ["honor", "verified"]:
CourseModeFactory.create(mode_slug=mode, course_id=self.course.id) CourseModeFactory.create(mode_slug=mode, course_id=self.course.id)
self.mock_enterprise_learner_api()
# Load the track selection page # Load the track selection page
url = reverse('course_modes_choose', args=[unicode(self.course.id)]) url = reverse('course_modes_choose', args=[unicode(self.course.id)])
response = self.client.get(url) response = self.client.get(url)
...@@ -558,7 +439,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest ...@@ -558,7 +439,7 @@ class CourseModeViewTest(CatalogIntegrationMixin, UrlResetMixin, ModuleStoreTest
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase, EnterpriseServiceMockMixin): class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase):
"""Test embargo restrictions on the track selection page. """ """Test embargo restrictions on the track selection page. """
URLCONF_MODULES = ['openedx.core.djangoapps.embargo'] URLCONF_MODULES = ['openedx.core.djangoapps.embargo']
...@@ -576,13 +457,6 @@ class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase, EnterpriseSe ...@@ -576,13 +457,6 @@ class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase, EnterpriseSe
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx") self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
self.client.login(username=self.user.username, password="edx") self.client.login(username=self.user.username, password="edx")
# Create a service user
UserFactory.create(
username='enterprise_worker',
email="enterprise_worker@example.com",
password="edx",
)
# Construct the URL for the track selection page # Construct the URL for the track selection page
self.url = reverse('course_modes_choose', args=[unicode(self.course.id)]) self.url = reverse('course_modes_choose', args=[unicode(self.course.id)])
...@@ -595,6 +469,5 @@ class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase, EnterpriseSe ...@@ -595,6 +469,5 @@ class TrackSelectionEmbargoTest(UrlResetMixin, ModuleStoreTestCase, EnterpriseSe
@httpretty.activate @httpretty.activate
def test_embargo_allow(self): def test_embargo_allow(self):
self.mock_enterprise_learner_api()
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -24,7 +24,6 @@ from edxmako.shortcuts import render_to_response ...@@ -24,7 +24,6 @@ from edxmako.shortcuts import render_to_response
from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.experiments.utils import get_experiment_user_metadata_context from lms.djangoapps.experiments.utils import get_experiment_user_metadata_context
from openedx.core.djangoapps.embargo import api as embargo_api from openedx.core.djangoapps.embargo import api as embargo_api
from openedx.features.enterprise_support import api as enterprise_api
from student.models import CourseEnrollment from student.models import CourseEnrollment
from third_party_auth.decorators import tpa_hint_ends_existing_session from third_party_auth.decorators import tpa_hint_ends_existing_session
from util import organizations_helpers as organization_api from util import organizations_helpers as organization_api
...@@ -162,36 +161,6 @@ class ChooseModeView(View): ...@@ -162,36 +161,6 @@ class ChooseModeView(View):
title_content = _("Congratulations! You are now enrolled in {course_name}").format( title_content = _("Congratulations! You are now enrolled in {course_name}").format(
course_name=course.display_name_with_default_escaped course_name=course.display_name_with_default_escaped
) )
enterprise_learner_data = enterprise_api.get_enterprise_learner_data(site=request.site, user=request.user)
if enterprise_learner_data:
enterprise_learner = enterprise_learner_data[0]
is_course_in_enterprise_catalog = enterprise_api.is_course_in_enterprise_catalog(
site=request.site,
course_id=course_id,
enterprise_catalog_id=enterprise_learner['enterprise_customer']['catalog']
)
if is_course_in_enterprise_catalog:
partner_names = partner_name = course.display_organization \
if course.display_organization else course.org
enterprise_name = enterprise_learner['enterprise_customer']['name']
organizations = organization_api.get_course_organizations(course_id=course.id)
if organizations:
partner_names = ' and '.join([org.get('name', partner_name) for org in organizations])
title_content = _("Welcome, {username}! You are about to enroll in {course_name},"
" from {partner_names}, sponsored by {enterprise_name}. Please select your enrollment"
" information below.").format(
username=request.user.username,
course_name=course.display_name_with_default_escaped,
partner_names=partner_names,
enterprise_name=enterprise_name
)
# Hide the audit modes for this enterprise customer, if necessary
if not enterprise_learner['enterprise_customer'].get('enable_audit_enrollment'):
for audit_mode in CourseMode.AUDIT_MODES:
modes.pop(audit_mode, None)
context["title_content"] = title_content context["title_content"] = title_content
......
...@@ -56,6 +56,7 @@ class EnrollmentTestMixin(object): ...@@ -56,6 +56,7 @@ class EnrollmentTestMixin(object):
min_mongo_calls=0, min_mongo_calls=0,
max_mongo_calls=0, max_mongo_calls=0,
enterprise_course_consent=None, enterprise_course_consent=None,
linked_enterprise_customer=None,
): ):
""" """
Enroll in the course and verify the response's status code. If the expected status is 200, also validates Enroll in the course and verify the response's status code. If the expected status is 200, also validates
...@@ -85,6 +86,9 @@ class EnrollmentTestMixin(object): ...@@ -85,6 +86,9 @@ class EnrollmentTestMixin(object):
if enterprise_course_consent is not None: if enterprise_course_consent is not None:
data['enterprise_course_consent'] = enterprise_course_consent data['enterprise_course_consent'] = enterprise_course_consent
if linked_enterprise_customer is not None:
data['linked_enterprise_customer'] = linked_enterprise_customer
extra = {} extra = {}
if as_server: if as_server:
extra['HTTP_X_EDX_API_KEY'] = self.API_KEY extra['HTTP_X_EDX_API_KEY'] = self.API_KEY
...@@ -961,6 +965,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente ...@@ -961,6 +965,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
self.assertTrue(is_active) self.assertTrue(is_active)
self.assertEqual(course_mode, updated_mode) self.assertEqual(course_mode, updated_mode)
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=True)
def test_enterprise_course_enrollment_invalid_consent(self): def test_enterprise_course_enrollment_invalid_consent(self):
"""Verify that the enterprise_course_consent must be a boolean. """ """Verify that the enterprise_course_consent must be a boolean. """
CourseModeFactory.create( CourseModeFactory.create(
...@@ -976,6 +981,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente ...@@ -976,6 +981,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
@httpretty.activate @httpretty.activate
@override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker') @override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker')
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=True)
def test_enterprise_course_enrollment_api_error(self): def test_enterprise_course_enrollment_api_error(self):
"""Verify that enterprise service errors are handled properly. """ """Verify that enterprise service errors are handled properly. """
UserFactory.create( UserFactory.create(
...@@ -1003,6 +1009,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente ...@@ -1003,6 +1009,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
@httpretty.activate @httpretty.activate
@override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker') @override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker')
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=True)
def test_enterprise_course_enrollment_successful(self): def test_enterprise_course_enrollment_successful(self):
"""Verify that the enrollment completes when the EnterpriseCourseEnrollment creation succeeds. """ """Verify that the enrollment completes when the EnterpriseCourseEnrollment creation succeeds. """
UserFactory.create( UserFactory.create(
...@@ -1028,6 +1035,43 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente ...@@ -1028,6 +1035,43 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
'No request was made to the mocked enterprise-course-enrollment API' 'No request was made to the mocked enterprise-course-enrollment API'
) )
@httpretty.activate
@override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker')
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=True)
def test_enterprise_course_enrollment_with_ec_uuid(self):
"""Verify that the enrollment completes when the EnterpriseCourseEnrollment creation succeeds. """
UserFactory.create(
username='enterprise_worker',
email=self.EMAIL,
password=self.PASSWORD,
)
CourseModeFactory.create(
course_id=self.course.id,
mode_slug=CourseMode.DEFAULT_MODE_SLUG,
mode_display_name=CourseMode.DEFAULT_MODE_SLUG,
)
consent_kwargs = {
'username': self.user.username,
'course_id': unicode(self.course.id),
'ec_uuid': 'this-is-a-real-uuid'
}
self.mock_consent_missing(**consent_kwargs)
self.mock_consent_post(**consent_kwargs)
self.assert_enrollment_status(
expected_status=status.HTTP_200_OK,
as_server=True,
username='enterprise_worker',
linked_enterprise_customer='this-is-a-real-uuid',
)
self.assertEqual(
httpretty.last_request().path,
'/consent/api/v1/data_sharing_consent',
)
self.assertEqual(
httpretty.last_request().method,
httpretty.POST
)
def test_enrollment_attributes_always_written(self): def test_enrollment_attributes_always_written(self):
""" Enrollment attributes should always be written, regardless of whether """ Enrollment attributes should always be written, regardless of whether
the enrollment is being created or updated. the enrollment is being created or updated.
......
...@@ -29,7 +29,12 @@ from openedx.core.lib.api.authentication import ( ...@@ -29,7 +29,12 @@ from openedx.core.lib.api.authentication import (
from openedx.core.lib.api.permissions import ApiKeyHeaderPermission, ApiKeyHeaderPermissionIsAuthenticated from openedx.core.lib.api.permissions import ApiKeyHeaderPermission, ApiKeyHeaderPermissionIsAuthenticated
from openedx.core.lib.exceptions import CourseNotFoundError from openedx.core.lib.exceptions import CourseNotFoundError
from openedx.core.lib.log_utils import audit_log from openedx.core.lib.log_utils import audit_log
from openedx.features.enterprise_support.api import EnterpriseApiClient, EnterpriseApiException, enterprise_enabled from openedx.features.enterprise_support.api import (
ConsentApiClient,
EnterpriseApiClient,
EnterpriseApiException,
enterprise_enabled
)
from student.auth import user_has_role from student.auth import user_has_role
from student.models import User from student.models import User
from student.roles import CourseStaffRole, GlobalStaff from student.roles import CourseStaffRole, GlobalStaff
...@@ -591,27 +596,42 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn): ...@@ -591,27 +596,42 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
) )
enterprise_course_consent = request.data.get('enterprise_course_consent') enterprise_course_consent = request.data.get('enterprise_course_consent')
# Check if the enterprise_course_enrollment is a boolean explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
if has_api_key_permissions and enterprise_enabled() and enterprise_course_consent is not None: if has_api_key_permissions and enterprise_enabled():
if not isinstance(enterprise_course_consent, bool): # We received an explicitly-linked EnterpriseCustomer for the enrollment
return Response( if explicit_linked_enterprise is not None:
status=status.HTTP_400_BAD_REQUEST, kwargs = {
data={ 'username': username,
'message': (u"'{value}' is an invalid enterprise course consent value.").format( 'course_id': unicode(course_id),
value=enterprise_course_consent 'enterprise_customer_uuid': explicit_linked_enterprise,
) }
} consent_client = ConsentApiClient()
) consent_client.provide_consent(**kwargs)
try:
EnterpriseApiClient().post_enterprise_course_enrollment( # We received an implicit "consent granted" parameter from ecommerce
username, # TODO: Once ecommerce has been deployed with explicit enterprise support, remove this
unicode(course_id), # entire chunk of logic, related tests, and any supporting methods no longer required.
enterprise_course_consent elif enterprise_course_consent is not None:
) # Check if the enterprise_course_enrollment is a boolean
except EnterpriseApiException as error: if not isinstance(enterprise_course_consent, bool):
log.exception("An unexpected error occurred while creating the new EnterpriseCourseEnrollment " return Response(
"for user [%s] in course run [%s]", username, course_id) status=status.HTTP_400_BAD_REQUEST,
raise CourseEnrollmentError(error.message) data={
'message': (u"'{value}' is an invalid enterprise course consent value.").format(
value=enterprise_course_consent
)
}
)
try:
EnterpriseApiClient().post_enterprise_course_enrollment(
username,
unicode(course_id),
enterprise_course_consent
)
except EnterpriseApiException as error:
log.exception("An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
"for user [%s] in course run [%s]", username, course_id)
raise CourseEnrollmentError(error.message)
enrollment_attributes = request.data.get('enrollment_attributes') enrollment_attributes = request.data.get('enrollment_attributes')
enrollment = api.get_enrollment(username, unicode(course_id)) enrollment = api.get_enrollment(username, unicode(course_id))
......
...@@ -12,6 +12,7 @@ from lms.djangoapps.ccx.tests.factories import CcxFactory ...@@ -12,6 +12,7 @@ from lms.djangoapps.ccx.tests.factories import CcxFactory
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
from pyquery import PyQuery as pq from pyquery import PyQuery as pq
from student.models import CourseEnrollment from student.models import CourseEnrollment
from student.tests.factories import AdminFactory from student.tests.factories import AdminFactory
...@@ -32,7 +33,7 @@ QUERY_COUNT_TABLE_BLACKLIST = WAFFLE_TABLES ...@@ -32,7 +33,7 @@ QUERY_COUNT_TABLE_BLACKLIST = WAFFLE_TABLES
@attr(shard=1) @attr(shard=1)
class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase): class CourseInfoTestCase(EnterpriseTestConsentRequired, LoginEnrollmentTestCase, SharedModuleStoreTestCase):
""" """
Tests for the Course Info page Tests for the Course Info page
""" """
...@@ -61,8 +62,7 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase): ...@@ -61,8 +62,7 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
self.assertNotIn("You are not currently enrolled in this course", resp.content) self.assertNotIn("You are not currently enrolled in this course", resp.content)
# TODO: LEARNER-611: If this is only tested under Course Info, does this need to move? # TODO: LEARNER-611: If this is only tested under Course Info, does this need to move?
@mock.patch('openedx.features.enterprise_support.api.get_enterprise_consent_url') def test_redirection_missing_enterprise_consent(self):
def test_redirection_missing_enterprise_consent(self, mock_get_url):
""" """
Verify that users viewing the course info who are enrolled, but have not provided Verify that users viewing the course info who are enrolled, but have not provided
data sharing consent, are first redirected to a consent page, and then, once they've data sharing consent, are first redirected to a consent page, and then, once they've
...@@ -70,19 +70,10 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase): ...@@ -70,19 +70,10 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
""" """
self.setup_user() self.setup_user()
self.enroll(self.course) self.enroll(self.course)
mock_get_url.return_value = reverse('dashboard')
url = reverse('info', args=[self.course.id.to_deprecated_string()])
response = self.client.get(url) url = reverse('info', args=[self.course.id.to_deprecated_string()])
self.assertRedirects( self.verify_consent_required(self.client, url)
response,
reverse('dashboard')
)
mock_get_url.assert_called_once()
mock_get_url.return_value = None
response = self.client.get(url)
self.assertNotIn("You are not currently enrolled in this course", response.content)
def test_anonymous_user(self): def test_anonymous_user(self):
url = reverse('info', args=[self.course.id.to_deprecated_string()]) url = reverse('info', args=[self.course.id.to_deprecated_string()])
......
...@@ -15,6 +15,7 @@ from courseware.tests.factories import ( ...@@ -15,6 +15,7 @@ from courseware.tests.factories import (
StaffFactory StaffFactory
) )
from courseware.tests.helpers import CourseAccessTestMixin, LoginEnrollmentTestCase from courseware.tests.helpers import CourseAccessTestMixin, LoginEnrollmentTestCase
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
from student.tests.factories import CourseEnrollmentFactory, UserFactory from student.tests.factories import CourseEnrollmentFactory, UserFactory
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
...@@ -22,7 +23,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory ...@@ -22,7 +23,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@attr(shard=1) @attr(shard=1)
class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): class TestViewAuth(EnterpriseTestConsentRequired, ModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
Check that view authentication works properly. Check that view authentication works properly.
""" """
...@@ -201,28 +202,18 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -201,28 +202,18 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
) )
) )
@patch('openedx.features.enterprise_support.api.get_enterprise_consent_url') def test_redirection_missing_enterprise_consent(self):
def test_redirection_missing_enterprise_consent(self, mock_get_url):
""" """
Verify that enrolled students are redirected to the Enterprise consent Verify that enrolled students are redirected to the Enterprise consent
URL if a linked Enterprise Customer requires data sharing consent URL if a linked Enterprise Customer requires data sharing consent
and it has not yet been provided. and it has not yet been provided.
""" """
mock_get_url.return_value = reverse('dashboard')
self.login(self.enrolled_user) self.login(self.enrolled_user)
url = reverse( url = reverse(
'courseware', 'courseware',
kwargs={'course_id': self.course.id.to_deprecated_string()} kwargs={'course_id': self.course.id.to_deprecated_string()}
) )
response = self.client.get(url) self.verify_consent_required(self.client, url, status_code=302)
self.assertRedirects(
response,
reverse('dashboard')
)
mock_get_url.assert_called_once()
mock_get_url.return_value = None
response = self.client.get(url)
self.assertNotIn("You are not currently enrolled in this course", response.content)
def test_instructor_page_access_nonstaff(self): def test_instructor_page_access_nonstaff(self):
""" """
......
...@@ -212,8 +212,8 @@ class IndexQueryTestCase(ModuleStoreTestCase): ...@@ -212,8 +212,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
NUM_PROBLEMS = 20 NUM_PROBLEMS = 20
@ddt.data( @ddt.data(
(ModuleStoreEnum.Type.mongo, 10, 146), (ModuleStoreEnum.Type.mongo, 10, 145),
(ModuleStoreEnum.Type.split, 4, 146), (ModuleStoreEnum.Type.split, 4, 145),
) )
@ddt.unpack @ddt.unpack
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count): def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
......
...@@ -472,34 +472,24 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi ...@@ -472,34 +472,24 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
@mock.patch('student_account.views.enterprise_customer_for_request') @mock.patch('student_account.views.enterprise_customer_for_request')
@ddt.data( @ddt.data(
('signin_user', False, None, None, None), ('signin_user', False, None, None),
('register_user', False, None, None, None), ('register_user', False, None, None),
('signin_user', True, 'Fake EC', 'http://logo.com/logo.jpg', u'{enterprise_name} - {platform_name}'), ('signin_user', True, 'Fake EC', 'http://logo.com/logo.jpg'),
('register_user', True, 'Fake EC', 'http://logo.com/logo.jpg', u'{enterprise_name} - {platform_name}'), ('register_user', True, 'Fake EC', 'http://logo.com/logo.jpg'),
('signin_user', True, 'Fake EC', None, u'{enterprise_name} - {platform_name}'), ('signin_user', True, 'Fake EC', None),
('register_user', True, 'Fake EC', None, u'{enterprise_name} - {platform_name}'), ('register_user', True, 'Fake EC', None),
('signin_user', True, 'Fake EC', 'http://logo.com/logo.jpg', None),
('register_user', True, 'Fake EC', 'http://logo.com/logo.jpg', None),
('signin_user', True, 'Fake EC', None, None),
('register_user', True, 'Fake EC', None, None),
) )
@ddt.unpack @ddt.unpack
def test_enterprise_register(self, url_name, ec_present, ec_name, logo_url, welcome_message, mock_get_ec): def test_enterprise_register(self, url_name, ec_present, ec_name, logo_url, mock_get_ec):
""" """
Verify that when an EnterpriseCustomer is received on the login and register views, Verify that when an EnterpriseCustomer is received on the login and register views,
the appropriate sidebar is rendered. the appropriate sidebar is rendered.
""" """
if ec_present: if ec_present:
mock_ec = mock_get_ec.return_value mock_get_ec.return_value = {
mock_ec.name = ec_name 'name': ec_name,
if logo_url: 'branding_configuration': {'logo': logo_url}
mock_ec.branding_configuration.logo.url = logo_url }
else:
mock_ec.branding_configuration.logo = None
if welcome_message:
mock_ec.branding_configuration.welcome_message = welcome_message
else:
del mock_ec.branding_configuration.welcome_message
else: else:
mock_get_ec.return_value = None mock_get_ec.return_value = None
...@@ -511,8 +501,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi ...@@ -511,8 +501,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
self.assertNotContains(response, text=enterprise_sidebar_div_id) self.assertNotContains(response, text=enterprise_sidebar_div_id)
else: else:
self.assertContains(response, text=enterprise_sidebar_div_id) self.assertContains(response, text=enterprise_sidebar_div_id)
if not welcome_message: welcome_message = settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
welcome_message = settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
expected_message = welcome_message.format( expected_message = welcome_message.format(
start_bold=u'<b>', start_bold=u'<b>',
end_bold=u'</b>', end_bold=u'</b>',
......
...@@ -263,23 +263,17 @@ def enterprise_sidebar_context(request): ...@@ -263,23 +263,17 @@ def enterprise_sidebar_context(request):
platform_name = configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME) platform_name = configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME)
if enterprise_customer.branding_configuration.logo: logo_url = enterprise_customer.get('branding_configuration', {}).get('logo', '')
enterprise_logo_url = enterprise_customer.branding_configuration.logo.url
else:
enterprise_logo_url = ''
if getattr(enterprise_customer.branding_configuration, 'welcome_message', None): branded_welcome_template = configuration_helpers.get_value(
branded_welcome_template = enterprise_customer.branding_configuration.welcome_message 'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
else: settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
branded_welcome_template = configuration_helpers.get_value( )
'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
)
branded_welcome_string = branded_welcome_template.format( branded_welcome_string = branded_welcome_template.format(
start_bold=u'<b>', start_bold=u'<b>',
end_bold=u'</b>', end_bold=u'</b>',
enterprise_name=enterprise_customer.name, enterprise_name=enterprise_customer['name'],
platform_name=platform_name platform_name=platform_name
) )
...@@ -290,8 +284,8 @@ def enterprise_sidebar_context(request): ...@@ -290,8 +284,8 @@ def enterprise_sidebar_context(request):
platform_welcome_string = platform_welcome_template.format(platform_name=platform_name) platform_welcome_string = platform_welcome_template.format(platform_name=platform_name)
context = { context = {
'enterprise_name': enterprise_customer.name, 'enterprise_name': enterprise_customer['name'],
'enterprise_logo_url': enterprise_logo_url, 'enterprise_logo_url': logo_url,
'enterprise_branded_welcome_string': branded_welcome_string, 'enterprise_branded_welcome_string': branded_welcome_string,
'platform_welcome_string': platform_welcome_string, 'platform_welcome_string': platform_welcome_string,
} }
......
...@@ -969,6 +969,11 @@ if LMS_ROOT_URL is not None: ...@@ -969,6 +969,11 @@ if LMS_ROOT_URL is not None:
DEFAULT_ENTERPRISE_API_URL = LMS_ROOT_URL + '/enterprise/api/v1/' DEFAULT_ENTERPRISE_API_URL = LMS_ROOT_URL + '/enterprise/api/v1/'
ENTERPRISE_API_URL = ENV_TOKENS.get('ENTERPRISE_API_URL', DEFAULT_ENTERPRISE_API_URL) ENTERPRISE_API_URL = ENV_TOKENS.get('ENTERPRISE_API_URL', DEFAULT_ENTERPRISE_API_URL)
DEFAULT_ENTERPRISE_CONSENT_API_URL = None
if LMS_ROOT_URL is not None:
DEFAULT_ENTERPRISE_CONSENT_API_URL = LMS_ROOT_URL + '/consent/api/v1/'
ENTERPRISE_CONSENT_API_URL = ENV_TOKENS.get('ENTERPRISE_CONSENT_API_URL', DEFAULT_ENTERPRISE_CONSENT_API_URL)
ENTERPRISE_SERVICE_WORKER_USERNAME = ENV_TOKENS.get( ENTERPRISE_SERVICE_WORKER_USERNAME = ENV_TOKENS.get(
'ENTERPRISE_SERVICE_WORKER_USERNAME', 'ENTERPRISE_SERVICE_WORKER_USERNAME',
ENTERPRISE_SERVICE_WORKER_USERNAME ENTERPRISE_SERVICE_WORKER_USERNAME
......
...@@ -3216,6 +3216,7 @@ ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES = ['audit', 'honor'] ...@@ -3216,6 +3216,7 @@ ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES = ['audit', 'honor']
# and are overridden by the configuration parameter accessors defined in aws.py # and are overridden by the configuration parameter accessors defined in aws.py
ENTERPRISE_API_URL = LMS_ROOT_URL + '/enterprise/api/v1/' ENTERPRISE_API_URL = LMS_ROOT_URL + '/enterprise/api/v1/'
ENTERPRISE_CONSENT_API_URL = LMS_ROOT_URL + '/consent/api/v1/'
ENTERPRISE_SERVICE_WORKER_USERNAME = 'enterprise_worker' ENTERPRISE_SERVICE_WORKER_USERNAME = 'enterprise_worker'
ENTERPRISE_API_CACHE_TIMEOUT = 3600 # Value is in seconds ENTERPRISE_API_CACHE_TIMEOUT = 3600 # Value is in seconds
ENTERPRISE_CUSTOMER_LOGO_IMAGE_SIZE = 512 # Enterprise logo image size limit in KB's ENTERPRISE_CUSTOMER_LOGO_IMAGE_SIZE = 512 # Enterprise logo image size limit in KB's
......
...@@ -605,7 +605,9 @@ COMPREHENSIVE_THEME_LOCALE_PATHS = [REPO_ROOT / "themes/conf/locale", ] ...@@ -605,7 +605,9 @@ COMPREHENSIVE_THEME_LOCALE_PATHS = [REPO_ROOT / "themes/conf/locale", ]
LMS_ROOT_URL = "http://localhost:8000" LMS_ROOT_URL = "http://localhost:8000"
ENABLE_ENTERPRISE_INTEGRATION = False
ECOMMERCE_API_URL = 'https://ecommerce.example.com/api/v2/' ECOMMERCE_API_URL = 'https://ecommerce.example.com/api/v2/'
ENTERPRISE_API_URL = 'http://enterprise.example.com/enterprise/api/v1/' ENTERPRISE_API_URL = 'http://enterprise.example.com/enterprise/api/v1/'
ENTERPRISE_CONSENT_API_URL = 'http://enterprise.example.com/consent/api/v1/'
ACTIVATION_EMAIL_FROM_ADDRESS = 'test_activate@edx.org' ACTIVATION_EMAIL_FROM_ADDRESS = 'test_activate@edx.org'
...@@ -160,7 +160,7 @@ class TestCourseHomePage(CourseHomePageTestCase): ...@@ -160,7 +160,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
course_home_url(self.course) course_home_url(self.course)
# Fetch the view and verify the query counts # Fetch the view and verify the query counts
with self.assertNumQueries(42, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with self.assertNumQueries(41, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
with check_mongo_calls(4): with check_mongo_calls(4):
url = course_home_url(self.course) url = course_home_url(self.course)
self.client.get(url) self.client.get(url)
......
...@@ -127,7 +127,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase): ...@@ -127,7 +127,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
course_updates_url(self.course) course_updates_url(self.course)
# Fetch the view and verify that the query counts haven't changed # Fetch the view and verify that the query counts haven't changed
with self.assertNumQueries(33, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with self.assertNumQueries(32, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
with check_mongo_calls(4): with check_mongo_calls(4):
url = course_updates_url(self.course) url = course_updates_url(self.course)
self.client.get(url) self.client.get(url)
...@@ -7,6 +7,8 @@ import mock ...@@ -7,6 +7,8 @@ import mock
import httpretty import httpretty
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.test import SimpleTestCase
class EnterpriseServiceMockMixin(object): class EnterpriseServiceMockMixin(object):
...@@ -14,6 +16,8 @@ class EnterpriseServiceMockMixin(object): ...@@ -14,6 +16,8 @@ class EnterpriseServiceMockMixin(object):
Mocks for the Enterprise service responses. Mocks for the Enterprise service responses.
""" """
consent_url = '{}{}'.format(settings.ENTERPRISE_CONSENT_API_URL, 'data_sharing_consent')
def setUp(self): def setUp(self):
super(EnterpriseServiceMockMixin, self).setUp() super(EnterpriseServiceMockMixin, self).setUp()
cache.clear() cache.clear()
...@@ -23,6 +27,19 @@ class EnterpriseServiceMockMixin(object): ...@@ -23,6 +27,19 @@ class EnterpriseServiceMockMixin(object):
"""Return a URL to the configured Enterprise API. """ """Return a URL to the configured Enterprise API. """
return '{}{}/'.format(settings.ENTERPRISE_API_URL, path) return '{}{}/'.format(settings.ENTERPRISE_API_URL, path)
def mock_get_enterprise_customer(self, uuid, response, status):
"""
Helper to mock the HTTP call to the /enterprise-customer/uuid endpoint
"""
body = json.dumps(response)
httpretty.register_uri(
method=httpretty.GET,
uri=(self.get_enterprise_url('enterprise-customer') + uuid + '/'),
body=body,
content_type='application/json',
status=status,
)
def mock_enterprise_course_enrollment_post_api( # pylint: disable=invalid-name def mock_enterprise_course_enrollment_post_api( # pylint: disable=invalid-name
self, self,
username='test_user', username='test_user',
...@@ -57,6 +74,70 @@ class EnterpriseServiceMockMixin(object): ...@@ -57,6 +74,70 @@ class EnterpriseServiceMockMixin(object):
status=500 status=500
) )
def mock_consent_response(
self,
username,
course_id,
ec_uuid,
method=httpretty.GET,
granted=True,
required=False,
exists=True,
response_code=None
):
response_body = {
'username': username,
'course_id': course_id,
'enterprise_customer_uuid': ec_uuid,
'consent_provided': granted,
'consent_required': required,
'exists': exists,
}
httpretty.register_uri(
method=method,
uri=self.consent_url,
content_type='application/json',
body=json.dumps(response_body),
status=response_code or 200,
)
def mock_consent_post(self, username, course_id, ec_uuid):
self.mock_consent_response(
username,
course_id,
ec_uuid,
method=httpretty.POST,
granted=True,
exists=True,
)
def mock_consent_get(self, username, course_id, ec_uuid):
self.mock_consent_response(
username,
course_id,
ec_uuid
)
def mock_consent_missing(self, username, course_id, ec_uuid):
self.mock_consent_response(
username,
course_id,
ec_uuid,
exists=False,
granted=False,
required=True,
)
def mock_consent_not_required(self, username, course_id, ec_uuid):
self.mock_consent_response(
username,
course_id,
ec_uuid,
exists=False,
granted=False,
required=False,
)
def mock_enterprise_learner_api( def mock_enterprise_learner_api(
self, self,
catalog_id=1, catalog_id=1,
...@@ -134,7 +215,7 @@ class EnterpriseServiceMockMixin(object): ...@@ -134,7 +215,7 @@ class EnterpriseServiceMockMixin(object):
) )
class EnterpriseTestConsentRequired(object): class EnterpriseTestConsentRequired(SimpleTestCase):
""" """
Mixin to help test the data_sharing_consent_required decorator. Mixin to help test the data_sharing_consent_required decorator.
""" """
...@@ -149,20 +230,30 @@ class EnterpriseTestConsentRequired(object): ...@@ -149,20 +230,30 @@ class EnterpriseTestConsentRequired(object):
* url: URL to test * url: URL to test
* status_code: expected status code of URL when no data sharing consent is required. * status_code: expected status code of URL when no data sharing consent is required.
""" """
with mock.patch('openedx.features.enterprise_support.api.enterprise_enabled', return_value=True): def mock_consent_reverse(*args, **kwargs):
with mock.patch('openedx.features.enterprise_support.api.consent_necessary_for_course') as mock_consent_necessary: # pylint: disable=line-too-long if args[0] == 'grant_data_sharing_permissions':
# Ensure that when consent is necessary, the user is redirected to the consent page. return '/enterprise/grant_data_sharing_permissions'
mock_consent_necessary.return_value = True return reverse(*args, **kwargs)
response = client.get(url)
assert response.status_code == 302 with mock.patch('openedx.features.enterprise_support.api.reverse', side_effect=mock_consent_reverse):
assert 'grant_data_sharing_permissions' in response.url # pylint: disable=no-member with mock.patch('openedx.features.enterprise_support.api.enterprise_enabled', return_value=True):
with mock.patch(
# Ensure that when consent is not necessary, the user continues through to the requested page. 'openedx.features.enterprise_support.api.consent_needed_for_course'
mock_consent_necessary.return_value = False ) as mock_consent_necessary:
response = client.get(url) # Ensure that when consent is necessary, the user is redirected to the consent page.
assert response.status_code == status_code mock_consent_necessary.return_value = True
response = client.get(url)
# If we were expecting a redirect, ensure it's not to the data sharing permission page while(response.status_code == 302 and 'grant_data_sharing_permissions' not in response.url):
if status_code == 302: response = client.get(response.url)
assert 'grant_data_sharing_permissions' not in response.url # pylint: disable=no-member self.assertEqual(response.status_code, 302)
return response self.assertIn('grant_data_sharing_permissions', response.url) # pylint: disable=no-member
# Ensure that when consent is not necessary, the user continues through to the requested page.
mock_consent_necessary.return_value = False
response = client.get(url)
self.assertEqual(response.status_code, status_code)
# If we were expecting a redirect, ensure it's not to the data sharing permission page
if status_code == 302:
self.assertNotIn('grant_data_sharing_permissions', response.url) # pylint: disable=no-member
return response
...@@ -48,7 +48,7 @@ edx-lint==0.4.3 ...@@ -48,7 +48,7 @@ edx-lint==0.4.3
astroid==1.3.8 astroid==1.3.8
edx-django-oauth2-provider==1.1.4 edx-django-oauth2-provider==1.1.4
edx-django-sites-extensions==2.3.0 edx-django-sites-extensions==2.3.0
edx-enterprise==0.40.1 edx-enterprise==0.40.2
edx-oauth2-provider==1.2.0 edx-oauth2-provider==1.2.0
edx-opaque-keys==0.4.0 edx-opaque-keys==0.4.0
edx-organizations==0.4.5 edx-organizations==0.4.5
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment