Example #1
0
class FxOSMalformedPOSTTest(TestCase):
    """Bug 962225"""

    def setUp(self):
        self.rf = RequestFactory()

    def test_deals_with_broken_post_data(self, update_user_mock):
        """Should be able to parse data from the raw request body.

        FxOS sends POST requests with the wrong mime-type, so request.POST is never
        filled out. We should parse the raw request body to get the data until this
        is fixed in FxOS in bug 949170.
        """
        req = self.rf.generic(
            "POST",
            "/news/subscribe/",
            data="[email protected]&newsletters=firefox-os",
            content_type="text/plain; charset=UTF-8",
        )
        self.assertFalse(bool(req.POST))
        views.subscribe(req)
        update_user_mock.assert_called_with(
            req,
            views.SUBSCRIBE,
            data={"email": "*****@*****.**", "newsletters": "firefox-os"},
            optin=False,
            sync=False,
        )
Example #2
0
class FxOSMalformedPOSTTest(TestCase):
    """Bug 962225"""
    def setUp(self):
        self.rf = RequestFactory()

    def test_deals_with_broken_post_data(self, update_user_mock):
        """Should be able to parse data from the raw request body.

        FxOS sends POST requests with the wrong mime-type, so request.POST is never
        filled out. We should parse the raw request body to get the data until this
        is fixed in FxOS in bug 949170.
        """
        req = self.rf.generic(
            'POST',
            '/news/subscribe/',
            data='[email protected]&newsletters=firefox-os',
            content_type='text/plain; charset=UTF-8')
        self.assertFalse(bool(req.POST))
        views.subscribe(req)
        update_user_mock.assert_called_with(req,
                                            views.SUBSCRIBE,
                                            data={
                                                'email':
                                                '*****@*****.**',
                                                'newsletters': 'firefox-os',
                                            },
                                            optin=False,
                                            sync=False)
Example #3
0
def create_request_from_job(job_data, raw_data=''):

    if DEBUG_BACKGROUND is True:
        logger.info('create_request_from_job: %r', job_data)
    if raw_data:
        logger.info('raw_data: %r, %d', type(raw_data), len(raw_data))
        if len(raw_data) < 1000:
            logger.info('raw_data: %r', raw_data)
        else:
            logger.info('raw_data(trunc): %r', raw_data[:1000])
    request_factory = RequestFactory()

    path = job_data[JOB.URI]
    method = job_data[JOB.METHOD]
    encoding = job_data[JOB.ENCODING]
    content_type = job_data[JOB.CONTENT_TYPE]
    # For job processing, force response to JSON
    #     accept = urllib.unquote(job_data[JOB.HTTP_ACCEPT])
    accept = JSON_MIMETYPE
    comment = job_data[JOB.COMMENT]
    params = job_data[JOB.PARAMS]
    if params:
        params = json.loads(params)
    else:
        params = {}
    params[HEADER_APILOG_COMMENT_CLIENT] = comment
    params[HEADER_APILOG_COMMENT] = comment

    if raw_data:
        if DEBUG_BACKGROUND is True:
            logger.info('add raw data: %d', len(raw_data))
        if MULTIPART_MIMETYPE not in content_type:
            msg = 'content type must contain %r for raw data post: found: %r'\
                 % (MULTIPART_MIMETYPE, content_type)
            logger.error(msg)
            raise ValidationError(key=JOB.CONTENT_TYPE, msg=msg)
        if method != 'POST':
            errmsg = 'method %r is not %r, required for raw data post' % (
                method, 'POST')
            raise ValidationError(key=JOB.METHOD, msg=errmsg)
    else:
        if DEBUG_BACKGROUND is True:
            logger.info('no raw data to add')

    if DEBUG_BACKGROUND is True:
        logger.info('create_request_from_job content type: %r', content_type)
    request = request_factory.generic(method,
                                      path,
                                      data=raw_data,
                                      HTTP_ACCEPT=accept,
                                      content_type=content_type,
                                      **params)

    if DEBUG_BACKGROUND is True:
        logger.info('create_request_from_job: META: %r', request.META)
        logger.info('create_request_from_job: FILES: %r', request.FILES)

    return request
Example #4
0
def _create_mock_json_request(user, data, method='POST'):
    """
    Returns a mock JSON request for the specified user.
    """
    factory = RequestFactory()
    request = factory.generic(method, '/', content_type='application/json', data=json.dumps(data))
    request.user = user
    request.session = {}
    return request
Example #5
0
def _create_mock_json_request(user, data, method='POST'):
    """
    Returns a mock JSON request for the specified user.
    """
    factory = RequestFactory()
    request = factory.generic(method, '/', content_type='application/json', data=json.dumps(data))
    request.user = user
    request.session = {}
    return request
