| 206 | |
| 207 | |
| 208 | def test_multipart_encode(tmp_path: typing.Any) -> None: |
| 209 | path = str(tmp_path / "name.txt") |
| 210 | with open(path, "wb") as f: |
| 211 | f.write(b"<file content>") |
| 212 | |
| 213 | url = "https://www.example.com/" |
| 214 | headers = {"Content-Type": "multipart/form-data; boundary=BOUNDARY"} |
| 215 | data = { |
| 216 | "a": "1", |
| 217 | "b": b"C", |
| 218 | "c": ["11", "22", "33"], |
| 219 | "d": "", |
| 220 | "e": True, |
| 221 | "f": "", |
| 222 | } |
| 223 | with open(path, "rb") as input_file: |
| 224 | files = {"file": ("name.txt", input_file)} |
| 225 | |
| 226 | request = httpx.Request("POST", url, headers=headers, data=data, files=files) |
| 227 | request.read() |
| 228 | |
| 229 | assert request.headers == { |
| 230 | "Host": "www.example.com", |
| 231 | "Content-Type": "multipart/form-data; boundary=BOUNDARY", |
| 232 | "Content-Length": str(len(request.content)), |
| 233 | } |
| 234 | assert request.content == ( |
| 235 | '--BOUNDARY\r\nContent-Disposition: form-data; name="a"\r\n\r\n1\r\n' |
| 236 | '--BOUNDARY\r\nContent-Disposition: form-data; name="b"\r\n\r\nC\r\n' |
| 237 | '--BOUNDARY\r\nContent-Disposition: form-data; name="c"\r\n\r\n11\r\n' |
| 238 | '--BOUNDARY\r\nContent-Disposition: form-data; name="c"\r\n\r\n22\r\n' |
| 239 | '--BOUNDARY\r\nContent-Disposition: form-data; name="c"\r\n\r\n33\r\n' |
| 240 | '--BOUNDARY\r\nContent-Disposition: form-data; name="d"\r\n\r\n\r\n' |
| 241 | '--BOUNDARY\r\nContent-Disposition: form-data; name="e"\r\n\r\ntrue\r\n' |
| 242 | '--BOUNDARY\r\nContent-Disposition: form-data; name="f"\r\n\r\n\r\n' |
| 243 | '--BOUNDARY\r\nContent-Disposition: form-data; name="file";' |
| 244 | ' filename="name.txt"\r\n' |
| 245 | "Content-Type: text/plain\r\n\r\n<file content>\r\n" |
| 246 | "--BOUNDARY--\r\n" |
| 247 | "".encode("ascii") |
| 248 | ) |
| 249 | |
| 250 | |
| 251 | def test_multipart_encode_unicode_file_contents() -> None: |