diff --git a/test_http_request/common.py b/test_http_request/common.py index 477a731f..1c0bc346 100644 --- a/test_http_request/common.py +++ b/test_http_request/common.py @@ -6,7 +6,7 @@ from contextlib import contextmanager from odoo.addons.http_routing.models.ir_http import url_for from odoo.api import Environment -from odoo.http import HttpRequest, OpenERPSession, _request_stack +from odoo.http import HttpRequest, JsonRequest, OpenERPSession, _request_stack from odoo.tools import config from typing import Optional from werkzeug.contrib.sessions import FilesystemSessionStore @@ -14,7 +14,7 @@ from werkzeug.wrappers import Request -class _MockOdooHttpRequest(HttpRequest): +class _MockOdooRequestMixin: @staticmethod def redirect(url, code=302): @@ -46,12 +46,42 @@ def __exit__(self, exc_type, exc_value, traceback): _request_stack.pop() +class _MockOdooHttpRequest(_MockOdooRequestMixin, HttpRequest): + pass + + +class _MockOdooJsonRequest(_MockOdooRequestMixin, JsonRequest): + pass + + +def _make_environ( + method: str = 'POST', + headers: Optional[dict] = None, + data: Optional[dict] = None, + routing_type: str = 'http', +): + """Make an environ for the given request parameters.""" + assert routing_type in ('http', 'json') + environ_builder = EnvironBuilder( + method=method, + data=json.dumps(data or {}) if routing_type == 'json' else data, + headers=headers, + ) + environ_builder.content_type = ( + 'application/json' if routing_type == 'json' else + 'application/x-www-form-urlencoded' + ) + environ = environ_builder.get_environ() + return environ + + @contextmanager def mock_odoo_request( env: Environment, - method: str ='POST', - headers: Optional[dict]=None, - data: Optional[dict]=None, + method: str = 'POST', + headers: Optional[dict] = None, + data: Optional[dict] = None, + routing_type: str = 'http', ): """Mock an Odoo HTTP request. @@ -65,8 +95,12 @@ def mock_odoo_request( :param env: the odoo environment to bind with the request. :param method: the HTTP method called during the request. :param headers: the request headers. - :param data: an optional dict to be serialized as json data. + :param data: an optional dict to be serialized as json or url-encoded data. + :param routing_type: whether to use an http (x-www-form-urlencoded) or json request. """ + environ = _make_environ(method, headers, data, routing_type) + httprequest = Request(environ) + session_store = FilesystemSessionStore( config.session_dir, session_class=OpenERPSession, renew_missing=True) session = session_store.new() @@ -74,14 +108,13 @@ def mock_odoo_request( session.uid = env.uid session.context = env.context - json_data = json.dumps(data or {}) - - environ_builder = EnvironBuilder(method=method, data=json_data, headers=headers) - environ = environ_builder.get_environ() - httprequest = Request(environ) httprequest.session = session - odoo_http_request = _MockOdooHttpRequest(httprequest) + odoo_http_request_cls = ( + _MockOdooJsonRequest if routing_type == 'json' else + _MockOdooHttpRequest + ) + odoo_http_request = odoo_http_request_cls(httprequest) odoo_http_request._env = env odoo_http_request._cr = env.cr odoo_http_request._uid = env.uid diff --git a/test_http_request/tests/__init__.py b/test_http_request/tests/__init__.py new file mode 100644 index 00000000..6ef2df91 --- /dev/null +++ b/test_http_request/tests/__init__.py @@ -0,0 +1,2 @@ +# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/test_http_request/tests/test_mock_http_request.py b/test_http_request/tests/test_mock_http_request.py new file mode 100644 index 00000000..9f2786af --- /dev/null +++ b/test_http_request/tests/test_mock_http_request.py @@ -0,0 +1,59 @@ +# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import json +from collections import OrderedDict +from ddt import data, ddt, unpack +from odoo.http import request +from odoo.tests import common +from werkzeug.datastructures import ImmutableMultiDict +from ..common import mock_odoo_request + + +@ddt +class TestMockHttpRequest(common.TransactionCase): + + def setUp(self): + super().setUp() + self.data = OrderedDict([ + ('firstname', 'John'), + ('lastname', 'Doe'), + ]) + + def test_env_propagated_to_request(self): + with mock_odoo_request(self.env, data=self.data): + assert request.env == self.env + + def test_method_propagated_to_request(self): + method = 'PATCH' + with mock_odoo_request(self.env, data=self.data, method=method): + assert request.httprequest.method == method + + def test_headers_propagated_to_request(self): + header_key = 'Some-Header' + header_value = 'some value' + headers = { + header_key: header_value, + } + with mock_odoo_request(self.env, data=self.data, headers=headers): + assert request.httprequest.headers[header_key] == header_value + + @data( + ('http', 'application/x-www-form-urlencoded'), + ('json', 'application/json'), + ) + @unpack + def test_content_type(self, routing_type, content_type): + with mock_odoo_request(self.env, data=self.data, routing_type=routing_type): + assert request.httprequest.content_type == content_type + + def test_if_http_routing__data_contained_in_request_form(self): + with mock_odoo_request(self.env, data=self.data): + assert request.httprequest.form == ImmutableMultiDict(self.data) + assert not request.httprequest.data + + def test_if_json_routing__data_contained_in_request_data(self): + json_data = json.dumps(self.data).encode() + with mock_odoo_request(self.env, data=self.data, routing_type='json'): + assert not request.httprequest.form + assert request.httprequest.data == json_data