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
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, ), }
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_render_blank_markup_with_error(): job = Job('MyCoolComponent.js', data={'title': 'sup'}, context={}) markup = render_blank_markup('my-unique-token', job, True, JSONEncoder()) assert markup == dedent(''' <div data-hypernova-key="MyCoolComponentjs" data-hypernova-id="my-unique-token"></div> <script type="application/json" data-hypernova-key="MyCoolComponentjs" data-hypernova-id="my-unique-token" ><!--{"title": "sup"}--></script> <script type="text/javascript"> (function () { function ServerSideRenderingError(component) { this.name = 'ServerSideRenderingError'; this.component = component; } ServerSideRenderingError.prototype = Object.create(ServerSideRenderingError.prototype); ServerSideRenderingError.prototype.constructor = ServerSideRenderingError; throw new ServerSideRenderingError('MyCoolComponentjs failed to render server-side, and fell back to client-side rendering.'); }()); </script> ''') # noqa: ignore=E501
def create_fallback_response(jobs, throw_client_error, error=None): return { identifier: JobResult( error=error, html=render_blank_markup(identifier, job, throw_client_error), job=job, ) for identifier, job in jobs.items() }
def test_create_fallback_response(jobs, throw_client_error, json_encoder): expected_response = { identifier: JobResult( error=None, html=render_blank_markup(identifier, job, throw_client_error, json_encoder), job=job, ) for identifier, job in jobs.items() } assert create_fallback_response(jobs, throw_client_error, json_encoder) == expected_response
def test_render_blank_markup(): job = Job('MyCoolComponent.js', data={'title': 'sup'}, context={}) markup = render_blank_markup('my-unique-token', job, False, JSONEncoder()) assert markup == dedent(''' <div data-hypernova-key="MyCoolComponentjs" data-hypernova-id="my-unique-token"></div> <script type="application/json" data-hypernova-key="MyCoolComponentjs" data-hypernova-id="my-unique-token" ><!--{"title": "sup"}--></script> ''')
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_render_blank_markup_with_custom_json_encoder(): job = Job('MyCoolComponent.js', data={'a complex subject': 4.3 + 2.1j}) markup = render_blank_markup('my-unique-token', job, False, ComplexJSONEncoder()) assert markup == dedent(''' <div data-hypernova-key="MyCoolComponentjs" data-hypernova-id="my-unique-token"></div> <script type="application/json" data-hypernova-key="MyCoolComponentjs" data-hypernova-id="my-unique-token" ><!--{"a complex subject": [4.3, 2.1]}--></script> ''')
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, ), }
def create_fallback_response(jobs, throw_client_error, json_encoder, error=None): """Create a response dict for falling back to client-side rendering. :rtype: Dict[str, Job] """ return { identifier: JobResult( error=error, html=render_blank_markup(identifier, job, throw_client_error, json_encoder), job=job, ) for identifier, job in jobs.items() }
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_create_fallback_response(throw_client_error): jobs = { 'some-unique-id': Job( name='FooBar.js', data={'baz': 1234}, ), 'some-other-unique-id': Job( name='MyComponent.js', data={'title': 'sup'}, ), } expected_response = { identifier: JobResult( error=None, html=render_blank_markup(identifier, job, throw_client_error), job=job, ) for identifier, job in jobs.items() } assert create_fallback_response(jobs, throw_client_error) == expected_response