def create_request_from_job(job_data, raw_data=''):
    
    if DEBUG_BACKGROUND is True:
        logger.info('create_request_from_job: %r', job_data)
    if raw_data:
        logger.info('raw_data: %r, %d', type(raw_data), len(raw_data))
        if len(raw_data) < 1000:
            logger.info('raw_data: %r', raw_data)
        else: 
            logger.info('raw_data(trunc): %r', raw_data[:1000])
    request_factory = RequestFactory()
    
    path = job_data[JOB.URI]
    method = job_data[JOB.METHOD]
    encoding = job_data[JOB.ENCODING]
    content_type = job_data[JOB.CONTENT_TYPE]
    # For job processing, force response to JSON
    #     accept = urllib.unquote(job_data[JOB.HTTP_ACCEPT])
    accept = JSON_MIMETYPE
    comment = job_data[JOB.COMMENT]
    params = job_data[JOB.PARAMS]
    if params:
        params = json.loads(params)
    else:
        params = {}
    params[HEADER_APILOG_COMMENT_CLIENT] = comment
    params[HEADER_APILOG_COMMENT] = comment
    
    if raw_data:
        if DEBUG_BACKGROUND is True:
            logger.info('add raw data: %d', len(raw_data))
        if MULTIPART_MIMETYPE not in content_type:
            msg = 'content type must contain %r for raw data post: found: %r'\
                 % (MULTIPART_MIMETYPE, content_type)
            logger.error(msg)
            raise ValidationError(key=JOB.CONTENT_TYPE, msg=msg)
        if method != 'POST':
            errmsg = 'method %r is not %r, required for raw data post' % (
                method, 'POST')
            raise ValidationError(key=JOB.METHOD, msg=errmsg)
    else:
        if DEBUG_BACKGROUND is True:
            logger.info('no raw data to add')
        
    if DEBUG_BACKGROUND is True:
        logger.info('create_request_from_job content type: %r', content_type)
    request = request_factory.generic(
        method, path, data=raw_data, HTTP_ACCEPT=accept,
        content_type=content_type,**params )
    
    if DEBUG_BACKGROUND is True:
        logger.info('create_request_from_job: META: %r', request.META )
        logger.info('create_request_from_job: FILES: %r', request.FILES )
    
    return request
Example #7
0
    class DjangoTestAdapter(requests.adapters.HTTPAdapter):
        """
        A transport adapter for `requests`, that makes requests via the
        Django WSGI app, rather than making actual HTTP requests over the network.
        """
        def __init__(self):
            self.app = WSGIHandler()
            self.factory = DjangoRequestFactory()

        def get_environ(self, request):
            """
            Given a `requests.PreparedRequest` instance, return a WSGI environ dict.
            """
            method = request.method
            url = request.url
            kwargs = {}

            # Set request content, if any exists.
            if request.body is not None:
                if hasattr(request.body, 'read'):
                    kwargs['data'] = request.body.read()
                else:
                    kwargs['data'] = request.body
            if 'content-type' in request.headers:
                kwargs['content_type'] = request.headers['content-type']

            # Set request headers.
            for key, value in request.headers.items():
                key = key.upper()
                if key in ('CONNECTION', 'CONTENT-LENGTH', 'CONTENT-TYPE'):
                    continue
                kwargs['HTTP_%s' % key.replace('-', '_')] = value

            return self.factory.generic(method, url, **kwargs).environ

        def send(self, request, *args, **kwargs):
            """
            Make an outgoing request to the Django WSGI application.
            """
            raw_kwargs = {}

            def start_response(wsgi_status, wsgi_headers):
                status, _, reason = wsgi_status.partition(' ')
                raw_kwargs['status'] = int(status)
                raw_kwargs['reason'] = reason
                raw_kwargs['headers'] = wsgi_headers
                raw_kwargs['version'] = 11
                raw_kwargs['preload_content'] = False
                raw_kwargs['original_response'] = MockOriginalResponse(wsgi_headers)

            # Make the outgoing request via WSGI.
            environ = self.get_environ(request)
            wsgi_response = self.app(environ, start_response)

            # Build the underlying urllib3.HTTPResponse
            raw_kwargs['body'] = io.BytesIO(b''.join(wsgi_response))
            raw = requests.packages.urllib3.HTTPResponse(**raw_kwargs)

            # Build the requests.Response
            return self.build_response(request, raw)

        def close(self):
            pass
