Example #1
0
    def test_batch_request_with_application_error(self, spy_plugin_controller, batch_request):
        job = Job(name='MyComponent.js', data={'foo': 'bar'})
        token = batch_request.render('MyComponent.js', {'foo': 'bar'})

        fake_response_json = {
            'error': {
                'name': 'SomeError',
                'message': 'yikes',
                'stack': ['line 1', 'line 2']
            }
        }

        with mock.patch('fido.fetch') as mock_fetch:
            mock_fetch.return_value.wait.return_value.json.return_value = fake_response_json
            response = batch_request.submit()

        if batch_request.max_batch_size is None:
            assert mock_fetch.call_count == 1
        else:
            # Division (rounded-up) up to get total number of calls
            jobs_count = len(batch_request.jobs)
            max_batch_size = batch_request.max_batch_size
            assert mock_fetch.call_count == (jobs_count + (max_batch_size - 1)) // max_batch_size

        assert response == {
            token.identifier: JobResult(
                error=HypernovaError(
                    name='SomeError',
                    message='yikes',
                    stack=['line 1', 'line 2'],
                ),
                html=render_blank_markup(token.identifier, job, True),
                job=job,
            ),
        }
Example #2
0
    def _parse_response(self, response_json):
        """Parse a raw JSON response into a response dict.

        :rtype: Dict[str, JobResult]
        """
        response = {}
        for identifier, result in response_json['results'].items():
            job = self.jobs[identifier]

            error = None
            if result['error']:
                error = HypernovaError(
                    name=result['error']['name'],
                    message=result['error']['message'],
                    stack=result['error']['stack'],
                )
                self.plugin_controller.on_error(error, {identifier: job},
                                                self.pyramid_request)

            html = result['html']
            if not html:
                html = render_blank_markup(identifier, job, True,
                                           self.json_encoder)

            response[identifier] = JobResult(error=error, html=html, job=job)
        return response
Example #3
0
    def test_batch_request_with_unhealthy_service(self, spy_plugin_controller, test_data, batch_request):
        data = test_data[0]
        job = Job(name='MyComponent.js', data=data[0])
        token = batch_request.render('MyComponent.js', data[0])

        with mock.patch('fido.fetch') as mock_fetch:
            mock_fetch.return_value.wait.return_value.json.side_effect = NetworkError('oh no')
            response = batch_request.submit()

        if batch_request.max_batch_size is None:
            assert mock_fetch.call_count == 1
        else:
            # Division (rounded-up) up to get total number of calls
            jobs_count = len(batch_request.jobs)
            max_batch_size = batch_request.max_batch_size
            assert mock_fetch.call_count == (jobs_count + (max_batch_size - 1)) // max_batch_size

        assert response == {
            token.identifier: JobResult(
                error=HypernovaError(
                    name="<class 'fido.exceptions.NetworkError'>",
                    message='oh no',
                    stack=mock.ANY,
                ),
                html=render_blank_markup(token.identifier, job, True, batch_request.json_encoder),
                job=job,
            ),
        }
    def test_calls_on_error_on_unhealthy_service(
        self,
        spy_plugin_controller,
        test_data,
        batch_request,
        mock_hypernova_query,
    ):
        data = test_data[0]
        batch_request.render('MyComponent.js', data[0])

        with mock.patch(
                'traceback.format_tb',
                return_value=[
                    'Traceback:\n',
                    '  foo:\n',
                ],
        ):
            mock_hypernova_query.return_value.json.side_effect = HypernovaQueryError(
                'oh no')
            batch_request.submit()

        spy_plugin_controller.on_error.assert_called_once_with(
            HypernovaError(
                name='HypernovaQueryError',
                message='oh no',
                stack=['Traceback:', '  foo:'],
            ), batch_request.jobs, batch_request.pyramid_request)
