84 lines
3.2 KiB
Python
84 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from backend.application import (
|
|
AUTHENTICATED_POST_HANDLERS,
|
|
PUBLIC_POST_HANDLERS,
|
|
RequestHandler,
|
|
)
|
|
|
|
|
|
class HttpDispatchContractTests(unittest.TestCase):
|
|
@staticmethod
|
|
def handler(path: str, calls: list[str]) -> RequestHandler:
|
|
handler = RequestHandler.__new__(RequestHandler)
|
|
handler.path = path
|
|
handler.require_auth = lambda: calls.append("auth") or True
|
|
handler.require_csrf = lambda: calls.append("csrf") or True
|
|
handler.require_access = lambda method, route: (
|
|
calls.append(f"access:{method}:{route}") or True
|
|
)
|
|
return handler
|
|
|
|
def test_named_handlers_are_real_registered_post_routes(self) -> None:
|
|
all_handlers = {**PUBLIC_POST_HANDLERS, **AUTHENTICATED_POST_HANDLERS}
|
|
self.assertEqual(
|
|
set(PUBLIC_POST_HANDLERS) & set(AUTHENTICATED_POST_HANDLERS), set()
|
|
)
|
|
for path, handler_name in all_handlers.items():
|
|
with self.subTest(path=path):
|
|
route = RequestHandler.route_registry.resolve("POST", path)
|
|
self.assertIsNotNone(route)
|
|
self.assertTrue(callable(getattr(RequestHandler, handler_name)))
|
|
expected_access = "public" if path in PUBLIC_POST_HANDLERS else None
|
|
if expected_access:
|
|
self.assertEqual(route.access, expected_access)
|
|
else:
|
|
self.assertNotEqual(route.access, "public")
|
|
|
|
def test_public_post_dispatches_without_authentication(self) -> None:
|
|
for path, handler_name in PUBLIC_POST_HANDLERS.items():
|
|
with self.subTest(path=path):
|
|
calls: list[str] = []
|
|
handler = self.handler(path, calls)
|
|
handler.require_auth = lambda: (_ for _ in ()).throw(
|
|
AssertionError("public route required authentication")
|
|
)
|
|
setattr(handler, handler_name, lambda: calls.append(handler_name))
|
|
|
|
RequestHandler.do_POST(handler)
|
|
|
|
self.assertEqual(calls, [handler_name])
|
|
|
|
def test_authenticated_post_preserves_guard_order(self) -> None:
|
|
for path, handler_name in AUTHENTICATED_POST_HANDLERS.items():
|
|
with self.subTest(path=path):
|
|
calls: list[str] = []
|
|
handler = self.handler(path, calls)
|
|
setattr(handler, handler_name, lambda: calls.append(handler_name))
|
|
|
|
RequestHandler.do_POST(handler)
|
|
|
|
self.assertEqual(
|
|
calls,
|
|
["auth", "csrf", f"access:POST:{path}", handler_name],
|
|
)
|
|
|
|
def test_failed_access_never_dispatches_protected_handler(self) -> None:
|
|
path, handler_name = next(iter(AUTHENTICATED_POST_HANDLERS.items()))
|
|
calls: list[str] = []
|
|
handler = self.handler(path, calls)
|
|
handler.require_access = lambda method, route: (
|
|
calls.append(f"access:{method}:{route}") or False
|
|
)
|
|
setattr(handler, handler_name, lambda: calls.append(handler_name))
|
|
|
|
RequestHandler.do_POST(handler)
|
|
|
|
self.assertEqual(calls, ["auth", "csrf", f"access:POST:{path}"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|