Files
rose-ash/lib/dream/tests/demos.sx
giles b1be3a36ec
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m2s
dream: chat (ws rooms) + todo (forms+CSRF) demos + 17 tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 14:57:17 +00:00

199 lines
6.1 KiB
Plaintext

;; lib/dream/tests/demos.sx — end-to-end demo apps exercising the full stack.
(define dream-dm-pass 0)
(define dream-dm-fail 0)
(define dream-dm-fails (list))
(define
dream-dm-test
(fn
(name actual expected)
(if
(= actual expected)
(set! dream-dm-pass (+ dream-dm-pass 1))
(begin
(set! dream-dm-fail (+ dream-dm-fail 1))
(append! dream-dm-fails {:name name :actual actual :expected expected})))))
(define
dream-dm-req
(fn (method target headers) (dream-request method target headers "")))
;; ── hello ──────────────────────────────────────────────────────────
(dream-dm-test
"hello root"
(dream-resp-body (dream-hello-app (dream-dm-req "GET" "/" {})))
"<h1>Hello, World!</h1>")
(dream-dm-test
"hello name"
(dream-resp-body
(dream-hello-app (dream-dm-req "GET" "/hello/Ada" {})))
"<h1>Hello, Ada!</h1>")
(dream-dm-test
"hello content-type"
(dream-resp-header
(dream-hello-app (dream-dm-req "GET" "/" {}))
"content-type")
"text/html; charset=utf-8")
;; ── counter (sessions) ─────────────────────────────────────────────
(define dream-dm-cbackend (dream-memory-sessions))
(define dream-dm-capp (dream-counter-app-with dream-dm-cbackend))
(define dream-dm-c1 (dream-dm-capp (dream-dm-req "GET" "/" {})))
(dream-dm-test
"counter first visit"
(dream-resp-body dream-dm-c1)
"<p>You have visited this page 1 time(s).</p>")
(dream-dm-test
"counter sets cookie"
(len (dream-resp-cookies dream-dm-c1))
1)
(dream-dm-test
"counter second visit"
(dream-resp-body (dream-dm-capp (dream-dm-req "GET" "/" {:Cookie "dream.session=s1"})))
"<p>You have visited this page 2 time(s).</p>")
(dream-dm-test
"counter third visit"
(dream-resp-body (dream-dm-capp (dream-dm-req "GET" "/" {:Cookie "dream.session=s1"})))
"<p>You have visited this page 3 time(s).</p>")
(define
dream-dm-reset
(dream-dm-capp (dream-dm-req "POST" "/reset" {:Cookie "dream.session=s1"})))
(dream-dm-test
"counter reset redirects"
(dream-status dream-dm-reset)
303)
(dream-dm-test
"counter after reset"
(dream-resp-body (dream-dm-capp (dream-dm-req "GET" "/" {:Cookie "dream.session=s1"})))
"<p>You have visited this page 1 time(s).</p>")
(dream-dm-test
"counter distinct session"
(dream-resp-body (dream-dm-capp (dream-dm-req "GET" "/" {})))
"<p>You have visited this page 1 time(s).</p>")
;; ── chat (websocket rooms) ─────────────────────────────────────────
(define dream-dm-rooms (dream-chat-rooms))
(define dream-dm-wsB (dream-mock-ws (list)))
(define dream-dm-wsC (dream-mock-ws (list)))
((get dream-dm-rooms :join) "general" dream-dm-wsB)
((get dream-dm-rooms :join) "general" dream-dm-wsC)
(dream-dm-test
"room has two members"
(len ((get dream-dm-rooms :members) "general"))
2)
;; client A joins, sends two messages, then disconnects
(define dream-dm-wsA (dream-mock-ws (list "hi" "again")))
((dream-chat-session dream-dm-rooms "general") dream-dm-wsA)
(dream-dm-test
"B got broadcasts"
(dream-ws-sent dream-dm-wsB)
(list "hi" "again"))
(dream-dm-test
"C got broadcasts"
(dream-ws-sent dream-dm-wsC)
(list "hi" "again"))
(dream-dm-test
"A echoed own messages"
(dream-ws-sent dream-dm-wsA)
(list "hi" "again"))
(dream-dm-test
"A left on disconnect"
(len ((get dream-dm-rooms :members) "general"))
2)
(dream-dm-test "A closed" (dream-ws-closed? dream-dm-wsA) true)
;; route produces an upgrade response
(define dream-dm-chat-app (dream-chat-app-with (dream-chat-rooms)))
(dream-dm-test
"chat route upgrades"
(dream-websocket?
(dream-dm-chat-app (dream-dm-req "GET" "/chat/lobby" {})))
true)
(dream-dm-test
"chat index html"
(dream-resp-body (dream-dm-chat-app (dream-dm-req "GET" "/" {})))
"<h1>Rooms</h1>")
;; ── todo (forms + CSRF) ────────────────────────────────────────────
(define dream-dm-todo-store (dream-todo-store))
(define dream-dm-todo-backend (dream-memory-sessions))
(define
dream-dm-todo-app
(dream-todo-app-with dream-dm-todo-store dream-dm-todo-backend "topsecret"))
(define
dream-dm-todo-tok
(dr/csrf-make-token dream-csrf-sign-default "topsecret" "s1"))
;; establish session s1
(dream-dm-todo-app (dream-request "GET" "/" {} ""))
(define
dream-dm-add1
(dream-dm-todo-app
(dream-request
"POST"
"/add"
{:Cookie "dream.session=s1"}
(str "text=Buy+milk&dream.csrf=" dream-dm-todo-tok))))
(dream-dm-test "todo add redirects" (dream-status dream-dm-add1) 303)
(dream-dm-test
"todo store has item"
(len ((get dream-dm-todo-store :all)))
1)
(define
dream-dm-todo-page
(dream-resp-body
(dream-dm-todo-app (dream-request "GET" "/" {:Cookie "dream.session=s1"} ""))))
(dream-dm-test
"todo lists item"
(contains? dream-dm-todo-page "Buy milk")
true)
(dream-dm-test
"todo has csrf tag"
(contains? dream-dm-todo-page "dream.csrf")
true)
(dream-dm-test
"todo item not done"
(contains? dream-dm-todo-page "[ ] Buy milk")
true)
(dream-dm-todo-app
(dream-request
"POST"
"/toggle/1"
{:Cookie "dream.session=s1"}
(str "dream.csrf=" dream-dm-todo-tok)))
(dream-dm-test
"todo toggled done"
(contains?
(dream-resp-body
(dream-dm-todo-app (dream-request "GET" "/" {:Cookie "dream.session=s1"} "")))
"[x] Buy milk")
true)
(dream-dm-test
"todo add without token 403"
(dream-status
(dream-dm-todo-app (dream-request "POST" "/add" {:Cookie "dream.session=s1"} "text=Sneaky")))
403)
(dream-dm-test
"todo unchanged after reject"
(len ((get dream-dm-todo-store :all)))
1)
(dream-dm-todo-app
(dream-request
"POST"
"/delete/1"
{:Cookie "dream.session=s1"}
(str "dream.csrf=" dream-dm-todo-tok)))
(dream-dm-test
"todo deleted"
(len ((get dream-dm-todo-store :all)))
0)
(define dream-dm-tests-run! (fn () {:total (+ dream-dm-pass dream-dm-fail) :passed dream-dm-pass :failed dream-dm-fail :fails dream-dm-fails}))