Example #8
0
    class DjangoTestAdapter(requests.adapters.HTTPAdapter):
        """
        A transport adapter for `requests`, that makes requests via the
        Django WSGI app, rather than making actual HTTP requests over the network.
        """
        def __init__(self):
            self.app = WSGIHandler()
            self.factory = DjangoRequestFactory()

        def get_environ(self, request):
            """
            Given a `requests.PreparedRequest` instance, return a WSGI environ dict.
            """
            method = request.method
            url = request.url
            kwargs = {}

            # Set request content, if any exists.
            if request.body is not None:
                if hasattr(request.body, 'read'):
                    kwargs['data'] = request.body.read()
                else:
                    kwargs['data'] = request.body
            if 'content-type' in request.headers:
                kwargs['content_type'] = request.headers['content-type']

            # Set request headers.
            for key, value in request.headers.items():
                key = key.upper()
                if key in ('CONNECTION', 'CONTENT-LENGTH', 'CONTENT-TYPE'):
                    continue
                kwargs['HTTP_%s' % key.replace('-', '_')] = value

            return self.factory.generic(method, url, **kwargs).environ

        def send(self, request, *args, **kwargs):
            """
            Make an outgoing request to the Django WSGI application.
            """
            raw_kwargs = {}

            def start_response(wsgi_status, wsgi_headers):
                status, _, reason = wsgi_status.partition(' ')
                raw_kwargs['status'] = int(status)
                raw_kwargs['reason'] = reason
                raw_kwargs['headers'] = wsgi_headers
                raw_kwargs['version'] = 11
                raw_kwargs['preload_content'] = False
                raw_kwargs['original_response'] = MockOriginalResponse(
                    wsgi_headers)

            # Make the outgoing request via WSGI.
            environ = self.get_environ(request)
            wsgi_response = self.app(environ, start_response)

            # Build the underlying urllib3.HTTPResponse
            raw_kwargs['body'] = io.BytesIO(b''.join(wsgi_response))
            raw = requests.packages.urllib3.HTTPResponse(**raw_kwargs)

            # Build the requests.Response
            return self.build_response(request, raw)

        def close(self):
            pass
Example #9
0
class LocalApiAdapter(BaseAdapter):

    SPECIAL_URL = "http://localapi"

    def __init__(self):
        self.request_factory = RequestFactory()
        super(LocalApiAdapter, self).__init__()

    def prepared_request_to_wsgi_request(self, prepared_request):
        """
        transform a PreparedRequest into a WsgiRequest for django to use
        :param requests.models.PreparedRequest prepared_request:
        :return: the request ready to use for django
        :rtype: django.core.handlers.wsgi.WSGIRequest
        """
        wsgi_request = self.request_factory.generic(
            method=prepared_request.method,
            path=prepared_request.url,
            data=prepared_request.body,
            content_type=prepared_request.headers.get(
                'Content-Type', 'application/x-www-form-urlencoded'))
        for name, val in prepared_request.headers.items():
            wsgi_request.META['HTTP_' + name.upper()] = val
        return wsgi_request

    def http_response_to_response(self, http_response, prepared_request):
        """
        transform a WSGIResponse into a requests's Response model
        :param django.http.response.HttpResponse http_response: the http response send by django view
        :return: the requests's Response model corresponding to the http_response
        :rtype: Response
        """
        response = Response()

        # Fallback to None if there's no status_code, for whatever reason.
        response.status_code = getattr(http_response, 'status_code', None)

        # Make headers case-insensitive.
        response.headers = CaseInsensitiveDict(
            getattr(http_response._headers, 'headers', {}))

        # Set encoding.
        response.encoding = get_encoding_from_headers(response.headers)
        response.raw = http_response
        response.reason = response.raw.reason_phrase
        response._content = http_response.content
        req = prepared_request

        if isinstance(req.url, bytes):  # pragma: no cover
            response.url = req.url.decode('utf-8')
        else:
            response.url = req.url

        # Add new cookies from the server.
        extract_cookies_to_jar(response.cookies, req, response)

        # Give the Response some context.
        response.request = req
        response.connection = self

        return response

    def send(self,
             request,
             stream=False,
             timeout=None,
             verify=True,
             cert=None,
             proxies=None):
        handler = BaseHandler()
        handler.load_middleware()
        wsgi_request = self.prepared_request_to_wsgi_request(request)
        http_response = handler.get_response(wsgi_request)
        requests_response = self.http_response_to_response(
            http_response, request)
        return requests_response

    def close(self):  # pragma: no cover
        pass
