def test_force_synchronous_tasks(self): expected_response = {"foo": "bar"} endpoint = _TestEndpoint(expected_response) manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) conf = tasks.future_task_runner.app.conf old_val = conf["task_always_eager"] conf["task_always_eager"] = True cache.set(tasks.JOB_COUNT_CACHE_KEY, 0) with mock.patch( "django_declarative_apis.machinery.tasks.future_task_runner.apply_async" ) as mock_apply: mock_apply.side_effect = kombu.exceptions.OperationalError with self.settings(DECLARATIVE_ENDPOINT_TASKS_FORCE_SYNCHRONOUS=True): try: manager.get_response() except kombu.exceptions.OperationalError: self.fail("OperationalError should not have been triggered") finally: conf["task_always_eager"] = old_val self.assertEqual(0, mock_apply.call_count) self.assertEqual("deferred task executed", _TestEndpoint.semaphore["status"])
def test_get_response_success(self): expected_response = {"foo": "bar"} endpoint = _TestEndpoint(expected_response) manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) # can't use mock.patch.dict here because it doesn't implement the api that the unpatcher expects conf = tasks.future_task_runner.app.conf old_val = conf["task_always_eager"] conf["task_always_eager"] = True cache.set(tasks.JOB_COUNT_CACHE_KEY, 0) try: resp = manager.get_response() finally: conf["task_always_eager"] = old_val self.assertEqual(resp, (http.HTTPStatus.OK, expected_response)) self.assertTrue(cache.get(tasks.JOB_COUNT_CACHE_KEY) != 0) self.assertEqual("deferred task executed", _TestEndpoint.semaphore["status"]) self.assertEqual(3, _TestEndpoint.semaphore["retry_count"]) self.assertEqual(3, _TestEndpoint.semaphore["filtered_retry_count_1"]) self.assertEqual(1, _TestEndpoint.semaphore["filtered_retry_count_2"]) _TestEndpoint.semaphore = { "status": None, "retry_count": 0, "filtered_retry_count_1": 0, "filtered_retry_count_2": 0, }
def test_get_response_with_filtered_list(self): class _TestResource: def __init__(self, name, secret): self.name = name self.secret = secret class _QuerySet(list): pass data = _QuerySet([_TestResource("foo", "bar"), _TestResource("bar", "baz")]) filter_def = { _TestResource: {"name": filtering.ALWAYS, "secret": filtering.NEVER} } class _TestEndpoint(machinery.EndpointDefinition): @machinery.endpoint_resource(type=_TestResource, filter=filter_def) def resource(self): return data @property def response(self): return {"people": self.resource} endpoint = _TestEndpoint() manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) status, resp = manager.get_response() self.assertEqual(status, http.HTTPStatus.OK) # make sure the list is in the expected order resp["people"].sort(key=lambda p: p["name"].lower()) self.assertEqual(resp, {"people": [{"name": "bar"}, {"name": "foo"}]})
def test_get_response_with_dirty_resource(self): class _TestResource: def is_dirty(self, check_relationship=False): return True def save(self): pass class _TestEndpoint(machinery.EndpointDefinition): @machinery.endpoint_resource(type=_TestResource) def resource(self): return _TestResource() endpoint = _TestEndpoint() manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) class _FakeRequest: META = {} manager.bound_endpoint.request = _FakeRequest() with mock.patch.object(_TestResource, "save", return_value=None) as mock_save: manager.get_response() # save is called before and after tasks. since we've hardcoded _TestResource.is_dirty to return True, # both of them should fire self.assertEqual(mock_save.call_count, 2)
def test_get_response_custom_http_error(self): expected_data = {"error": "something bad happened"} class _TestEndpoint(machinery.EndpointDefinition): @property def resource(self): pass @property def response(self): return django.http.HttpResponse( content=json.dumps(expected_data), status=http.HTTPStatus.BAD_REQUEST, ) @property def http_status(self): # TODO: it's kind of strange that http_status has to be defined if the response returns a custom # http response return http.HTTPStatus.BAD_REQUEST endpoint = _TestEndpoint() manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) try: manager.get_response() self.fail("This should have failed") except HttpStatusCode as err: self.assertEqual(err.response.status_code, http.HTTPStatus.BAD_REQUEST) self.assertEqual(json.loads(err.response.content), expected_data)
def test_get_response_with_client_error_while_executing_tasks(self): class _TestResource: def is_dirty(self, check_relationship=False): return True def save(self): pass class _TestEndpoint(machinery.EndpointDefinition): @machinery.endpoint_resource(type=_TestResource) def resource(self): return _TestResource() @machinery.task def raise_an_exception(self): raise errors.ClientError( code=http.HTTPStatus.BAD_REQUEST, message="something bad happened", save_changes=error_should_save_changes, ) for error_should_save_changes in (True, False): with mock.patch.object(_TestResource, "save") as mock_save: endpoint = _TestEndpoint() manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint) try: manager.get_response() self.fail("This should have failed") except errors.ClientError: # save should be called twice if the exception says the resource should be saved: once before # tasks are executed and once during exception handling. self.assertEqual(mock_save.call_count, 2 if error_should_save_changes else 1)
def _bind_endpoint(endpoint_cls, request, url_field=None): endpoint = endpoint_cls() binder = machinery.EndpointBinder(endpoint_cls) return binder.create_bound_endpoint( machinery._EndpointRequestLifecycleManager(endpoint), request, url_field=url_field, )
def test_process_request_and_get_response_success(self): req = self.create_request() expected_data = {"foo": "bar"} class _TestEndpoint(machinery.EndpointDefinition): def is_authorized(self): return True @property def resource(self): return expected_data manager = machinery._EndpointRequestLifecycleManager(_TestEndpoint) status, data = manager.process_request_and_get_response(req) self.assertEqual(status, http.HTTPStatus.OK) self.assertEqual(data, expected_data)
def test_get_response_kombu_error_attempts_exceeded(self): expected_response = {"foo": "bar"} endpoint = _TestEndpoint(expected_response) manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) machinery.EndpointBinder(endpoint).create_bound_endpoint(manager, HttpRequest()) conf = tasks.future_task_runner.app.conf old_val = conf["task_always_eager"] conf["task_always_eager"] = True cache.set(tasks.JOB_COUNT_CACHE_KEY, 0) with mock.patch( "django_declarative_apis.machinery.tasks.future_task_runner.apply_async" ) as mock_apply: exceptions = iter( [ kombu.exceptions.OperationalError, kombu.exceptions.OperationalError, kombu.exceptions.OperationalError, ] ) def _side_effect(*args, **kwargs): try: raise next(exceptions) except StopIteration: return future_task_runner.apply(*args, **kwargs) mock_apply.side_effect = _side_effect try: manager.get_response() self.fail("should have triggered a kombu.exceptions.OperationalError") except kombu.exceptions.OperationalError: pass finally: conf["task_always_eager"] = old_val self.assertIsNone(_TestEndpoint.semaphore["status"])
def test_get_response_with_non_client_error(self, mock_logging): class _TestException(Exception): pass class _TestEndpoint(machinery.EndpointDefinition): @property def resource(self): return {} endpoint = _TestEndpoint() manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) manager.binding_exc_info = ( _TestException, _TestException("something bad happened"), None, ) self.assertRaises(_TestException, manager.get_response) mock_logging.error.assert_called_with("('something bad happened',)\nNone")
def test_get_response_kombu_error_retried(self): expected_response = {"foo": "bar"} endpoint = _TestEndpoint(expected_response) manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint) machinery.EndpointBinder(endpoint).create_bound_endpoint( manager, HttpRequest()) conf = tasks.future_task_runner.app.conf old_val = conf["task_always_eager"] conf["task_always_eager"] = True cache.set(tasks.JOB_COUNT_CACHE_KEY, 0) with mock.patch( "django_declarative_apis.machinery.tasks.future_task_runner.apply_async" ) as mock_apply: exceptions = iter([ kombu.exceptions.OperationalError, kombu.exceptions.OperationalError ]) def _side_effect(*args, **kwargs): try: raise next(exceptions) except StopIteration: return future_task_runner.apply(*args, **kwargs) mock_apply.side_effect = _side_effect try: resp = manager.get_response() finally: conf["task_always_eager"] = old_val self.assertEqual(resp, (http.HTTPStatus.OK, expected_response)) self.assertTrue(cache.get(tasks.JOB_COUNT_CACHE_KEY) != 0) self.assertEqual("deferred task executed", _TestEndpoint.semaphore["status"])
def test_get_response_custom_http_response(self): expected_data = {"foo": "bar"} expected_response = django.http.HttpResponse(content=json.dumps(expected_data)) class _TestEndpoint(machinery.EndpointDefinition): @property def resource(self): pass @property def response(self): return expected_response endpoint = _TestEndpoint() manager = machinery.EndpointBinder.BoundEndpointManager( machinery._EndpointRequestLifecycleManager(endpoint), endpoint ) status, resp = manager.get_response() self.assertEqual(status, http.HTTPStatus.OK) self.assertEqual(resp, expected_response) self.assertEqual(json.loads(resp.content), expected_data)