-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
137 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import asyncio | ||
import json as stdjson | ||
|
||
from collections import namedtuple | ||
from textwrap import dedent | ||
from typing import AnyStr | ||
|
||
import pytest | ||
|
||
from sanic_testing.reusable import ReusableClient | ||
|
||
from sanic import json, text | ||
from sanic.app import Sanic | ||
|
||
|
||
PORT = 1234 | ||
|
||
|
||
class RawClient: | ||
CRLF = b"\r\n" | ||
|
||
def __init__(self, host: str, port: int): | ||
self.reader = None | ||
self.writer = None | ||
self.host = host | ||
self.port = port | ||
|
||
async def connect(self): | ||
self.reader, self.writer = await asyncio.open_connection( | ||
self.host, self.port | ||
) | ||
|
||
async def close(self): | ||
self.writer.close() | ||
await self.writer.wait_closed() | ||
|
||
async def send(self, message: AnyStr): | ||
if isinstance(message, str): | ||
msg = self._clean(message).encode("utf-8") | ||
else: | ||
msg = message | ||
await self._send(msg) | ||
|
||
async def _send(self, message: bytes): | ||
if not self.writer: | ||
raise Exception("No open write stream") | ||
self.writer.write(message) | ||
|
||
async def recv(self, nbytes: int = -1) -> bytes: | ||
if not self.reader: | ||
raise Exception("No open read stream") | ||
return await self.reader.read(nbytes) | ||
|
||
def _clean(self, message: str) -> str: | ||
return ( | ||
dedent(message) | ||
.lstrip("\n") | ||
.replace("\n", self.CRLF.decode("utf-8")) | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def test_app(app: Sanic): | ||
app.config.KEEP_ALIVE_TIMEOUT = 1 | ||
|
||
@app.get("/") | ||
async def base_handler(request): | ||
return text("111122223333444455556666777788889999") | ||
|
||
@app.post("/upload", stream=True) | ||
async def upload_handler(request): | ||
data = [part.decode("utf-8") async for part in request.stream] | ||
return json(data) | ||
|
||
return app | ||
|
||
|
||
@pytest.fixture | ||
def runner(test_app): | ||
client = ReusableClient(test_app, port=PORT) | ||
client.run() | ||
yield client | ||
client.stop() | ||
|
||
|
||
@pytest.fixture | ||
def client(runner): | ||
client = namedtuple("Client", ("raw", "send", "recv")) | ||
|
||
raw = RawClient(runner.host, runner.port) | ||
runner._run(raw.connect()) | ||
|
||
def send(msg): | ||
nonlocal runner | ||
nonlocal raw | ||
runner._run(raw.send(msg)) | ||
|
||
def recv(**kwargs): | ||
nonlocal runner | ||
nonlocal raw | ||
method = raw.recv_until if "until" in kwargs else raw.recv | ||
return runner._run(method(**kwargs)) | ||
|
||
yield client(raw, send, recv) | ||
|
||
runner._run(raw.close()) | ||
|
||
|
||
def test_full_message(client): | ||
client.send( | ||
""" | ||
GET / HTTP/1.1 | ||
host: localhost:7777 | ||
""" | ||
) | ||
response = client.recv() | ||
assert len(response) == 140 | ||
assert b"200 OK" in response | ||
|
||
|
||
def test_transfer_chunked(client): | ||
client.send( | ||
""" | ||
POST /upload HTTP/1.1 | ||
transfer-encoding: chunked | ||
""" | ||
) | ||
client.send(b"3\r\nfoo\r\n") | ||
client.send(b"3\r\nbar\r\n") | ||
client.send(b"0\r\n\r\n") | ||
response = client.recv() | ||
_, body = response.rsplit(b"\r\n\r\n", 1) | ||
data = stdjson.loads(body) | ||
|
||
assert data == ["foo", "bar"] |