Example #10
0
class SubscribeTests(ViewsPatcherMixin, TestCase):
    def setUp(self):
        self.factory = RequestFactory()

        self._patch_views('update_user_task')
        self._patch_views('process_email')
        self._patch_views('is_authorized')

    def tearDown(self):
        cache.clear()
        email_block_list_cache.clear()

    def assert_response_error(self, response, status_code, basket_code):
        self.assertEqual(response.status_code, status_code)
        response_data = json.loads(response.content)
        self.assertEqual(response_data['code'], basket_code)

    def test_newsletters_missing(self):
        """If the newsletters param is missing, return a 400."""
        request = self.factory.post('/')
        response = views.subscribe(request)
        self.assert_response_error(response, 400, errors.BASKET_USAGE_ERROR)

    def test_optin_valid_api_key_required(self):
        """
        If optin is 'Y' but the API key isn't valid, disable optin.
        """
        request_data = {
            'newsletters': 'asdf',
            'optin': 'Y',
            'email': '*****@*****.**'
        }
        update_data = request_data.copy()
        del update_data['optin']
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/', request_data)
        self.is_authorized.return_value = False

        response = views.subscribe(request)
        self.assertEqual(response, self.update_user_task.return_value)
        self.update_user_task.assert_called_with(request,
                                                 SUBSCRIBE,
                                                 data=update_data,
                                                 optin=False,
                                                 sync=False)

    def test_sync_invalid_api_key(self):
        """
        If sync is set to 'Y' and the request has an invalid API key,
        return a 401.
        """
        request = self.factory.post('/', {
            'newsletters': 'asdf',
            'sync': 'Y',
            'email': '*****@*****.**'
        })
        self.is_authorized.return_value = False

        response = views.subscribe(request)
        self.assert_response_error(response, 401, errors.BASKET_AUTH_ERROR)
        self.is_authorized.assert_called_with(request,
                                              self.process_email.return_value)

    def test_email_validation_error(self):
        """
        If process_email returns None, return an
        invalid email response.
        """
        request_data = {'newsletters': 'asdf', 'email': '*****@*****.**'}
        request = self.factory.post('/', request_data)
        self.process_email.return_value = None

        with patch('basket.news.views.invalid_email_response'
                   ) as invalid_email_response:
            response = views.subscribe(request)
            self.assertEqual(response, invalid_email_response.return_value)
            self.process_email.assert_called_with(request_data['email'])
            invalid_email_response.assert_called()

    @patch('basket.news.utils.get_email_block_list')
    def test_blocked_email(self, get_block_list_mock):
        """Test basic success case with no optin or sync."""
        get_block_list_mock.return_value = ['example.com']
        request_data = {
            'newsletters': 'news,lets',
            'optin': 'N',
            'sync': 'N',
            'email': '*****@*****.**'
        }
        request = self.factory.post('/', request_data)

        response = views.subscribe(request)
        self.assertEqual(response.status_code, 200)
        self.assertFalse(self.update_user_task.called)

    @patch('basket.news.views.update_user_task')
    def test_email_fxos_malformed_post_bad_data(self, update_user_mock):
        """Should be able to parse data from the raw request body even with bad data."""
        # example from real error with PII details changed
        self.process_email.return_value = '*****@*****.**'
        req = self.factory.generic(
            'POST',
            '/news/subscribe/',
            data='newsletters=mozilla-foundation&'
            'source_url=https%3A%2F%2Fadvocacy.mozilla.org%2Fencrypt'
            '&lang=en&[email protected]'
            '&country=DE&first_name=Dude&Walter',
            content_type='text/plain; charset=UTF-8')
        views.subscribe(req)
        update_user_mock.assert_called_with(
            req,
            views.SUBSCRIBE,
            data={
                'email': '*****@*****.**',
                'newsletters': 'mozilla-foundation',
                'source_url': 'https%3A%2F%2Fadvocacy.mozilla.org%2Fencrypt',
                'lang': 'en',
                'country': 'DE',
                'first_name': 'Dude',
            },
            optin=False,
            sync=False)

    @patch('basket.news.views.update_user_task')
    def test_email_fxos_malformed_post(self, update_user_mock):
        """Should be able to parse data from the raw request body."""
        self.process_email.return_value = '*****@*****.**'
        req = self.factory.generic(
            'POST',
            '/news/subscribe/',
            data='[email protected]&newsletters=firefox-os',
            content_type='text/plain; charset=UTF-8')
        views.subscribe(req)
        update_user_mock.assert_called_with(req,
                                            views.SUBSCRIBE,
                                            data={
                                                'email': '*****@*****.**',
                                                'newsletters': 'firefox-os',
                                            },
                                            optin=False,
                                            sync=False)

    def test_no_source_url_referrer(self):
        """Test referrer used when no source_url."""
        request_data = {
            'newsletters': 'news,lets',
            'optin': 'N',
            'sync': 'N',
            'email': '*****@*****.**',
            'first_name': 'The',
            'last_name': 'Dude'
        }
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        update_data['source_url'] = 'https://example.com/newsletter'
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/',
                                    request_data,
                                    HTTP_REFERER=update_data['source_url'])

        response = views.subscribe(request)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with(request_data['email'])
        self.update_user_task.assert_called_with(request,
                                                 SUBSCRIBE,
                                                 data=update_data,
                                                 optin=False,
                                                 sync=False)

    def test_source_url_overrides_referrer(self):
        """Test source_url used when referrer also provided."""
        request_data = {
            'newsletters': 'news,lets',
            'optin': 'N',
            'sync': 'N',
            'email': '*****@*****.**',
            'first_name': 'The',
            'last_name': 'Dude',
            'source_url': 'https://example.com/thedude'
        }
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/',
                                    request_data,
                                    HTTP_REFERER='https://example.com/donnie')

        response = views.subscribe(request)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with(request_data['email'])
        self.update_user_task.assert_called_with(request,
                                                 SUBSCRIBE,
                                                 data=update_data,
                                                 optin=False,
                                                 sync=False)

    def test_success(self):
        """Test basic success case with no optin or sync."""
        request_data = {
            'newsletters': 'news,lets',
            'optin': 'N',
            'sync': 'N',
            'email': '*****@*****.**',
            'first_name': 'The',
            'last_name': 'Dude'
        }
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/', request_data)

        response = views.subscribe(request)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with(request_data['email'])
        self.update_user_task.assert_called_with(request,
                                                 SUBSCRIBE,
                                                 data=update_data,
                                                 optin=False,
                                                 sync=False)

    def test_success_sync_optin(self):
        """Test success case with optin and sync."""
        request_data = {
            'newsletters': 'news,lets',
            'optin': 'Y',
            'sync': 'Y',
            'email': '*****@*****.**'
        }
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        request = self.factory.post('/', request_data)
        self.is_authorized.return_value = True
        self.process_email.return_value = update_data['email']

        response = views.subscribe(request)

        self.is_authorized.assert_called_with(request,
                                              self.process_email.return_value)
        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with('*****@*****.**')
        self.update_user_task.assert_called_with(request,
                                                 SUBSCRIBE,
                                                 data=update_data,
                                                 optin=True,
                                                 sync=True)

    def test_success_sync_optin_lowercase(self):
        """Test success case with optin and sync, using lowercase y."""
        request_data = {
            'newsletters': 'news,lets',
            'optin': 'y',
            'sync': 'y',
            'email': '*****@*****.**'
        }
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        request = self.factory.post('/', request_data)
        self.process_email.return_value = update_data['email']
        self.is_authorized.return_value = True
        response = views.subscribe(request)
        self.is_authorized.assert_called_with(request,
                                              self.process_email.return_value)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with('*****@*****.**')
        self.update_user_task.assert_called_with(request,
                                                 SUBSCRIBE,
                                                 data=update_data,
                                                 optin=True,
                                                 sync=True)
