feat: sync latest ARR implementation
This commit is contained in:
151
tests/test_arr_web_server.py
Normal file
151
tests/test_arr_web_server.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import threading
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from http.server import ThreadingHTTPServer
|
||||
from typing import Any, Iterator, Mapping
|
||||
|
||||
from arr_web.contracts import MAX_UPLOAD_BYTES, Response, success
|
||||
from arr_web.server import handler_for
|
||||
|
||||
|
||||
class RecordingApplication:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[dict[str, Any]] = []
|
||||
|
||||
def handle(
|
||||
self,
|
||||
method: str,
|
||||
path: str,
|
||||
headers: Mapping[str, str],
|
||||
body: bytes,
|
||||
*,
|
||||
client_id: str,
|
||||
) -> Response:
|
||||
self.calls.append(
|
||||
{
|
||||
"method": method,
|
||||
"path": path,
|
||||
"headers": dict(headers),
|
||||
"body": body,
|
||||
"client_id": client_id,
|
||||
}
|
||||
)
|
||||
return Response.json(200, success({"method": method}))
|
||||
|
||||
|
||||
@contextmanager
|
||||
def running_server(
|
||||
application: RecordingApplication,
|
||||
) -> Iterator[tuple[str, int]]:
|
||||
server = ThreadingHTTPServer(("127.0.0.1", 0), handler_for(application))
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
host, port = server.server_address
|
||||
yield str(host), int(port)
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
thread.join(timeout=5)
|
||||
|
||||
|
||||
class PortalHttpAdapterTests(unittest.TestCase):
|
||||
def test_patch_and_delete_forward_json_bodies_to_application(self) -> None:
|
||||
application = RecordingApplication()
|
||||
with running_server(application) as (host, port):
|
||||
connection = http.client.HTTPConnection(host, port, timeout=5)
|
||||
try:
|
||||
patch_body = json.dumps(
|
||||
{"draft_id": "bookingdraft-test", "room_type": "U-TWN", "quantity": 2}
|
||||
).encode("utf-8")
|
||||
connection.request(
|
||||
"PATCH",
|
||||
"/api/company-reports/source/draft/items/7",
|
||||
body=patch_body,
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
patch_response = connection.getresponse()
|
||||
self.assertEqual(patch_response.status, 200)
|
||||
self.assertEqual(patch_response.getheader("X-Content-Type-Options"), "nosniff")
|
||||
patch_response.read()
|
||||
|
||||
delete_body = b'{"draft_id":"bookingdraft-test"}'
|
||||
connection.request(
|
||||
"DELETE",
|
||||
"/api/company-reports/source/draft/items/7",
|
||||
body=delete_body,
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
delete_response = connection.getresponse()
|
||||
self.assertEqual(delete_response.status, 200)
|
||||
delete_response.read()
|
||||
|
||||
batch_delete_body = b'{"draft_id":"bookingdraft-test","item_ids":[7,8]}'
|
||||
connection.request(
|
||||
"DELETE",
|
||||
"/api/company-reports/source/draft/items",
|
||||
body=batch_delete_body,
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
batch_delete_response = connection.getresponse()
|
||||
self.assertEqual(batch_delete_response.status, 200)
|
||||
batch_delete_response.read()
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
self.assertEqual(
|
||||
[(call["method"], call["body"]) for call in application.calls],
|
||||
[
|
||||
("PATCH", patch_body),
|
||||
("DELETE", delete_body),
|
||||
("DELETE", batch_delete_body),
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
[call["path"] for call in application.calls],
|
||||
[
|
||||
"/api/company-reports/source/draft/items/7",
|
||||
"/api/company-reports/source/draft/items/7",
|
||||
"/api/company-reports/source/draft/items",
|
||||
],
|
||||
)
|
||||
|
||||
def test_mutation_methods_reject_missing_or_oversized_content_length(self) -> None:
|
||||
application = RecordingApplication()
|
||||
with running_server(application) as (host, port):
|
||||
missing = http.client.HTTPConnection(host, port, timeout=5)
|
||||
try:
|
||||
missing.putrequest("PATCH", "/api/company-reports/source/draft/items/7")
|
||||
missing.endheaders()
|
||||
missing_response = missing.getresponse()
|
||||
self.assertEqual(missing_response.status, 411)
|
||||
self.assertEqual(
|
||||
json.loads(missing_response.read())["error"]["code"],
|
||||
"CONTENT_LENGTH_REQUIRED",
|
||||
)
|
||||
finally:
|
||||
missing.close()
|
||||
|
||||
oversized = http.client.HTTPConnection(host, port, timeout=5)
|
||||
try:
|
||||
oversized.putrequest("DELETE", "/api/company-reports/source/draft")
|
||||
oversized.putheader("Content-Length", str(MAX_UPLOAD_BYTES + 1))
|
||||
oversized.endheaders()
|
||||
oversized_response = oversized.getresponse()
|
||||
self.assertEqual(oversized_response.status, 413)
|
||||
self.assertEqual(
|
||||
json.loads(oversized_response.read())["error"]["code"],
|
||||
"REQUEST_TOO_LARGE",
|
||||
)
|
||||
finally:
|
||||
oversized.close()
|
||||
|
||||
self.assertEqual(application.calls, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user