async def test_empty_content(): stream = encode() content = b"".join([part async for part in stream]) assert stream.can_replay() assert stream.get_headers() == {} assert content == b""
async def test_bytes_content(): stream = encode(data=b"Hello, world!") content = b"".join([part async for part in stream]) assert stream.can_replay() assert stream.get_headers() == {"Content-Length": "13"} assert content == b"Hello, world!"
def test_multipart_encode(): data = { "a": "1", "b": b"C", "c": ["11", "22", "33"], "d": { "ff": ["1", b"2", "3"], "fff": ["11", b"22", "33"] }, "f": "", } files = {"file": ("name.txt", io.BytesIO(b"<file content>"))} with mock.patch("os.urandom", return_value=os.urandom(16)): boundary = binascii.hexlify(os.urandom(16)).decode("ascii") stream = encode(data=data, files=files) assert stream.content_type == f"multipart/form-data; boundary={boundary}" assert stream.body == ( '--{0}\r\nContent-Disposition: form-data; name="a"\r\n\r\n1\r\n' '--{0}\r\nContent-Disposition: form-data; name="b"\r\n\r\nC\r\n' '--{0}\r\nContent-Disposition: form-data; name="c"\r\n\r\n11\r\n' '--{0}\r\nContent-Disposition: form-data; name="c"\r\n\r\n22\r\n' '--{0}\r\nContent-Disposition: form-data; name="c"\r\n\r\n33\r\n' '--{0}\r\nContent-Disposition: form-data; name="d"\r\n\r\nff\r\n' '--{0}\r\nContent-Disposition: form-data; name="d"\r\n\r\nfff\r\n' '--{0}\r\nContent-Disposition: form-data; name="f"\r\n\r\n\r\n' '--{0}\r\nContent-Disposition: form-data; name="file";' ' filename="name.txt"\r\n' "Content-Type: text/plain\r\n\r\n<file content>\r\n" "--{0}--\r\n" "".format(boundary).encode("ascii"))
async def test_multipart_files_content(): files = {"file": io.BytesIO(b"<file content>")} stream = encode(files=files, boundary=b"+++") sync_content = b"".join([part for part in stream]) async_content = b"".join([part async for part in stream]) assert stream.can_replay() assert stream.get_headers() == { "Content-Length": "138", "Content-Type": "multipart/form-data; boundary=+++", } assert sync_content == b"".join([ b"--+++\r\n", b'Content-Disposition: form-data; name="file"; filename="upload"\r\n', b"Content-Type: application/octet-stream\r\n", b"\r\n", b"<file content>\r\n", b"--+++--\r\n", ]) assert async_content == b"".join([ b"--+++\r\n", b'Content-Disposition: form-data; name="file"; filename="upload"\r\n', b"Content-Type: application/octet-stream\r\n", b"\r\n", b"<file content>\r\n", b"--+++--\r\n", ])
async def test_urlencoded_content(): stream = encode(data={"Hello": "world!"}) content = b"".join([part async for part in stream]) assert stream.can_replay() assert stream.get_headers() == { "Content-Length": "14", "Content-Type": "application/x-www-form-urlencoded", } assert content == b"Hello=world%21"
async def test_json_content(): stream = encode(json={"Hello": "world!"}) content = b"".join([part async for part in stream]) assert stream.can_replay() assert stream.get_headers() == { "Content-Length": "19", "Content-Type": "application/json", } assert content == b'{"Hello": "world!"}'
async def test_aiterator_content(): async def hello_world(): yield b"Hello, " yield b"world!" stream = encode(data=hello_world()) content = b"".join([part async for part in stream]) assert not stream.can_replay() assert stream.get_headers() == {"Transfer-Encoding": "chunked"} assert content == b"Hello, world!"
def test_multipart_encode_files_allows_filenames_as_none(): files = {"file": (None, io.BytesIO(b"<file content>"))} with mock.patch("os.urandom", return_value=os.urandom(16)): boundary = binascii.hexlify(os.urandom(16)).decode("ascii") stream = encode(data={}, files=files) assert stream.content_type == f"multipart/form-data; boundary={boundary}" assert stream.body == ( '--{0}\r\nContent-Disposition: form-data; name="file"\r\n\r\n' "<file content>\r\n--{0}--\r\n" "".format(boundary).encode("ascii"))
async def test_aiterator_is_stream_consumed(): async def hello_world(): yield b"Hello, " yield b"world!" stream = encode(data=hello_world()) b"".join([part async for part in stream]) assert stream.is_stream_consumed with pytest.raises(StreamConsumed) as _: b"".join([part async for part in stream])
def test_multipart_encode_files_allows_str_content(): files = {"file": ("test.txt", "<string content>", "text/plain")} with mock.patch("os.urandom", return_value=os.urandom(16)): boundary = binascii.hexlify(os.urandom(16)).decode("ascii") stream = encode(data={}, files=files) assert stream.content_type == f"multipart/form-data; boundary={boundary}" assert stream.body == ( '--{0}\r\nContent-Disposition: form-data; name="file"; ' 'filename="test.txt"\r\n' "Content-Type: text/plain\r\n\r\n<string content>\r\n" "--{0}--\r\n" "".format(boundary).encode("ascii"))
def test_multipart_encode_files_guesses_correct_content_type( file_name, expected_content_type): files = {"file": (file_name, io.BytesIO(b"<file content>"))} with mock.patch("os.urandom", return_value=os.urandom(16)): boundary = binascii.hexlify(os.urandom(16)).decode("ascii") stream = encode(data={}, files=files) assert stream.content_type == f"multipart/form-data; boundary={boundary}" assert stream.body == ( f'--{boundary}\r\nContent-Disposition: form-data; name="file"; ' f'filename="{file_name}"\r\nContent-Type: ' f"{expected_content_type}\r\n\r\n<file content>\r\n--{boundary}--\r\n" "".encode("ascii"))
async def test_aiterator_content(): async def hello_world(): yield b"Hello, " yield b"world!" stream = encode(data=hello_world()) content = b"".join([part async for part in stream]) assert not stream.can_replay() assert stream.get_headers() == {"Transfer-Encoding": "chunked"} assert content == b"Hello, world!" with pytest.raises(RuntimeError): [part for part in stream] with pytest.raises(StreamConsumed): [part async for part in stream]
async def test_multipart_data_and_files_content(): data = {"message": "Hello, world!"} files = {"file": io.BytesIO(b"<file content>")} stream = encode(data=data, files=files, boundary=b"+++") content = b"".join([part async for part in stream]) assert stream.can_replay() assert stream.get_headers() == { "Content-Length": "210", "Content-Type": "multipart/form-data; boundary=+++", } assert content == b"".join([ b"--+++\r\n", b'Content-Disposition: form-data; name="message"\r\n', b"\r\n", b"Hello, world!\r\n", b"--+++\r\n", b'Content-Disposition: form-data; name="file"; filename="upload"\r\n', b"Content-Type: application/octet-stream\r\n", b"\r\n", b"<file content>\r\n", b"--+++--\r\n", ])
def add_response( self, status_code: int = 200, http_version: str = "HTTP/1.1", headers: dict = None, data: content_streams.RequestData = None, files: content_streams.RequestFiles = None, json: Any = None, boundary: bytes = None, **matchers, ): """ Mock the response that will be sent if a request match. :param status_code: HTTP status code of the response to send. Default to 200 (OK). :param http_version: HTTP protocol version of the response to send. Default to HTTP/1.1 :param headers: HTTP headers of the response to send. Default to no headers. :param data: HTTP body of the response, can be an iterator to stream content, bytes, str of the full body or a dictionary in case of a multipart. :param files: Multipart files. :param json: HTTP body of the response (if JSON should be used as content type) if data is not provided. :param boundary: Multipart boundary if files is provided. :param url: Full URL identifying the request(s) to match. Can be a str, a re.Pattern instance or a httpx.URL instance. :param method: HTTP method identifying the request(s) to match. :param match_headers: HTTP headers identifying the request(s) to match. Must be a dictionary. :param match_content: Full HTTP body identifying the request(s) to match. Must be bytes. """ response = Response( status_code=status_code, http_version=http_version, headers=list(headers.items()) if headers else [], stream=content_streams.encode( data=data, files=files, json=json, boundary=boundary ), request=None, # Will be set upon reception of the actual request ) self._responses.append((_RequestMatcher(**matchers), response))
def test_invalid_argument(): with pytest.raises(TypeError): encode(123)