Example #11
0
class SubscribeEmailValidationTest(TestCase):
    email = '*****@*****.**'
    data = {
        'email': email,
        'newsletters': 'os',
    }
    view = 'subscribe'

    def setUp(self):
        self.rf = RequestFactory()

    @patch('basket.news.views.process_email')
    def test_invalid_email(self, mock_validate):
        """Should return proper error for invalid email."""
        mock_validate.return_value = None
        view = getattr(views, self.view)
        resp = view(self.rf.post('/', self.data))
        resp_data = json.loads(resp.content)
        mock_validate.assert_called_with(self.email)
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp_data['status'], 'error')
        self.assertEqual(resp_data['code'], errors.BASKET_INVALID_EMAIL)

    @patch('basket.news.views.update_user_task')
    def test_non_ascii_email_fxos_malformed_post(self, update_user_mock):
        """Should be able to parse data from the raw request body including non-ascii chars."""
        req = self.rf.generic('POST',
                              '/news/subscribe/',
                              data='email=dude@黒川.日本&newsletters=firefox-os',
                              content_type='text/plain; charset=UTF-8')
        views.subscribe(req)
        update_user_mock.assert_called_with(req,
                                            views.SUBSCRIBE,
                                            data={
                                                'email':
                                                u'[email protected]',
                                                'newsletters': 'firefox-os',
                                            },
                                            optin=False,
                                            sync=False)

    @patch('basket.news.views.update_user_task')
    def test_non_ascii_email(self, update_user_mock):
        """Should be able to accept valid email including non-ascii chars."""
        req = self.rf.post('/news/subscribe/',
                           data={
                               'email': 'dude@黒川.日本',
                               'newsletters': 'firefox-os'
                           })
        views.subscribe(req)
        update_user_mock.assert_called_with(req,
                                            views.SUBSCRIBE,
                                            data={
                                                'email':
                                                u'[email protected]',
                                                'newsletters': 'firefox-os',
                                            },
                                            optin=False,
                                            sync=False)

    @patch('basket.news.views.update_user_task')
    def test_empty_email_invalid(self, update_user_mock):
        """Should report an error for missing or empty value."""
        req = self.rf.post('/news/subscribe/',
                           data={
                               'email': '',
                               'newsletters': 'firefox-os'
                           })
        resp = views.subscribe(req)
        resp_data = json.loads(resp.content)
        self.assertEqual(resp_data['status'], 'error')
        self.assertEqual(resp_data['code'], errors.BASKET_INVALID_EMAIL)
        self.assertFalse(update_user_mock.called)

        # no email at all
        req = self.rf.post('/news/subscribe/',
                           data={'newsletters': 'firefox-os'})
        resp = views.subscribe(req)
        resp_data = json.loads(resp.content)
        self.assertEqual(resp_data['status'], 'error')
        self.assertEqual(resp_data['code'], errors.BASKET_USAGE_ERROR)
        self.assertFalse(update_user_mock.called)
