class TestGetUserByEmailUserID:
    """Tests for get_user_by_email_user_id()."""
    @pytest.mark.parametrize(
        'mock_kwargs,expected_exception',
        (
            (
                {
                    'status_code': status.HTTP_400_BAD_REQUEST
                },
                SSORequestError('SSO request failed'),
            ),
            (
                {
                    'status_code': status.HTTP_404_NOT_FOUND
                },
                SSOUserDoesNotExist(),
            ),
            (
                {
                    'exc': ConnectionError
                },
                SSORequestError('SSO service unavailable'),
            ),
            (
                {
                    'text': '{"invalid-json}'
                },
                SSORequestError('SSO response parsing failed'),
            ),
        ),
    )
    def test_error_handling(self, requests_mock, mock_kwargs,
                            expected_exception):
        """Test that various errors are handled as expected."""
        params = {'email_user_id': '*****@*****.**'}
        request_url = f'{settings.STAFF_SSO_BASE_URL}api/v1/user/introspect/?{urlencode(params)}'
        requests_mock.get(request_url, **mock_kwargs)

        with pytest.raises(expected_exception.__class__) as excinfo:
            get_user_by_email_user_id('*****@*****.**')

        assert str(excinfo.value) == str(expected_exception)

    def test_returns_data_on_success(self, requests_mock):
        """Test that user data is returned on success."""
        params = {'email_user_id': '*****@*****.**'}
        request_url = f'{settings.STAFF_SSO_BASE_URL}api/v1/user/introspect/?{urlencode(params)}'
        requests_mock.get(request_url, json=FAKE_SSO_USER_DATA)

        assert get_user_by_email_user_id('*****@*****.**') == FAKE_SSO_USER_DATA
    def test_validation_fails_when_there_is_a_request_error(self, mock_get_user_by_email_user_id):
        """
        Test that validation fails if there's an error communicating with Staff SSO.
        """
        mock_get_user_by_email_user_id.side_effect = SSORequestError('Test error')

        data = {'search_email': '*****@*****.**'}
        form = AddAdviserFromSSOForm(data=data)

        assert form.errors == {
            NON_FIELD_ERRORS: [
                'There was an error communicating with Staff SSO: Test error. Please try again.',
            ],
        }
class TestIntrospectToken:
    """Tests for introspect_token()."""
    @pytest.mark.parametrize(
        'mock_kwargs,expected_exception',
        (
            (
                {
                    'status_code': status.HTTP_400_BAD_REQUEST
                },
                SSORequestError('SSO request failed'),
            ),
            (
                {
                    'status_code': status.HTTP_401_UNAUTHORIZED,
                    'json': {
                        'active': False
                    },
                },
                SSOInvalidToken(),
            ),
            (
                {
                    'status_code': status.HTTP_200_OK,
                    'json': {
                        'active': False
                    },
                },
                SSOInvalidToken(),
            ),
            (
                {
                    'exc': ConnectionError
                },
                SSORequestError('SSO service unavailable'),
            ),
            (
                {
                    'text': '{"invalid-json}'
                },
                SSORequestError('SSO response parsing failed'),
            ),
            # Valid JSON but expected properties missing
            (
                {
                    'json': {}
                },
                SSORequestError('SSO response validation failed'),
            ),
        ),
    )
    def test_error_handling(self, requests_mock, mock_kwargs,
                            expected_exception):
        """Test that various errors are handled as expected."""
        requests_mock.post(
            f'{settings.STAFF_SSO_BASE_URL}o/introspect/',
            **mock_kwargs,
        )
        with pytest.raises(expected_exception.__class__) as excinfo:
            introspect_token('test-token')

        assert str(excinfo.value) == str(expected_exception)

    def test_returns_validated_data(self, requests_mock):
        """Test that introspected token data is returned on success."""
        mock_data = {
            'active': True,
            'username': '******',
            'email_user_id': '*****@*****.**',
            'exp': 1584118925,
        }
        requests_mock.post(
            f'{settings.STAFF_SSO_BASE_URL}o/introspect/',
            json=mock_data,
        )
        assert introspect_token('test-token') == mock_data
        assert requests_mock.last_request.text == 'token=test-token'