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