Example #12
0
class SubscribeTests(ViewsPatcherMixin, TestCase):
    def setUp(self):
        self.factory = RequestFactory()

        self._patch_views('update_user_task')
        self._patch_views('process_email')
        self._patch_views('has_valid_api_key')

    def tearDown(self):
        cache.clear()
        email_block_list_cache.clear()

    def assert_response_error(self, response, status_code, basket_code):
        self.assertEqual(response.status_code, status_code)
        response_data = json.loads(response.content)
        self.assertEqual(response_data['code'], basket_code)

    def test_newsletters_missing(self):
        """If the newsletters param is missing, return a 400."""
        request = self.factory.post('/')
        response = views.subscribe(request)
        self.assert_response_error(response, 400, errors.BASKET_USAGE_ERROR)

    def test_optin_valid_api_key_required(self):
        """
        If optin is 'Y' but the API key isn't valid, disable optin.
        """
        request_data = {'newsletters': 'asdf', 'optin': 'Y', 'email': '*****@*****.**'}
        update_data = request_data.copy()
        del update_data['optin']
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/', request_data)
        self.has_valid_api_key.return_value = False

        response = views.subscribe(request)
        self.assertEqual(response, self.update_user_task.return_value)
        self.update_user_task.assert_called_with(request, SUBSCRIBE, data=update_data,
                                                 optin=False, sync=False)

    def test_sync_invalid_api_key(self):
        """
        If sync is set to 'Y' and the request has an invalid API key,
        return a 401.
        """
        request = self.factory.post('/', {'newsletters': 'asdf', 'sync': 'Y',
                                          'email': '*****@*****.**'})
        self.has_valid_api_key.return_value = False

        response = views.subscribe(request)
        self.assert_response_error(response, 401, errors.BASKET_AUTH_ERROR)
        self.has_valid_api_key.assert_called_with(request)

    def test_email_validation_error(self):
        """
        If process_email returns None, return an
        invalid email response.
        """
        request_data = {'newsletters': 'asdf', 'email': '*****@*****.**'}
        request = self.factory.post('/', request_data)
        self.process_email.return_value = None

        with patch('basket.news.views.invalid_email_response') as invalid_email_response:
            response = views.subscribe(request)
            self.assertEqual(response, invalid_email_response.return_value)
            self.process_email.assert_called_with(request_data['email'])
            invalid_email_response.assert_called()

    @patch('basket.news.utils.get_email_block_list')
    def test_blocked_email(self, get_block_list_mock):
        """Test basic success case with no optin or sync."""
        get_block_list_mock.return_value = ['example.com']
        request_data = {'newsletters': 'news,lets', 'optin': 'N', 'sync': 'N',
                        'email': '*****@*****.**'}
        request = self.factory.post('/', request_data)

        response = views.subscribe(request)
        self.assertEqual(response.status_code, 200)
        self.assertFalse(self.update_user_task.called)

    @patch('basket.news.views.update_user_task')
    def test_email_fxos_malformed_post_bad_data(self, update_user_mock):
        """Should be able to parse data from the raw request body even with bad data."""
        # example from real error with PII details changed
        self.process_email.return_value = '*****@*****.**'
        req = self.factory.generic('POST', '/news/subscribe/',
                                   data='newsletters=mozilla-foundation&'
                                        'source_url=https%3A%2F%2Fadvocacy.mozilla.org%2Fencrypt'
                                        '&lang=en&[email protected]'
                                        '&country=DE&first_name=Dude&Walter',
                                   content_type='text/plain; charset=UTF-8')
        views.subscribe(req)
        update_user_mock.assert_called_with(req, views.SUBSCRIBE, data={
            'email': '*****@*****.**',
            'newsletters': 'mozilla-foundation',
            'source_url': 'https%3A%2F%2Fadvocacy.mozilla.org%2Fencrypt',
            'lang': 'en',
            'country': 'DE',
            'first_name': 'Dude',
        }, optin=False, sync=False)

    @patch('basket.news.views.update_user_task')
    def test_email_fxos_malformed_post(self, update_user_mock):
        """Should be able to parse data from the raw request body."""
        self.process_email.return_value = '*****@*****.**'
        req = self.factory.generic('POST', '/news/subscribe/',
                                   data='[email protected]&newsletters=firefox-os',
                                   content_type='text/plain; charset=UTF-8')
        views.subscribe(req)
        update_user_mock.assert_called_with(req, views.SUBSCRIBE, data={
            'email': '*****@*****.**',
            'newsletters': 'firefox-os',
        }, optin=False, sync=False)

    def test_no_source_url_referrer(self):
        """Test referrer used when no source_url."""
        request_data = {'newsletters': 'news,lets', 'optin': 'N', 'sync': 'N',
                        'email': '*****@*****.**', 'first_name': 'The', 'last_name': 'Dude'}
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        update_data['source_url'] = 'https://example.com/newsletter'
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/', request_data,
                                    HTTP_REFERER=update_data['source_url'])

        response = views.subscribe(request)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with(request_data['email'])
        self.update_user_task.assert_called_with(request, SUBSCRIBE, data=update_data,
                                                 optin=False, sync=False)

    def test_source_url_overrides_referrer(self):
        """Test source_url used when referrer also provided."""
        request_data = {'newsletters': 'news,lets', 'optin': 'N', 'sync': 'N',
                        'email': '*****@*****.**', 'first_name': 'The', 'last_name': 'Dude',
                        'source_url': 'https://example.com/thedude'}
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/', request_data,
                                    HTTP_REFERER='https://example.com/donnie')

        response = views.subscribe(request)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with(request_data['email'])
        self.update_user_task.assert_called_with(request, SUBSCRIBE, data=update_data,
                                                 optin=False, sync=False)

    def test_success(self):
        """Test basic success case with no optin or sync."""
        request_data = {'newsletters': 'news,lets', 'optin': 'N', 'sync': 'N',
                        'email': '*****@*****.**', 'first_name': 'The', 'last_name': 'Dude'}
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        self.process_email.return_value = update_data['email']
        request = self.factory.post('/', request_data)

        response = views.subscribe(request)

        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with(request_data['email'])
        self.update_user_task.assert_called_with(request, SUBSCRIBE, data=update_data,
                                                 optin=False, sync=False)

    def test_success_sync_optin(self):
        """Test success case with optin and sync."""
        request_data = {'newsletters': 'news,lets', 'optin': 'Y', 'sync': 'Y',
                        'email': '*****@*****.**'}
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        request = self.factory.post('/', request_data)
        self.has_valid_api_key.return_value = True
        self.process_email.return_value = update_data['email']

        response = views.subscribe(request)

        self.has_valid_api_key.assert_called_with(request)
        self.assertEqual(response, self.update_user_task.return_value)
        self.process_email.assert_called_with('*****@*****.**')
        self.update_user_task.assert_called_with(request, SUBSCRIBE, data=update_data,
                                                 optin=True, sync=True)

    def test_success_sync_optin_lowercase(self):
        """Test success case with optin and sync, using lowercase y."""
        request_data = {'newsletters': 'news,lets', 'optin': 'y', 'sync': 'y',
                        'email': '*****@*****.**'}
        update_data = request_data.copy()
        del update_data['optin']
        del update_data['sync']
        request = self.factory.post('/', request_data)
        self.process_email.return_value = update_data['email']

        with patch('basket.news.views.has_valid_api_key') as has_valid_api_key:
            has_valid_api_key.return_value = True
            response = views.subscribe(request)
            has_valid_api_key.assert_called_with(request)

            self.assertEqual(response, self.update_user_task.return_value)
            self.process_email.assert_called_with('*****@*****.**')
            self.update_user_task.assert_called_with(request, SUBSCRIBE, data=update_data,
                                                     optin=True, sync=True)
