Files
rose-ash/lib/dream/tests/static.sx
giles 04b44401fb
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
dream: static file serving — mime, etags, 304, ranges, traversal guard + 28 tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 14:51:25 +00:00

126 lines
4.6 KiB
Plaintext

;; lib/dream/tests/static.sx — content types, etags, 304, ranges, traversal.
(define dream-st-pass 0)
(define dream-st-fail 0)
(define dream-st-fails (list))
(define
dream-st-test
(fn
(name actual expected)
(if
(= actual expected)
(set! dream-st-pass (+ dream-st-pass 1))
(begin
(set! dream-st-fail (+ dream-st-fail 1))
(append! dream-st-fails {:name name :actual actual :expected expected})))))
;; ── content type + ext ─────────────────────────────────────────────
(dream-st-test "ext css" (dr/ext-of "a/b/style.css") "css")
(dream-st-test "ext multi-dot" (dr/ext-of "a.min.js") "js")
(dream-st-test "ext none" (dr/ext-of "README") "")
(dream-st-test
"ctype css"
(dream-content-type-for "x.css")
"text/css; charset=utf-8")
(dream-st-test
"ctype html"
(dream-content-type-for "x.html")
"text/html; charset=utf-8")
(dream-st-test "ctype png" (dream-content-type-for "x.png") "image/png")
(dream-st-test
"ctype unknown"
(dream-content-type-for "x.bin")
"application/octet-stream")
;; ── etag ───────────────────────────────────────────────────────────
(dream-st-test
"etag deterministic"
(= (dr/etag-of "abc") (dr/etag-of "abc"))
true)
(dream-st-test
"etag content-sensitive"
(= (dr/etag-of "abc") (dr/etag-of "abd"))
false)
(dream-st-test
"etag length-sensitive"
(= (dr/etag-of "ab") (dr/etag-of "abc"))
false)
;; ── serving via router mount ───────────────────────────────────────
(define dream-st-files {:/srv/app.css "body{color:red}" :/srv/index.html "<h1>Hi</h1>"})
(define dream-st-fs (dream-memory-fs dream-st-files))
(define
dream-st-app
(dream-router
(list (dream-get "/static/**" (dream-static-with "/srv" dream-st-fs)))))
(define
dream-st-get
(fn
(target headers)
(dream-st-app (dream-request "GET" target headers ""))))
(define dream-st-css (dream-st-get "/static/app.css" {}))
(dream-st-test "serve status 200" (dream-status dream-st-css) 200)
(dream-st-test "serve body" (dream-resp-body dream-st-css) "body{color:red}")
(dream-st-test
"serve content-type"
(dream-resp-header dream-st-css "content-type")
"text/css; charset=utf-8")
(dream-st-test
"serve accept-ranges"
(dream-resp-header dream-st-css "accept-ranges")
"bytes")
(dream-st-test
"serve has etag"
(not (nil? (dream-resp-header dream-st-css "etag")))
true)
(dream-st-test
"missing file 404"
(dream-status (dream-st-get "/static/nope.txt" {}))
404)
(dream-st-test
"traversal blocked 403"
(dream-status (dream-st-get "/static/../secret" {}))
403)
;; ── conditional: If-None-Match -> 304 ──────────────────────────────
(define dream-st-etag (dream-resp-header dream-st-css "etag"))
(define dream-st-304 (dream-st-get "/static/app.css" {:If-None-Match dream-st-etag}))
(dream-st-test "matching etag 304" (dream-status dream-st-304) 304)
(dream-st-test "304 empty body" (dream-resp-body dream-st-304) "")
(dream-st-test
"stale etag 200"
(dream-status (dream-st-get "/static/app.css" {:If-None-Match "\"stale\""}))
200)
(dream-st-test
"star etag 304"
(dream-status (dream-st-get "/static/app.css" {:If-None-Match "*"}))
304)
;; ── range requests ─────────────────────────────────────────────────
(define dream-st-range (dream-st-get "/static/app.css" {:Range "bytes=0-3"}))
(dream-st-test "range status 206" (dream-status dream-st-range) 206)
(dream-st-test "range body slice" (dream-resp-body dream-st-range) "body")
(dream-st-test
"range content-range"
(dream-resp-header dream-st-range "content-range")
"bytes 0-3/15")
(define dream-st-open (dream-st-get "/static/app.css" {:Range "bytes=5-"}))
(dream-st-test "open range body" (dream-resp-body dream-st-open) "color:red}")
(dream-st-test
"open range header"
(dream-resp-header dream-st-open "content-range")
"bytes 5-14/15")
(define dream-st-bad (dream-st-get "/static/app.css" {:Range "bytes=20-30"}))
(dream-st-test
"unsatisfiable range 416"
(dream-status dream-st-bad)
416)
(dream-st-test
"416 content-range"
(dream-resp-header dream-st-bad "content-range")
"bytes */15")
(define dream-st-tests-run! (fn () {:total (+ dream-st-pass dream-st-fail) :passed dream-st-pass :failed dream-st-fail :fails dream-st-fails}))