;; lib/host/tests/sxtp.sx — SXTP message algebra, wire serialise/parse round-trip, ;; and the Dream HTTP <-> SXTP bridge. (define host-sx-pass 0) (define host-sx-fail 0) (define host-sx-fails (list)) (define host-sx-test (fn (name actual expected) (if (= actual expected) (set! host-sx-pass (+ host-sx-pass 1)) (begin (set! host-sx-fail (+ host-sx-fail 1)) (append! host-sx-fails {:name name :actual actual :expected expected}))))) ;; ── constructors + predicates ────────────────────────────────────── (define host-sx-req (sxtp/request "navigate" "/x" {:headers {:host "h"}})) (define host-sx-resp (sxtp/ok {:id "e1"})) (host-sx-test "request?" (sxtp/request? host-sx-req) true) (host-sx-test "request not response" (sxtp/response? host-sx-req) false) (host-sx-test "response?" (sxtp/response? host-sx-resp) true) (host-sx-test "condition?" (sxtp/condition? (sxtp/condition "x" {})) true) ;; ── accessors (verb/status are symbols) ──────────────────────────── (host-sx-test "verb" (symbol->string (sxtp/verb host-sx-req)) "navigate") (host-sx-test "path" (sxtp/path host-sx-req) "/x") (host-sx-test "req header" (get (sxtp/req-headers host-sx-req) :host) "h") (host-sx-test "status" (symbol->string (sxtp/status host-sx-resp)) "ok") (host-sx-test "body" (get (sxtp/body host-sx-resp) :id) "e1") ;; ── status helpers ───────────────────────────────────────────────── (host-sx-test "created status" (symbol->string (sxtp/status (sxtp/created {}))) "created") (host-sx-test "not-found status" (symbol->string (sxtp/status (sxtp/not-found "/p" "gone"))) "not-found") (host-sx-test "not-found body is condition" (sxtp/condition? (sxtp/body (sxtp/not-found "/p" "gone"))) true) (host-sx-test "forbidden message" (sxtp/cond-message (sxtp/body (sxtp/forbidden "no"))) "no") ;; ── serialise (deterministic top-level field order) ──────────────── (host-sx-test "serialize request" (sxtp/serialize host-sx-req) "(request :verb navigate :path \"/x\" :headers {:host \"h\"})") (host-sx-test "serialize ok" (sxtp/serialize (sxtp/ok {:id "e1"})) "(response :status ok :body {:id \"e1\"})") ;; nested condition rides the wire in its (condition ...) list form, no :msg leak. (host-sx-test "serialize nested condition as list" (contains? (sxtp/serialize (sxtp/not-found "/p" "gone")) "(condition :type resource-not-found") true) (host-sx-test "serialize no :msg leak" (contains? (sxtp/serialize host-sx-resp) ":msg") false) ;; ── parse + round-trip ───────────────────────────────────────────── (define host-sx-parsed (sxtp/parse "(request :verb query :path \"/events\" :headers {:host \"h\"})")) (host-sx-test "parse msg type" (sxtp/request? host-sx-parsed) true) (host-sx-test "parse verb" (symbol->string (sxtp/verb host-sx-parsed)) "query") (host-sx-test "parse path" (sxtp/path host-sx-parsed) "/events") (host-sx-test "parse nested header normalised" (get (sxtp/req-headers host-sx-parsed) :host) "h") (define host-sx-rt (sxtp/parse (sxtp/serialize (sxtp/ok {:id "e1" :n 3})))) (host-sx-test "round-trip status" (symbol->string (sxtp/status host-sx-rt)) "ok") (host-sx-test "round-trip body id" (get (sxtp/body host-sx-rt) :id) "e1") (host-sx-test "round-trip body n" (get (sxtp/body host-sx-rt) :n) 3) ;; ── HTTP <-> SXTP mappings ───────────────────────────────────────── (host-sx-test "verb GET->fetch" (symbol->string (sxtp/verb-for-method "GET")) "fetch") (host-sx-test "verb POST->create" (symbol->string (sxtp/verb-for-method "POST")) "create") (host-sx-test "verb DELETE->delete" (symbol->string (sxtp/verb-for-method "DELETE")) "delete") (host-sx-test "verb unknown->fetch" (symbol->string (sxtp/verb-for-method "WIBBLE")) "fetch") (host-sx-test "http ok->200" (sxtp/http-status (string->symbol "ok")) 200) (host-sx-test "http not-found->404" (sxtp/http-status (string->symbol "not-found")) 404) ;; ── Dream bridge ─────────────────────────────────────────────────── (define host-sx-from (sxtp/from-dream (dream-request "POST" "/feed?a=1" {} "hi"))) (host-sx-test "from-dream verb" (symbol->string (sxtp/verb host-sx-from)) "create") (host-sx-test "from-dream path" (sxtp/path host-sx-from) "/feed") (host-sx-test "from-dream param" (sxtp/param host-sx-from "a") "1") (host-sx-test "from-dream body" (sxtp/body host-sx-from) "hi") (define host-sx-tod (sxtp/to-dream (sxtp/ok {:id "e1"}))) (host-sx-test "to-dream status" (dream-status host-sx-tod) 200) (host-sx-test "to-dream content-type text/sx" (dream-resp-header host-sx-tod "content-type") "text/sx") (host-sx-test "to-dream body is sx text" (dream-resp-body host-sx-tod) "{:id \"e1\"}") (host-sx-test "to-dream not-found->404" (dream-status (sxtp/to-dream (sxtp/not-found "/p" "gone"))) 404) (host-sx-test "to-dream forbidden->403" (dream-status (sxtp/to-dream (sxtp/forbidden "no"))) 403) (define host-sx-tests-run! (fn () {:total (+ host-sx-pass host-sx-fail) :passed host-sx-pass :failed host-sx-fail :fails host-sx-fails}))