Example #13
0
class SubscribeEmailValidationTest(TestCase):
    email = '*****@*****.**'
    data = {
        'email': email,
        'newsletters': 'os',
    }
    view = 'subscribe'

    def setUp(self):
        self.rf = RequestFactory()

    @patch('basket.news.views.process_email')
    def test_invalid_email(self, mock_validate):
        """Should return proper error for invalid email."""
        mock_validate.return_value = None
        view = getattr(views, self.view)
        resp = view(self.rf.post('/', self.data))
        resp_data = json.loads(resp.content)
        mock_validate.assert_called_with(self.email)
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp_data['status'], 'error')
        self.assertEqual(resp_data['code'], errors.BASKET_INVALID_EMAIL)

    @patch('basket.news.views.update_user_task')
    def test_non_ascii_email_fxos_malformed_post(self, update_user_mock):
        """Should be able to parse data from the raw request body including non-ascii chars."""
        req = self.rf.generic('POST', '/news/subscribe/',
                              data='email=dude@黒川.日本&newsletters=firefox-os',
                              content_type='text/plain; charset=UTF-8')
        views.subscribe(req)
        update_user_mock.assert_called_with(req, views.SUBSCRIBE, data={
            'email': u'[email protected]',
            'newsletters': 'firefox-os',
        }, optin=False, sync=False)

    @patch('basket.news.views.update_user_task')
    def test_non_ascii_email(self, update_user_mock):
        """Should be able to accept valid email including non-ascii chars."""
        req = self.rf.post('/news/subscribe/', data={'email': 'dude@黒川.日本',
                                                     'newsletters': 'firefox-os'})
        views.subscribe(req)
        update_user_mock.assert_called_with(req, views.SUBSCRIBE, data={
            'email': u'[email protected]',
            'newsletters': 'firefox-os',
        }, optin=False, sync=False)

    @patch('basket.news.views.update_user_task')
    def test_empty_email_invalid(self, update_user_mock):
        """Should report an error for missing or empty value."""
        req = self.rf.post('/news/subscribe/', data={'email': '',
                                                     'newsletters': 'firefox-os'})
        resp = views.subscribe(req)
        resp_data = json.loads(resp.content)
        self.assertEqual(resp_data['status'], 'error')
        self.assertEqual(resp_data['code'], errors.BASKET_INVALID_EMAIL)
        self.assertFalse(update_user_mock.called)

        # no email at all
        req = self.rf.post('/news/subscribe/', data={'newsletters': 'firefox-os'})
        resp = views.subscribe(req)
        resp_data = json.loads(resp.content)
        self.assertEqual(resp_data['status'], 'error')
        self.assertEqual(resp_data['code'], errors.BASKET_USAGE_ERROR)
        self.assertFalse(update_user_mock.called)
