"""Tests for the declarative handler system.""" import asyncio import pytest from shared.sx import parse, evaluate from shared.sx.types import HandlerDef from shared.sx.handlers import ( register_handler, get_handler, get_all_handlers, clear_handlers, ) from shared.sx.async_eval import async_eval, async_render from shared.sx.primitives_io import RequestContext # --------------------------------------------------------------------------- # Registry # --------------------------------------------------------------------------- class TestHandlerRegistry: def setup_method(self): clear_handlers() def test_register_and_get(self): env = {} evaluate(parse("(defhandler test-card (&key slug) slug)"), env) handler = env["handler:test-card"] register_handler("blog", handler) assert get_handler("blog", "test-card") is handler def test_get_nonexistent(self): assert get_handler("blog", "nope") is None def test_get_all_handlers(self): env = {} evaluate(parse("(defhandler h1 (&key a) a)"), env) evaluate(parse("(defhandler h2 (&key b) b)"), env) register_handler("svc", env["handler:h1"]) register_handler("svc", env["handler:h2"]) all_h = get_all_handlers("svc") assert "h1" in all_h assert "h2" in all_h def test_clear_service(self): env = {} evaluate(parse("(defhandler h1 (&key a) a)"), env) register_handler("svc", env["handler:h1"]) clear_handlers("svc") assert get_handler("svc", "h1") is None def test_clear_all(self): env = {} evaluate(parse("(defhandler h1 (&key a) a)"), env) register_handler("svc1", env["handler:h1"]) register_handler("svc2", env["handler:h1"]) clear_handlers() assert get_all_handlers("svc1") == {} assert get_all_handlers("svc2") == {} # --------------------------------------------------------------------------- # HandlerDef creation via evaluator # --------------------------------------------------------------------------- class TestHandlerDefCreation: def test_basic(self): env = {} evaluate(parse("(defhandler my-handler (&key id name) (str id name))"), env) h = env["handler:my-handler"] assert isinstance(h, HandlerDef) assert h.name == "my-handler" assert h.params == ["id", "name"] def test_no_params(self): env = {} evaluate(parse("(defhandler simple (&key) 42)"), env) h = env["handler:simple"] assert h.params == [] def test_handler_closure_captures_env(self): env = {"x": 99} evaluate(parse("(defhandler uses-closure (&key) x)"), env) h = env["handler:uses-closure"] assert h.closure.get("x") == 99 # --------------------------------------------------------------------------- # Async evaluator # --------------------------------------------------------------------------- class TestAsyncEval: def test_literals(self): ctx = RequestContext() assert asyncio.get_event_loop().run_until_complete( async_eval(parse("42"), {}, ctx)) == 42 def test_let_and_arithmetic(self): ctx = RequestContext() result = asyncio.get_event_loop().run_until_complete( async_eval(parse("(let ((x 10) (y 20)) (+ x y))"), {}, ctx)) assert result == 30 def test_if_when(self): ctx = RequestContext() result = asyncio.get_event_loop().run_until_complete( async_eval(parse("(if true 1 2)"), {}, ctx)) assert result == 1 def test_map_lambda(self): ctx = RequestContext() result = asyncio.get_event_loop().run_until_complete( async_eval(parse("(map (fn (x) (* x x)) (list 1 2 3))"), {}, ctx)) assert result == [1, 4, 9] def test_macro_expansion(self): ctx = RequestContext() env = {} asyncio.get_event_loop().run_until_complete( async_eval(parse("(defmacro double (x) `(+ ,x ,x))"), env, ctx)) result = asyncio.get_event_loop().run_until_complete( async_eval(parse("(double 5)"), env, ctx)) assert result == 10 class TestAsyncRender: def test_simple_html(self): ctx = RequestContext() result = asyncio.get_event_loop().run_until_complete( async_render(parse('(div :class "test" "hello")'), {}, ctx)) assert result == '
hello
' def test_component(self): ctx = RequestContext() env = {} evaluate(parse('(defcomp ~bold (&key text) (strong text))'), env) result = asyncio.get_event_loop().run_until_complete( async_render(parse('(~bold :text "hi")'), env, ctx)) assert result == "hi" def test_let_with_render(self): ctx = RequestContext() result = asyncio.get_event_loop().run_until_complete( async_render(parse('(let ((x "hello")) (span x))'), {}, ctx)) assert result == "hello" def test_map_render(self): ctx = RequestContext() result = asyncio.get_event_loop().run_until_complete( async_render(parse('(ul (map (fn (x) (li x)) (list "a" "b")))'), {}, ctx)) assert result == "" def test_macro_in_render(self): ctx = RequestContext() env = {} evaluate(parse('(defmacro em-text (t) `(em ,t))'), env) result = asyncio.get_event_loop().run_until_complete( async_render(parse('(em-text "wow")'), env, ctx)) assert result == "wow"