2018-10-29 14:46:42 +00:00
|
|
|
import os
|
|
|
|
|
2018-10-12 17:15:04 +00:00
|
|
|
from starlette.formparsers import UploadFile
|
|
|
|
from starlette.requests import Request
|
2018-10-29 14:46:42 +00:00
|
|
|
from starlette.responses import JSONResponse
|
2018-10-12 17:15:04 +00:00
|
|
|
from starlette.testclient import TestClient
|
|
|
|
|
|
|
|
|
|
|
|
class ForceMultipartDict(dict):
|
|
|
|
def __bool__(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
# FORCE_MULTIPART is an empty dict that boolean-evaluates as `True`.
|
|
|
|
FORCE_MULTIPART = ForceMultipartDict()
|
|
|
|
|
|
|
|
|
2019-03-19 16:03:19 +00:00
|
|
|
async def app(scope, receive, send):
|
|
|
|
request = Request(scope, receive)
|
|
|
|
data = await request.form()
|
|
|
|
output = {}
|
|
|
|
for key, value in data.items():
|
|
|
|
if isinstance(value, UploadFile):
|
|
|
|
content = await value.read()
|
|
|
|
output[key] = {
|
|
|
|
"filename": value.filename,
|
|
|
|
"content": content.decode(),
|
|
|
|
"content_type": value.content_type,
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
output[key] = value
|
|
|
|
await request.close()
|
|
|
|
response = JSONResponse(output)
|
|
|
|
await response(scope, receive, send)
|
|
|
|
|
|
|
|
|
|
|
|
async def multi_items_app(scope, receive, send):
|
|
|
|
request = Request(scope, receive)
|
|
|
|
data = await request.form()
|
|
|
|
output = {}
|
|
|
|
for key, value in data.multi_items():
|
|
|
|
if key not in output:
|
|
|
|
output[key] = []
|
|
|
|
if isinstance(value, UploadFile):
|
|
|
|
content = await value.read()
|
|
|
|
output[key].append(
|
|
|
|
{
|
2019-02-01 09:06:33 +00:00
|
|
|
"filename": value.filename,
|
|
|
|
"content": content.decode(),
|
|
|
|
"content_type": value.content_type,
|
|
|
|
}
|
2019-03-19 16:03:19 +00:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
output[key].append(value)
|
|
|
|
await request.close()
|
|
|
|
response = JSONResponse(output)
|
|
|
|
await response(scope, receive, send)
|
|
|
|
|
|
|
|
|
|
|
|
async def app_read_body(scope, receive, send):
|
|
|
|
request = Request(scope, receive)
|
|
|
|
# Read bytes, to force request.stream() to return the already parsed body
|
2019-05-13 14:22:59 +00:00
|
|
|
await request.body()
|
2019-03-19 16:03:19 +00:00
|
|
|
data = await request.form()
|
|
|
|
output = {}
|
|
|
|
for key, value in data.items():
|
|
|
|
output[key] = value
|
|
|
|
await request.close()
|
|
|
|
response = JSONResponse(output)
|
|
|
|
await response(scope, receive, send)
|
2019-01-04 09:44:42 +00:00
|
|
|
|
|
|
|
|
2018-10-12 17:15:04 +00:00
|
|
|
def test_multipart_request_data(tmpdir):
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)
|
|
|
|
assert response.json() == {"some": "data"}
|
|
|
|
|
|
|
|
|
|
|
|
def test_multipart_request_files(tmpdir):
|
|
|
|
path = os.path.join(tmpdir, "test.txt")
|
|
|
|
with open(path, "wb") as file:
|
|
|
|
file.write(b"<file content>")
|
|
|
|
|
|
|
|
client = TestClient(app)
|
2019-01-08 15:12:26 +00:00
|
|
|
with open(path, "rb") as f:
|
|
|
|
response = client.post("/", files={"test": f})
|
|
|
|
assert response.json() == {
|
2019-02-01 09:06:33 +00:00
|
|
|
"test": {
|
|
|
|
"filename": "test.txt",
|
|
|
|
"content": "<file content>",
|
|
|
|
"content_type": "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_multipart_request_files_with_content_type(tmpdir):
|
|
|
|
path = os.path.join(tmpdir, "test.txt")
|
|
|
|
with open(path, "wb") as file:
|
|
|
|
file.write(b"<file content>")
|
|
|
|
|
|
|
|
client = TestClient(app)
|
|
|
|
with open(path, "rb") as f:
|
|
|
|
response = client.post("/", files={"test": ("test.txt", f, "text/plain")})
|
|
|
|
assert response.json() == {
|
|
|
|
"test": {
|
|
|
|
"filename": "test.txt",
|
|
|
|
"content": "<file content>",
|
|
|
|
"content_type": "text/plain",
|
|
|
|
}
|
2019-01-08 15:12:26 +00:00
|
|
|
}
|
2018-10-12 17:15:04 +00:00
|
|
|
|
|
|
|
|
2018-12-18 10:26:06 +00:00
|
|
|
def test_multipart_request_multiple_files(tmpdir):
|
|
|
|
path1 = os.path.join(tmpdir, "test1.txt")
|
|
|
|
with open(path1, "wb") as file:
|
|
|
|
file.write(b"<file1 content>")
|
|
|
|
|
|
|
|
path2 = os.path.join(tmpdir, "test2.txt")
|
|
|
|
with open(path2, "wb") as file:
|
|
|
|
file.write(b"<file2 content>")
|
|
|
|
|
|
|
|
client = TestClient(app)
|
2019-01-08 15:12:26 +00:00
|
|
|
with open(path1, "rb") as f1, open(path2, "rb") as f2:
|
2019-02-01 09:06:33 +00:00
|
|
|
response = client.post(
|
|
|
|
"/", files={"test1": f1, "test2": ("test2.txt", f2, "text/plain")}
|
|
|
|
)
|
2019-01-08 15:12:26 +00:00
|
|
|
assert response.json() == {
|
2019-02-01 09:06:33 +00:00
|
|
|
"test1": {
|
|
|
|
"filename": "test1.txt",
|
|
|
|
"content": "<file1 content>",
|
|
|
|
"content_type": "",
|
|
|
|
},
|
|
|
|
"test2": {
|
|
|
|
"filename": "test2.txt",
|
|
|
|
"content": "<file2 content>",
|
|
|
|
"content_type": "text/plain",
|
|
|
|
},
|
2019-01-08 15:12:26 +00:00
|
|
|
}
|
2018-12-18 10:26:06 +00:00
|
|
|
|
|
|
|
|
2019-01-22 19:47:47 +00:00
|
|
|
def test_multi_items(tmpdir):
|
|
|
|
path1 = os.path.join(tmpdir, "test1.txt")
|
|
|
|
with open(path1, "wb") as file:
|
|
|
|
file.write(b"<file1 content>")
|
|
|
|
|
|
|
|
path2 = os.path.join(tmpdir, "test2.txt")
|
|
|
|
with open(path2, "wb") as file:
|
|
|
|
file.write(b"<file2 content>")
|
|
|
|
|
|
|
|
client = TestClient(multi_items_app)
|
|
|
|
with open(path1, "rb") as f1, open(path2, "rb") as f2:
|
|
|
|
response = client.post(
|
2019-02-01 09:06:33 +00:00
|
|
|
"/",
|
|
|
|
data=[("test1", "abc")],
|
|
|
|
files=[("test1", f1), ("test1", ("test2.txt", f2, "text/plain"))],
|
2019-01-22 19:47:47 +00:00
|
|
|
)
|
|
|
|
assert response.json() == {
|
|
|
|
"test1": [
|
|
|
|
"abc",
|
2019-02-01 09:06:33 +00:00
|
|
|
{
|
|
|
|
"filename": "test1.txt",
|
|
|
|
"content": "<file1 content>",
|
|
|
|
"content_type": "",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"filename": "test2.txt",
|
|
|
|
"content": "<file2 content>",
|
|
|
|
"content_type": "text/plain",
|
|
|
|
},
|
2019-01-22 19:47:47 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-18 10:26:06 +00:00
|
|
|
def test_multipart_request_mixed_files_and_data(tmpdir):
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post(
|
|
|
|
"/",
|
|
|
|
data=(
|
|
|
|
# data
|
|
|
|
b"--a7f7ac8d4e2e437c877bb7b8d7cc549c\r\n"
|
|
|
|
b'Content-Disposition: form-data; name="field0"\r\n\r\n'
|
|
|
|
b"value0\r\n"
|
|
|
|
# file
|
|
|
|
b"--a7f7ac8d4e2e437c877bb7b8d7cc549c\r\n"
|
|
|
|
b'Content-Disposition: form-data; name="file"; filename="file.txt"\r\n'
|
|
|
|
b"Content-Type: text/plain\r\n\r\n"
|
|
|
|
b"<file content>\r\n"
|
|
|
|
# data
|
|
|
|
b"--a7f7ac8d4e2e437c877bb7b8d7cc549c\r\n"
|
|
|
|
b'Content-Disposition: form-data; name="field1"\r\n\r\n'
|
|
|
|
b"value1\r\n"
|
|
|
|
b"--a7f7ac8d4e2e437c877bb7b8d7cc549c--\r\n"
|
|
|
|
),
|
|
|
|
headers={
|
|
|
|
"Content-Type": "multipart/form-data; boundary=a7f7ac8d4e2e437c877bb7b8d7cc549c"
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert response.json() == {
|
2019-02-01 09:06:33 +00:00
|
|
|
"file": {
|
|
|
|
"filename": "file.txt",
|
|
|
|
"content": "<file content>",
|
|
|
|
"content_type": "text/plain",
|
|
|
|
},
|
2018-12-18 10:26:06 +00:00
|
|
|
"field0": "value0",
|
|
|
|
"field1": "value1",
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-12 17:15:04 +00:00
|
|
|
def test_urlencoded_request_data(tmpdir):
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/", data={"some": "data"})
|
|
|
|
assert response.json() == {"some": "data"}
|
|
|
|
|
|
|
|
|
|
|
|
def test_no_request_data(tmpdir):
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/")
|
|
|
|
assert response.json() == {}
|
2019-01-04 09:44:42 +00:00
|
|
|
|
|
|
|
|
2019-01-14 12:28:16 +00:00
|
|
|
def test_urlencoded_percent_encoding(tmpdir):
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/", data={"some": "da ta"})
|
|
|
|
assert response.json() == {"some": "da ta"}
|
|
|
|
|
|
|
|
|
|
|
|
def test_urlencoded_percent_encoding_keys(tmpdir):
|
|
|
|
client = TestClient(app)
|
|
|
|
response = client.post("/", data={"so me": "data"})
|
|
|
|
assert response.json() == {"so me": "data"}
|
|
|
|
|
|
|
|
|
2019-01-04 09:44:42 +00:00
|
|
|
def test_urlencoded_multi_field_app_reads_body(tmpdir):
|
|
|
|
client = TestClient(app_read_body)
|
|
|
|
response = client.post("/", data={"some": "data", "second": "key pair"})
|
|
|
|
assert response.json() == {"some": "data", "second": "key pair"}
|
|
|
|
|
|
|
|
|
|
|
|
def test_multipart_multi_field_app_reads_body(tmpdir):
|
|
|
|
client = TestClient(app_read_body)
|
|
|
|
response = client.post(
|
|
|
|
"/", data={"some": "data", "second": "key pair"}, files=FORCE_MULTIPART
|
|
|
|
)
|
|
|
|
assert response.json() == {"some": "data", "second": "key pair"}
|