Example #14
0
class SubscribeEmailValidationTest(TestCase):
    email = "*****@*****.**"
    data = {"email": email, "newsletters": "os"}
    view = "subscribe"

    def setUp(self):
        self.rf = RequestFactory()

    @patch("news.views.validate_email")
    def test_invalid_email(self, mock_validate):
        """Should return proper error for invalid email."""
        mock_validate.side_effect = utils.EmailValidationError("Invalid email")
        view = getattr(views, self.view)
        resp = view(self.rf.post("/", self.data))
        resp_data = json.loads(resp.content)
        mock_validate.assert_called_with(self.email)
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp_data["status"], "error")
        self.assertEqual(resp_data["code"], errors.BASKET_INVALID_EMAIL)
        self.assertNotIn("suggestion", resp_data)

    @patch("news.views.validate_email")
    def test_invalid_email_suggestion(self, mock_validate):
        """Should return proper error for invalid email."""
        mock_validate.side_effect = utils.EmailValidationError("Invalid email", "*****@*****.**")
        view = getattr(views, self.view)
        resp = view(self.rf.post("/", self.data))
        resp_data = json.loads(resp.content)
        mock_validate.assert_called_with(self.email)
        self.assertEqual(resp.status_code, 400)
        self.assertEqual(resp_data["status"], "error")
        self.assertEqual(resp_data["code"], errors.BASKET_INVALID_EMAIL)
        self.assertEqual(resp_data["suggestion"], "*****@*****.**")

    @patch("news.views.update_user_task")
    def test_non_ascii_email_fxos_malformed_post(self, update_user_mock):
        """Should be able to parse data from the raw request body including non-ascii chars."""
        req = self.rf.generic(
            "POST",
            "/news/subscribe/",
            data="email=dude@黒川.日本&newsletters=firefox-os",
            content_type="text/plain; charset=UTF-8",
        )
        views.subscribe(req)
        update_user_mock.assert_called_with(
            req, views.SUBSCRIBE, data={"email": u"dude@黒川.日本", "newsletters": "firefox-os"}, optin=False, sync=False
        )

    @patch("news.views.update_user_task")
    def test_non_ascii_email(self, update_user_mock):
        """Should be able to accept valid email including non-ascii chars."""
        req = self.rf.post("/news/subscribe/", data={"email": "dude@黒川.日本", "newsletters": "firefox-os"})
        views.subscribe(req)
        update_user_mock.assert_called_with(
            req, views.SUBSCRIBE, data={"email": u"dude@黒川.日本", "newsletters": "firefox-os"}, optin=False, sync=False
        )

    @patch("news.views.update_user_task")
    def test_empty_email_invalid(self, update_user_mock):
        """Should report an error for missing or empty value."""
        req = self.rf.post("/news/subscribe/", data={"email": "", "newsletters": "firefox-os"})
        resp = views.subscribe(req)
        resp_data = json.loads(resp.content)
        self.assertEqual(resp_data["status"], "error")
        self.assertEqual(resp_data["code"], errors.BASKET_INVALID_EMAIL)
        self.assertFalse(update_user_mock.called)

        # no email at all
        req = self.rf.post("/news/subscribe/", data={"newsletters": "firefox-os"})
        resp = views.subscribe(req)
        resp_data = json.loads(resp.content)
        self.assertEqual(resp_data["status"], "error")
        self.assertEqual(resp_data["code"], errors.BASKET_USAGE_ERROR)
        self.assertFalse(update_user_mock.called)