diff --git a/sanic/response.py b/sanic/response.py index 09c92225..7b245a82 100644 --- a/sanic/response.py +++ b/sanic/response.py @@ -1,3 +1,4 @@ +from functools import partial from mimetypes import guess_type from os import path from urllib.parse import quote_plus @@ -12,7 +13,11 @@ from sanic.helpers import STATUS_CODES, has_message_body, remove_entity_headers try: from ujson import dumps as json_dumps except BaseException: - from json import dumps as json_dumps + from json import dumps + + # This is done in order to ensure that the JSON response is + # kept consistent across both ujson and inbuilt json usage. + json_dumps = partial(dumps, separators=(",", ":")) class BaseHTTPResponse: diff --git a/tests/test_response.py b/tests/test_response.py index 7b679f43..d07eaa64 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,20 +1,19 @@ -import sys import asyncio import inspect import os -from aiofiles import os as async_os from mimetypes import guess_type +from random import choice +from unittest.mock import MagicMock from urllib.parse import unquote import pytest -from random import choice +from aiofiles import os as async_os from sanic.response import ( HTTPResponse, stream, StreamingHTTPResponse, file, file_stream, json ) from sanic.server import HttpProtocol from sanic.testing import HOST, PORT -from unittest.mock import MagicMock JSON_DATA = {'ok': True} @@ -38,7 +37,6 @@ async def sample_streaming_fn(response): def test_method_not_allowed(app): - @app.get('/') async def test_get(request): return response.json({'hello': 'world'}) @@ -55,12 +53,12 @@ def test_method_not_allowed(app): request, response = app.test_client.head('/') assert response.status == 405 - assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) + assert set(response.headers['Allow'].split(', ')) == {'GET', 'POST'} assert response.headers['Content-Length'] == '0' request, response = app.test_client.patch('/') assert response.status == 405 - assert set(response.headers['Allow'].split(', ')) == set(['GET', 'POST']) + assert set(response.headers['Allow'].split(', ')) == {'GET', 'POST'} assert response.headers['Content-Length'] == '0' @@ -74,19 +72,62 @@ def test_response_header(app): 'CONTENT-TYPE': 'application/json' }) - is_windows = sys.platform in ['win32', 'cygwin'] request, response = app.test_client.get('/') assert dict(response.headers) == { 'Connection': 'keep-alive', 'Keep-Alive': str(app.config.KEEP_ALIVE_TIMEOUT), - # response body contains an extra \r at the end if its windows - # TODO: this is the only place this difference shows up in our tests - # we should figure out a way to unify testing on both platforms - 'Content-Length': '12' if is_windows else '11', + 'Content-Length': '11', 'Content-Type': 'application/json', } +def test_response_content_length(app): + @app.get("/response_with_space") + async def response_with_space(request): + return json({ + "message": "Data", + "details": "Some Details" + }, headers={ + 'CONTENT-TYPE': 'application/json' + }) + + @app.get("/response_without_space") + async def response_without_space(request): + return json({ + "message":"Data", + "details":"Some Details" + }, headers={ + 'CONTENT-TYPE': 'application/json' + }) + + _, response = app.test_client.get("/response_with_space") + content_length_for_response_with_space = response.headers.get("Content-Length") + + _, response = app.test_client.get("/response_without_space") + content_length_for_response_without_space = response.headers.get("Content-Length") + + assert content_length_for_response_with_space == content_length_for_response_without_space + + assert content_length_for_response_with_space == '43' + + +def test_response_content_length_with_different_data_types(app): + @app.get("/") + async def get_data_with_different_types(request): + # Indentation issues in the Response is intentional. Please do not fix + return json({ + 'bool': True, + 'none': None, + 'string':'string', + 'number': -1}, + headers={ + 'CONTENT-TYPE': 'application/json' + }) + + _, response = app.test_client.get("/") + assert response.headers.get("Content-Length") == '55' + + @pytest.fixture def json_app(app):