Merge branch 'master' into add_tests_for_log
This commit is contained in:
commit
6270b27a97
|
@ -167,17 +167,17 @@ class ContentRangeHandler:
|
|||
)
|
||||
else:
|
||||
# this case represents `Content-Range: bytes 5-`
|
||||
self.end = self.total
|
||||
self.end = self.total - 1
|
||||
else:
|
||||
if self.start is None:
|
||||
# this case represents `Content-Range: bytes -5`
|
||||
self.start = self.total - self.end
|
||||
self.end = self.total
|
||||
self.end = self.total - 1
|
||||
if self.start >= self.end:
|
||||
raise ContentRangeError(
|
||||
"Invalid for Content Range parameters", self
|
||||
)
|
||||
self.size = self.end - self.start
|
||||
self.size = self.end - self.start + 1
|
||||
self.headers = {
|
||||
"Content-Range": "bytes %s-%s/%s"
|
||||
% (self.start, self.end, self.total)
|
||||
|
|
|
@ -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:
|
||||
|
@ -302,6 +307,7 @@ async def file(
|
|||
_range.end,
|
||||
_range.total,
|
||||
)
|
||||
status = 206
|
||||
else:
|
||||
out_stream = await _file.read()
|
||||
|
||||
|
@ -371,6 +377,7 @@ async def file_stream(
|
|||
_range.end,
|
||||
_range.total,
|
||||
)
|
||||
status = 206
|
||||
return StreamingHTTPResponse(
|
||||
streaming_fn=_streaming_fn,
|
||||
status=status,
|
||||
|
|
|
@ -48,6 +48,20 @@ def test_load_env_prefix():
|
|||
del environ["MYAPP_TEST_ANSWER"]
|
||||
|
||||
|
||||
def test_load_env_prefix_float_values():
|
||||
environ["MYAPP_TEST_ROI"] = "2.3"
|
||||
app = Sanic(load_env="MYAPP_")
|
||||
assert app.config.TEST_ROI == 2.3
|
||||
del environ["MYAPP_TEST_ROI"]
|
||||
|
||||
|
||||
def test_load_env_prefix_string_value():
|
||||
environ["MYAPP_TEST_TOKEN"] = "somerandomtesttoken"
|
||||
app = Sanic(load_env="MYAPP_")
|
||||
assert app.config.TEST_TOKEN == "somerandomtesttoken"
|
||||
del environ["MYAPP_TEST_TOKEN"]
|
||||
|
||||
|
||||
def test_load_from_file(app):
|
||||
config = dedent("""
|
||||
VALUE = 'some value'
|
||||
|
|
|
@ -2,7 +2,7 @@ from datetime import datetime, timedelta
|
|||
from http.cookies import SimpleCookie
|
||||
from sanic.response import text
|
||||
import pytest
|
||||
|
||||
from sanic.cookies import Cookie
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# GET
|
||||
|
@ -111,3 +111,22 @@ def test_cookie_deletion(app):
|
|||
assert int(response_cookies['i_want_to_die']['max-age']) == 0
|
||||
with pytest.raises(KeyError):
|
||||
response.cookies['i_never_existed']
|
||||
|
||||
|
||||
def test_cookie_reserved_cookie():
|
||||
with pytest.raises(expected_exception=KeyError) as e:
|
||||
Cookie("domain", "testdomain.com")
|
||||
assert e.message == "Cookie name is a reserved word"
|
||||
|
||||
|
||||
def test_cookie_illegal_key_format():
|
||||
with pytest.raises(expected_exception=KeyError) as e:
|
||||
Cookie("testå", "test")
|
||||
assert e.message == "Cookie key contains illegal characters"
|
||||
|
||||
|
||||
def test_cookie_set_unknown_property():
|
||||
c = Cookie("test_cookie", "value")
|
||||
with pytest.raises(expected_exception=KeyError) as e:
|
||||
c["invalid"] = "value"
|
||||
assert e.message == "Unknown cookie property"
|
||||
|
|
36
tests/test_helpers.py
Normal file
36
tests/test_helpers.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from sanic import helpers
|
||||
|
||||
|
||||
def test_has_message_body():
|
||||
tests = (
|
||||
(100, False),
|
||||
(102, False),
|
||||
(204, False),
|
||||
(200, True),
|
||||
(304, False),
|
||||
(400, True),
|
||||
)
|
||||
for status_code, expected in tests:
|
||||
assert helpers.has_message_body(status_code) is expected
|
||||
|
||||
|
||||
def test_is_entity_header():
|
||||
tests = (
|
||||
("allow", True),
|
||||
("extension-header", True),
|
||||
("", False),
|
||||
("test", False),
|
||||
)
|
||||
for header, expected in tests:
|
||||
assert helpers.is_entity_header(header) is expected
|
||||
|
||||
|
||||
def test_is_hop_by_hop_header():
|
||||
tests = (
|
||||
("connection", True),
|
||||
("upgrade", True),
|
||||
("", False),
|
||||
("test", False),
|
||||
)
|
||||
for header, expected in tests:
|
||||
assert helpers.is_hop_by_hop_header(header) is expected
|
|
@ -123,3 +123,13 @@ def test_logger(caplog):
|
|||
assert caplog.record_tuples[2] == ('sanic.root', logging.INFO, rand_string)
|
||||
assert caplog.record_tuples[-1] == ('sanic.root', logging.INFO, 'Server Stopped')
|
||||
|
||||
|
||||
def test_logging_modified_root_logger_config():
|
||||
reset_logging()
|
||||
|
||||
modified_config = LOGGING_CONFIG_DEFAULTS
|
||||
modified_config['loggers']['sanic.root']['level'] = 'DEBUG'
|
||||
|
||||
app = Sanic("test_logging", log_config=modified_config)
|
||||
|
||||
assert logging.getLogger('sanic.root').getEffectiveLevel() == logging.DEBUG
|
||||
|
|
|
@ -363,3 +363,83 @@ def test_url_attributes_with_ssl(app, path, query, expected_url):
|
|||
assert parsed.path == request.path
|
||||
assert parsed.query == request.query_string
|
||||
assert parsed.netloc == request.host
|
||||
|
||||
|
||||
def test_form_with_multiple_values(app):
|
||||
|
||||
@app.route('/', methods=['POST'])
|
||||
async def handler(request):
|
||||
return text("OK")
|
||||
|
||||
payload="selectedItems=v1&selectedItems=v2&selectedItems=v3"
|
||||
|
||||
headers = {'content-type': 'application/x-www-form-urlencoded'}
|
||||
|
||||
request, response = app.test_client.post('/', data=payload,
|
||||
headers=headers)
|
||||
|
||||
assert request.form.getlist("selectedItems") == ["v1", "v2", "v3"]
|
||||
|
||||
|
||||
def test_request_string_representation(app):
|
||||
@app.route('/', methods=["GET"])
|
||||
async def get(request):
|
||||
return text("OK")
|
||||
|
||||
request, _ = app.test_client.get("/")
|
||||
assert repr(request) == '<Request: GET />'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'payload', [
|
||||
'------sanic\r\n'
|
||||
'Content-Disposition: form-data; filename="filename"; name="test"\r\n'
|
||||
'\r\n'
|
||||
'OK\r\n'
|
||||
'------sanic--\r\n',
|
||||
'------sanic\r\n'
|
||||
'content-disposition: form-data; filename="filename"; name="test"\r\n'
|
||||
'\r\n'
|
||||
'content-type: application/json; {"field": "value"}\r\n'
|
||||
'------sanic--\r\n',
|
||||
])
|
||||
def test_request_multipart_files(app, payload):
|
||||
@app.route("/", methods=["POST"])
|
||||
async def post(request):
|
||||
return text("OK")
|
||||
|
||||
headers = {'content-type': 'multipart/form-data; boundary=----sanic'}
|
||||
|
||||
request, _ = app.test_client.post(data=payload, headers=headers)
|
||||
assert request.files.get('test').name == "filename"
|
||||
|
||||
|
||||
def test_request_multipart_file_with_json_content_type(app):
|
||||
@app.route("/", methods=["POST"])
|
||||
async def post(request):
|
||||
return text("OK")
|
||||
|
||||
payload = '------sanic\r\nContent-Disposition: form-data; name="file"; filename="test.json"' \
|
||||
'\r\nContent-Type: application/json\r\n\r\n\r\n------sanic--'
|
||||
|
||||
headers = {'content-type': 'multipart/form-data; boundary=------sanic'}
|
||||
|
||||
request, _ = app.test_client.post(data=payload, headers=headers)
|
||||
assert request.files.get('file').type == 'application/json'
|
||||
|
||||
|
||||
def test_request_multipart_with_multiple_files_and_type(app):
|
||||
@app.route("/", methods=["POST"])
|
||||
async def post(request):
|
||||
return text("OK")
|
||||
|
||||
payload = '------sanic\r\nContent-Disposition: form-data; name="file"; filename="test.json"' \
|
||||
'\r\nContent-Type: application/json\r\n\r\n\r\n' \
|
||||
'------sanic\r\nContent-Disposition: form-data; name="file"; filename="some_file.pdf"\r\n' \
|
||||
'Content-Type: application/pdf\r\n\r\n\r\n------sanic--'
|
||||
headers = {'content-type': 'multipart/form-data; boundary=------sanic'}
|
||||
|
||||
request, _ = app.test_client.post(data=payload, headers=headers)
|
||||
assert len(request.files.getlist('file')) == 2
|
||||
assert request.files.getlist('file')[0].type == 'application/json'
|
||||
assert request.files.getlist('file')[1].type == 'application/pdf'
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -84,11 +84,11 @@ def test_static_content_range_correct(app, file_name, static_file_directory):
|
|||
'Range': 'bytes=12-19'
|
||||
}
|
||||
request, response = app.test_client.get('/testing.file', headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
static_file_directory, file_name))[12:19]
|
||||
static_file_directory, file_name))[12:20]
|
||||
assert int(response.headers[
|
||||
'Content-Length']) == len(static_content)
|
||||
assert response.body == static_content
|
||||
|
@ -104,7 +104,7 @@ def test_static_content_range_front(app, file_name, static_file_directory):
|
|||
'Range': 'bytes=12-'
|
||||
}
|
||||
request, response = app.test_client.get('/testing.file', headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
|
@ -124,7 +124,7 @@ def test_static_content_range_back(app, file_name, static_file_directory):
|
|||
'Range': 'bytes=-12'
|
||||
}
|
||||
request, response = app.test_client.get('/testing.file', headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
|
|
|
@ -212,11 +212,11 @@ def test_static_content_range_correct(app, file_name, static_file_directory):
|
|||
assert uri == app.url_for('static', name='static', filename='any')
|
||||
|
||||
request, response = app.test_client.get(uri, headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
static_file_directory, file_name))[12:19]
|
||||
static_file_directory, file_name))[12:20]
|
||||
assert int(response.headers[
|
||||
'Content-Length']) == len(static_content)
|
||||
assert response.body == static_content
|
||||
|
@ -233,11 +233,11 @@ def test_static_content_range_correct(app, file_name, static_file_directory):
|
|||
filename='any')
|
||||
|
||||
request, response = app.test_client.get(uri, headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
static_file_directory, file_name))[12:19]
|
||||
static_file_directory, file_name))[12:20]
|
||||
assert int(response.headers[
|
||||
'Content-Length']) == len(static_content)
|
||||
assert response.body == static_content
|
||||
|
@ -263,7 +263,7 @@ def test_static_content_range_front(app, file_name, static_file_directory):
|
|||
assert uri == app.url_for('static', name='static', filename='any')
|
||||
|
||||
request, response = app.test_client.get(uri, headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
|
@ -284,7 +284,7 @@ def test_static_content_range_front(app, file_name, static_file_directory):
|
|||
filename='any')
|
||||
|
||||
request, response = app.test_client.get(uri, headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
|
@ -314,7 +314,7 @@ def test_static_content_range_back(app, file_name, static_file_directory):
|
|||
assert uri == app.url_for('static', name='static', filename='any')
|
||||
|
||||
request, response = app.test_client.get(uri, headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
|
@ -335,7 +335,7 @@ def test_static_content_range_back(app, file_name, static_file_directory):
|
|||
filename='any')
|
||||
|
||||
request, response = app.test_client.get(uri, headers=headers)
|
||||
assert response.status == 200
|
||||
assert response.status == 206
|
||||
assert 'Content-Length' in response.headers
|
||||
assert 'Content-Range' in response.headers
|
||||
static_content = bytes(get_file_content(
|
||||
|
|
Loading…
Reference in New Issue
Block a user