Data-first HO forms, fix plan pages, aser error handling (1080/1080)
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Evaluator: data-first higher-order forms — ho-swap-args auto-detects (map coll fn) vs (map fn coll), both work. Threading + HO: (-> data (map fn)) dispatches through CEK HO machinery via quoted-value splice. 17 new tests in test-cek-advanced.sx. Fix plan pages: add mother-language, isolated-evaluator, rust-wasm-host to page-functions.sx plan() — were in defpage but missing from URL router. Aser error handling: pages.py now catches EvalError separately, renders visible error banner instead of silently sending empty content. All except blocks include traceback in logs. Scope primitives: register collect!/collected/clear-collected!/emitted/ emit!/context in shared/sx/primitives.py so hand-written _aser can resolve them (fixes ~cssx/flush expansion failure). New test file: shared/sx/tests/test_aser_errors.py — 19 pytest tests for error propagation through all aser control flow forms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -344,3 +344,82 @@
|
||||
(deftest "scope pops correctly after body"
|
||||
(assert-equal "outer"
|
||||
(render-sx "(scope \"sc-pop\" :value \"outer\" (scope \"sc-pop\" :value \"inner\" \"ignore\") (context \"sc-pop\"))"))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Error propagation — errors in aser control flow must throw, not silently
|
||||
;; produce wrong output or fall through to :else branches.
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defsuite "aser-error-propagation"
|
||||
|
||||
;; --- case: matched branch errors must throw, not fall through to :else ---
|
||||
|
||||
(deftest "case — error in matched branch throws, not falls through"
|
||||
;; If the matched case body references an undefined symbol, the aser must
|
||||
;; throw an error — NOT silently skip to :else.
|
||||
(assert-throws
|
||||
(fn () (render-sx "(case \"x\" \"x\" undefined-symbol-xyz :else \"fallback\")"))))
|
||||
|
||||
(deftest "case — :else body error also throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(case \"no-match\" \"x\" \"ok\" :else undefined-symbol-xyz)"))))
|
||||
|
||||
(deftest "case — matched branch with nested error throws"
|
||||
;; Error inside a tag within the matched body must propagate.
|
||||
(assert-throws
|
||||
(fn () (render-sx "(case \"a\" \"a\" (div (p undefined-sym-abc)) :else (p \"index\"))"))))
|
||||
|
||||
;; --- cond: matched branch errors must throw ---
|
||||
|
||||
(deftest "cond — error in matched branch throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(cond true undefined-cond-sym :else \"fallback\")"))))
|
||||
|
||||
(deftest "cond — error in :else branch throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(cond false \"skip\" :else undefined-cond-sym)"))))
|
||||
|
||||
;; --- if/when: body errors must throw ---
|
||||
|
||||
(deftest "if — error in true branch throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(if true undefined-if-sym \"fallback\")"))))
|
||||
|
||||
(deftest "when — error in body throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(when true undefined-when-sym)"))))
|
||||
|
||||
;; --- let: binding or body errors must throw ---
|
||||
|
||||
(deftest "let — error in binding throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(let ((x undefined-let-sym)) (p x))"))))
|
||||
|
||||
(deftest "let — error in body throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(let ((x 1)) (p undefined-let-body-sym))"))))
|
||||
|
||||
;; --- begin/do: body errors must throw ---
|
||||
|
||||
(deftest "do — error in body throws"
|
||||
(assert-throws
|
||||
(fn () (render-sx "(do \"ok\" undefined-do-sym)"))))
|
||||
|
||||
;; --- component expansion inside case: the production bug ---
|
||||
|
||||
;; --- sync aser serializes components without expansion ---
|
||||
|
||||
(deftest "case — component in matched branch serializes unexpanded"
|
||||
;; Sync aser serializes component calls as SX wire format.
|
||||
;; Expansion only happens in async path with expand-components.
|
||||
(assert-equal "(~broken :title \"test\")"
|
||||
(render-sx
|
||||
"(do (defcomp ~broken (&key title) (div (p title) (p no-such-helper)))
|
||||
(case \"slug\" \"slug\" (~broken :title \"test\") :else \"index\"))")))
|
||||
|
||||
(deftest "case — unmatched falls through to :else correctly"
|
||||
(assert-equal "index"
|
||||
(render-sx
|
||||
"(do (defcomp ~page (&key x) (div x))
|
||||
(case \"miss\" \"hit\" (~page :x \"found\") :else \"index\"))"))))
|
||||
|
||||
Reference in New Issue
Block a user