Example #5
0
    def process_responses(self, query, jobs):
        """Retrieve response from EventualResponse object and calls
        lifecycle methods for corresponding jobs.

        :param query: a HypernovaQuery object
        :type query: HypernovaQuery
        :type jobs: Dict[str, Job]

        :rtype: Dict[str, JobResult]
        """

        pyramid_response = {}

        try:
            response_json = query.json()
            if response_json['error']:
                error = HypernovaError(
                    name=response_json['error']['name'],
                    message=response_json['error']['message'],
                    stack=response_json['error']['stack'],
                )
                pyramid_response = create_fallback_response(
                    jobs, True, self.json_encoder, error)
                self.plugin_controller.on_error(error, jobs,
                                                self.pyramid_request)
            else:
                pyramid_response = self._parse_response(response_json)
                self.plugin_controller.on_success(pyramid_response, jobs)

        except (HypernovaQueryError, ValueError) as e:
            # the service is unhealthy. fall back to client-side rendering
            __, __, exc_traceback = sys.exc_info()

            error = HypernovaError(
                type(e).__name__,
                str(e),
                [
                    line.rstrip('\n')
                    for line in traceback.format_tb(exc_traceback)
                ],
            )
            self.plugin_controller.on_error(error, jobs, self.pyramid_request)
            pyramid_response = create_fallback_response(
                jobs, True, self.json_encoder, error)

        return pyramid_response
Example #6
0
    def process_responses(self, future, jobs):
        """Retrieve response from EventualResponse object and calls
        lifecycle methods for corresponding jobs.

        :param future: a future for the HTTP request to render `jobs`
        :type future: EventualResult
        :type jobs: Dict[str, Job]

        :rtype: Dict[str, JobResult]
        """

        response = {}

        try:
            r = future.wait()
            response_json = r.json()
            if response_json['error']:
                error = HypernovaError(
                    name=response_json['error']['name'],
                    message=response_json['error']['message'],
                    stack=response_json['error']['stack'],
                )
                response = create_fallback_response(jobs, True, error)
                self.plugin_controller.on_error(error, jobs)
            else:
                response = self._parse_response(response_json)
                self.plugin_controller.on_success(response, jobs)

        except (NetworkError, ValueError) as e:
            # the service is unhealthy. fall back to client-side rendering
            __, __, exc_traceback = sys.exc_info()

            error = HypernovaError(
                repr(type(e)),
                str(e),
                traceback.format_tb(exc_traceback),
            )
            self.plugin_controller.on_error(error, jobs)
            response = create_fallback_response(jobs, True, error)

        return response
Example #7
0
    def test_batch_request_with_component_errors(self, spy_plugin_controller, test_data, batch_request):
        data = test_data[0]
        token_1 = batch_request.render('MyComponent1.js', data[0])
        token_2 = batch_request.render('MyComponent2.js', data[1])
        job_2 = Job(name='MyComponent2.js', data=data[1])

        fake_response_json = {
            'error': None,
            'results': {
                token_1.identifier: {
                    'error': None,
                    'html': '<div>wow such SSR</div>',
                },
                token_2.identifier: {
                    'error': {
                        'name': 'SomeError',
                        'message': 'we goofed',
                        'stack': ['line 1', 'line 2']
                    },
                    'html': None,
                }
            }
        }

        with mock.patch('fido.fetch') as mock_fetch:
            mock_fetch.return_value.wait.return_value.json.return_value = fake_response_json
            response = batch_request.submit()

        if batch_request.max_batch_size is None:
            assert mock_fetch.call_count == 1
        else:
            # Division (rounded-up) up to get total number of calls
            jobs_count = len(batch_request.jobs)
            max_batch_size = batch_request.max_batch_size
            assert mock_fetch.call_count == (jobs_count + (max_batch_size - 1)) // max_batch_size

        assert response == {
            token_1.identifier: JobResult(
                error=None,
                html='<div>wow such SSR</div>',
                job=Job(name='MyComponent1.js', data=data[0])
            ),
            token_2.identifier: JobResult(
                error=HypernovaError(
                    name='SomeError',
                    message='we goofed',
                    stack=['line 1', 'line 2'],
                ),
                html=render_blank_markup(token_2.identifier, job_2, True, batch_request.json_encoder),
                job=job_2,
            )
        }
    def test_batch_request_with_application_error(
        self,
        spy_plugin_controller,
        test_data,
        batch_request,
        mock_hypernova_query,
    ):
        data = test_data[0]
        job = Job(name='MyComponent.js', data=data[0], context={})
        token = batch_request.render('MyComponent.js', data[0])

        fake_response_json = {
            'error': {
                'name': 'SomeError',
                'message': 'yikes',
                'stack': ['line 1', 'line 2']
            }
        }

        mock_hypernova_query.return_value.json.return_value = fake_response_json
        response = batch_request.submit()

        if batch_request.max_batch_size is None:
            assert mock_hypernova_query.call_count == 1
        else:
            # Division (rounded-up) up to get total number of calls
            jobs_count = len(batch_request.jobs)
            max_batch_size = batch_request.max_batch_size
            batch_count = (jobs_count + (max_batch_size - 1)) // max_batch_size
            assert mock_hypernova_query.call_count == batch_count
            mock_hypernova_query.assert_called_with(mock.ANY, mock.ANY,
                                                    mock.ANY, batch_count == 1,
                                                    {})

        assert response == {
            token.identifier:
            JobResult(
                error=HypernovaError(
                    name='SomeError',
                    message='yikes',
                    stack=['line 1', 'line 2'],
                ),
                html=render_blank_markup(token.identifier, job, True,
                                         batch_request.json_encoder),
                job=job,
            ),
        }
