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")
Esempio n. 11
0
    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)