class TestUserPreferenceMiddleware(unittest.TestCase): """ Tests to make sure language configured by the `default_language` column on EnterpriseCustomer is being used. """ def setUp(self): """ Setup middleware, request, session, user and enterprise customer for tests. Also mock imports from edx-platform. """ self.mock_imports() super().setUp() self.middleware = EnterpriseLanguagePreferenceMiddleware() self.session_middleware = SessionMiddleware() self.user = UserFactory.create() self.anonymous_user = AnonymousUserFactory() self.request = RequestFactory().get('/somewhere') self.request.user = self.user self.request.META['HTTP_ACCEPT_LANGUAGE'] = 'ar;q=1.0' self.session_middleware.process_request(self.request) self.client = Client() self.enterprise_customer = EnterpriseCustomerFactory() self.enterprise_customer_user = EnterpriseCustomerUserFactory( enterprise_customer=self.enterprise_customer, user_id=self.user.id, ) def mock_imports(self): """ Mock all the imports from edx-platform """ mocks = [ mock.patch('enterprise.middleware.LANGUAGE_KEY', LANGUAGE_KEY), mock.patch('enterprise.middleware.UserAPIInternalError', UserAPIInternalError), mock.patch('enterprise.middleware.UserAPIRequestError', UserAPIRequestError), mock.patch('enterprise.middleware.get_user_preference', mock.MagicMock(return_value=None)), mock.patch('enterprise.middleware.is_request_from_mobile_app', mock.MagicMock(return_value=False)), mock.patch('enterprise.utils.UserPreference', mock.MagicMock()), ] for mock_object in mocks: self.jwt_builder = mock_object.start() self.addCleanup(mock_object.stop) @ddt.data(None, 'es', 'en') def test_preference_setting_changes_cookie(self, lang_pref_out): """ Validate that the language set via enterprise customer's `default_language` column is used as the learner's default language. """ self.enterprise_customer.default_language = lang_pref_out self.enterprise_customer.save() self.middleware.process_request(self.request) assert getattr(self.request, '_anonymous_user_cookie_lang', None) == lang_pref_out assert LANGUAGE_SESSION_KEY not in self.request.session def test_real_user_extracted_from_request(self): """ Validate the the real_user is used in cases where user is masquerading as someone else. """ # Hide the real user and masquerade as a fake user, fake user does not belong to any enterprise customer. self.request.user = UserFactory() self.request.user.real_user = self.user self.middleware.process_request(self.request) # Make sure the real user is used for setting the language cookie assert getattr(self.request, '_anonymous_user_cookie_lang', None) == self.enterprise_customer.default_language assert LANGUAGE_SESSION_KEY not in self.request.session def test_cookie_not_set_for_anonymous_user(self): """ Validate the language cookie is not set if the request user is not authenticated. """ # Hide the real user and masquerade as a fake user, fake user does not belong to any enterprise customer. self.request.user = self.anonymous_user self.middleware.process_request(self.request) # Make sure the set cookie is not called for anonymous users assert getattr(self.request, '_anonymous_user_cookie_lang', None) is None assert LANGUAGE_SESSION_KEY not in self.request.session def test_cookie_not_set_for_non_enterprise_learners(self): """ Validate the language cookie is not set if the request user does not belong to any enterprise customer. """ # Hide the real user and masquerade as a fake user, fake user does not belong to any enterprise customer. self.request.user = UserFactory() self.middleware.process_request(self.request) # Make sure the set cookie is not called for anonymous users assert getattr(self.request, '_anonymous_user_cookie_lang', None) is None assert LANGUAGE_SESSION_KEY not in self.request.session def test_cookie_when_there_is_no_request_user(self): """ Validate the language cookie is not set if, for some reason, the request user is not present. """ # Hide the real user and masquerade as a fake user, fake user does not belong to any enterprise customer. request = RequestFactory().get('/somewhere') session_middleware = SessionMiddleware() session_middleware.process_request(request) self.middleware.process_request(request) # Make sure the set cookie is not called for anonymous users assert getattr(self.request, '_anonymous_user_cookie_lang', None) is None assert LANGUAGE_SESSION_KEY not in self.request.session def test_errors_are_handled(self): """ Validate that the errors raised when querying user preference are handled correctly. In this case those errors are ignored. """ with mock.patch('enterprise.middleware.get_user_preference') as mock_get_user_preference: mock_get_user_preference.side_effect = UserAPIInternalError self.middleware.process_request(self.request) # Make sure the set cookie is not called for anonymous users # pylint: disable=protected-access,no-member assert self.request._anonymous_user_cookie_lang == self.enterprise_customer.default_language assert LANGUAGE_SESSION_KEY not in self.request.session def test_cookie_not_set_for_mobile_requests(self): """ Validate the language cookie is not set if the request is coming from the mobile app. """ with mock.patch('enterprise.middleware.is_request_from_mobile_app') as mock_is_request_from_mobile_app: mock_is_request_from_mobile_app.return_value = True self.middleware.process_request(self.request) # Make sure the set cookie is not called for anonymous users assert getattr(self.request, '_anonymous_user_cookie_lang', None) is None assert LANGUAGE_SESSION_KEY not in self.request.session
class TestEnterpriseApiClient(unittest.TestCase, EnterpriseMockMixin, CourseDiscoveryApiTestMixin): """ Test enterprise API client methods. """ def setUp(self): """ DRY method for TestEnterpriseApiClient. """ self.enterprise_customer = EnterpriseCustomerFactory( catalog=1, name='Veridian Dynamics', ) super(TestEnterpriseApiClient, self).setUp() self.catalog_api_config_mock = self._make_patch(self._make_catalog_api_location("CatalogIntegration")) self.user = UserFactory(is_staff=True) def tearDown(self): """ Clear any existing cache. """ cache.clear() super(TestEnterpriseApiClient, self).tearDown() def _assert_enterprise_courses_api_response(self, content_ids, content_metadata, expected_count): """ DRY method to verify the enterprise courses api response. """ assert len(content_ids) == len(content_metadata) assert expected_count == len(content_metadata) for item in content_metadata: assert get_content_metadata_item_id(item) in content_ids def _assert_num_requests(self, expected_count): """ DRY helper for verifying request counts. """ assert len(responses.calls) == expected_count @responses.activate @mock.patch('enterprise.api_client.lms.JwtBuilder', mock.Mock()) @mock.patch('enterprise.api_client.discovery.get_edx_api_data', mock.Mock()) @mock.patch('enterprise.api_client.discovery.JwtBuilder', mock.Mock()) def test_no_response_doesnt_get_cached(self): """ Response doesn't get cached when empty. """ uuid = str(self.enterprise_customer.uuid) api_resource_name = 'enterprise-customer' cache_key = get_cache_key( resource=api_resource_name, querystring={}, traverse_pagination=False, resource_id=uuid, ) cached_enterprise_api_response = cache.get(cache_key) assert cached_enterprise_api_response is None self.mock_empty_response('enterprise-customer-courses', uuid) client = enterprise_api.EnterpriseApiClient(self.user) response = client._load_data( # pylint: disable=protected-access resource=api_resource_name, detail_resource='courses', resource_id=uuid, ) assert not response # The empty response is not cached. cached_api_response = cache.get(cache_key) assert not cached_api_response @mock.patch('enterprise.api_client.lms.JwtBuilder', mock.Mock()) def test_skip_request_if_response_cached(self): """ We skip the request portion of the API's logic if the response is already cached. """ cache_key = get_cache_key( resource='resource', querystring={}, traverse_pagination=False, resource_id=None, ) cache_value = {'fake': 'response'} cache.set(cache_key, cache_value, settings.ENTERPRISE_API_CACHE_TIMEOUT) client = enterprise_api.EnterpriseApiClient(self.user) response = client._load_data('resource') # pylint: disable=protected-access assert response == cache_value @responses.activate @mock.patch('enterprise.api_client.lms.JwtBuilder', mock.Mock()) @mock.patch('enterprise.api_client.discovery.get_edx_api_data', mock.Mock()) @mock.patch('enterprise.api_client.discovery.JwtBuilder', mock.Mock()) def test_get_content_metadata(self): """ Verify that the client method `get_content_metadata` works as expected. """ uuid = str(self.enterprise_customer.uuid) course_run_ids = ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2'] self.mock_ent_courses_api_with_pagination( enterprise_uuid=uuid, course_run_ids=course_run_ids ) api_resource_name = 'enterprise-customer' cache_key = get_cache_key( resource=api_resource_name, querystring={}, resource_id=uuid, traverse_pagination=False, ) cached_enterprise_api_response = cache.get(cache_key) self.assertIsNone(cached_enterprise_api_response) # Verify that by default enterprise client fetches all the course runs associated with the catalog. client = enterprise_api.EnterpriseApiClient(self.user) api_response = client.get_content_metadata(self.enterprise_customer) self._assert_enterprise_courses_api_response( ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2'], api_response, expected_count=2 ) # Verify the enterprise API was hit twice self._assert_num_requests(2) @responses.activate @mock.patch('enterprise.api_client.lms.JwtBuilder', mock.Mock()) def test_get_content_metadata_with_enterprise_catalogs(self): """ Verify that the client method `get_content_metadata` works as expected. """ EnterpriseCustomerCatalogFactory( enterprise_customer=self.enterprise_customer, ) uuid = str(self.enterprise_customer.uuid) course_run_ids = ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2'] self.mock_ent_courses_api_with_pagination( enterprise_uuid=uuid, course_run_ids=course_run_ids ) enterprise_catalog_uuid = str(self.enterprise_customer.enterprise_customer_catalogs.first().uuid) self.mock_enterprise_customer_catalogs(enterprise_catalog_uuid) api_resource_name = 'enterprise-customer' cache_key = get_cache_key( resource=api_resource_name, querystring={}, resource_id=uuid, traverse_pagination=False, ) cached_enterprise_api_response = cache.get(cache_key) self.assertIsNone(cached_enterprise_api_response) # Verify that by default enterprise client fetches all the course runs associated with the catalog. client = enterprise_api.EnterpriseApiClient(self.user) course_runs = client.get_content_metadata(self.enterprise_customer) assert len(course_runs) == 5 @responses.activate @mock.patch('enterprise.api_client.lms.JwtBuilder', mock.Mock()) def test_get_content_metadata_with_enterprise_catalog_set_to_none(self): """ Verify that the client method `get_content_metadata` returns courses from associated EnterpriseCustomerCatalog objects only if EnterpriseCustomer.catalog is set to None. """ # Remove EnterpriseCustomer.catalog self.enterprise_customer.catalog = None self.enterprise_customer.save() EnterpriseCustomerCatalogFactory( enterprise_customer=self.enterprise_customer, ) uuid = str(self.enterprise_customer.uuid) course_run_ids = ['course-v1:edX+DemoX+Demo_Course_1', 'course-v1:edX+DemoX+Demo_Course_2'] self.mock_ent_courses_api_with_pagination( enterprise_uuid=uuid, course_run_ids=course_run_ids ) enterprise_catalog_uuid = str(self.enterprise_customer.enterprise_customer_catalogs.first().uuid) self.mock_enterprise_customer_catalogs(enterprise_catalog_uuid) api_resource_name = 'enterprise-customer' cache_key = get_cache_key( resource=api_resource_name, querystring={}, resource_id=uuid, traverse_pagination=False, ) cached_enterprise_api_response = cache.get(cache_key) self.assertIsNone(cached_enterprise_api_response) # Verify that by default enterprise client fetches all the course runs associated with the enterprise catalog. client = enterprise_api.EnterpriseApiClient(self.user) course_runs = client.get_content_metadata(self.enterprise_customer) assert len(course_runs) == 3