Example #9
0
    def test_calls_on_error_on_unhealthy_service(self, spy_plugin_controller, batch_request):
        batch_request.render('MyComponent.js', {'foo': 'bar'})

        with mock.patch(
            'fido.fetch'
        ) as mock_fetch, mock.patch(
            'traceback.format_tb'
        ) as mock_format_tb:
            mock_fetch.return_value.wait.return_value.json.side_effect = NetworkError('oh no')
            batch_request.submit()

        spy_plugin_controller.on_error.assert_called_once_with(
            HypernovaError(
                name="<class 'fido.exceptions.NetworkError'>",
                message='oh no',
                stack=mock_format_tb.return_value,
            ),
            batch_request.jobs,
        )
    def test_batch_request_with_unhealthy_service(
        self,
        spy_plugin_controller,
        test_data,
        batch_request,
        mock_hypernova_query,
    ):
        data = test_data[0]
        job = Job(name='MyComponent.js', data=data[0], context={})
        token = batch_request.render('MyComponent.js', data[0])

        mock_hypernova_query.return_value.json.side_effect = HypernovaQueryError(
            'oh no')
        response = batch_request.submit()

        if batch_request.max_batch_size is None:
            assert mock_hypernova_query.call_count == 1
        else:
            # Division (rounded-up) up to get total number of calls
            jobs_count = len(batch_request.jobs)
            max_batch_size = batch_request.max_batch_size
            batch_count = (jobs_count + (max_batch_size - 1)) // max_batch_size
            assert mock_hypernova_query.call_count == batch_count
            mock_hypernova_query.assert_called_with(mock.ANY, mock.ANY,
                                                    mock.ANY, batch_count == 1,
                                                    {})

        assert response == {
            token.identifier:
            JobResult(
                error=HypernovaError(
                    name='HypernovaQueryError',
                    message='oh no',
                    stack=mock.ANY,
                ),
                html=render_blank_markup(token.identifier, job, True,
                                         batch_request.json_encoder),
                job=job,
            ),
        }
    def test_calls_on_error(self, spy_plugin_controller, test_data,
                            batch_request, mock_hypernova_query):
        data = test_data[0]
        batch_request.render('MyComponent.js', data[0])

        fake_response_json = {
            'error': {
                'name': 'EverythingIsOnFire',
                'message': 'ya goofed',
                'stack': []
            }
        }

        mock_hypernova_query.return_value.json.return_value = fake_response_json
        batch_request.submit()

        spy_plugin_controller.on_error.assert_called_once_with(
            HypernovaError(
                name='EverythingIsOnFire',
                message='ya goofed',
                stack=[],
            ), batch_request.jobs, batch_request.pyramid_request)
Example #12
0
    def test_calls_on_error(self, spy_plugin_controller, batch_request):
        batch_request.render('MyComponent.js', {'foo': 'bar'})

        fake_response_json = {
            'error': {
                'name': 'EverythingIsOnFire',
                'message': 'ya goofed',
                'stack': []
            }
        }

        with mock.patch('fido.fetch') as mock_fetch:
            mock_fetch.return_value.wait.return_value.json.return_value = fake_response_json
            batch_request.submit()

        spy_plugin_controller.on_error.assert_called_once_with(
            HypernovaError(
                name='EverythingIsOnFire',
                message='ya goofed',
                stack=[],
            ),
            batch_request.jobs,
        )