host: blog published-post read endpoint GET /<slug>/ -> HTML, 156/156
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 15s

lib/host/blog.sx serves blog posts as HTML at GET /<slug>/ (the original
strangler target, Quart blog post_detail). A post is a content-on-sx CtDoc
rendered via content/html; anonymous + world-visible. In-memory slug->doc
registry now (host/blog-lookup swappable for a persist-backed content stream
later, handler/route unchanged). :slug catch-all mounted LAST so /feed,
/health, /internal/* take precedence. Needs the Smalltalk+persist+content
preload chain + (st-bootstrap-classes!)+(content/bootstrap!) — blog.sx
self-bootstraps at load. serve.sh loads the chain + seeds a welcome post.
Ledger gains the migrated blog post-detail (off-Quart 50% -> 53%).

LIVE: blog.rose-ash.com/welcome/ renders real HTML through Cloudflare->Caddy;
/feed still JSON (precedence verified), unknown slug 404.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 18:08:12 +00:00
parent 2217a704a6
commit e2a90e3bbd
7 changed files with 200 additions and 12 deletions

85
lib/host/tests/blog.sx Normal file
View File

@@ -0,0 +1,85 @@
;; lib/host/tests/blog.sx — the blog published-post read endpoint. A registered
;; post renders to HTML at GET /<slug>/; unknown slugs 404. Also pins route
;; precedence: the catch-all :slug must NOT shadow domain routes mounted before it.
(define host-bl-pass 0)
(define host-bl-fail 0)
(define host-bl-fails (list))
(define
host-bl-test
(fn
(name actual expected)
(if
(= actual expected)
(set! host-bl-pass (+ host-bl-pass 1))
(begin
(set! host-bl-fail (+ host-bl-fail 1))
(append! host-bl-fails {:name name :actual actual :expected expected})))))
(define host-bl-req (fn (target) (dream-request "GET" target {} "")))
;; feed mounted BEFORE blog so /feed is not captured by the :slug catch-all.
(define host-bl-app
(host/make-app (list host/feed-routes host/blog-routes)))
;; ── render a registered post ────────────────────────────────────────
(host/blog-reset!)
(host/blog-register! "welcome" (host/blog-make "welcome" "Hello SX" "Served by lib/host."))
(host-bl-test
"post 200"
(dream-status (host-bl-app (host-bl-req "/welcome/")))
200)
(host-bl-test
"post content-type html"
(contains? (dream-resp-header (host-bl-app (host-bl-req "/welcome/")) "content-type") "text/html")
true)
(host-bl-test
"post renders heading"
(contains? (dream-resp-body (host-bl-app (host-bl-req "/welcome/"))) "<h1>Hello SX</h1>")
true)
(host-bl-test
"post renders body"
(contains? (dream-resp-body (host-bl-app (host-bl-req "/welcome/"))) "Served by lib/host.")
true)
;; trailing slash optional — /welcome and /welcome/ both resolve
(host-bl-test
"no trailing slash also 200"
(dream-status (host-bl-app (host-bl-req "/welcome")))
200)
;; golden: endpoint body == content facade render of the same doc
(host-bl-test
"golden = content/html"
(dream-resp-body (host-bl-app (host-bl-req "/welcome/")))
(content/html (host/blog-make "welcome" "Hello SX" "Served by lib/host.")))
;; ── unknown slug -> 404 ─────────────────────────────────────────────
(host-bl-test
"unknown slug 404"
(dream-status (host-bl-app (host-bl-req "/nope/")))
404)
(host-bl-test
"404 names the slug"
(contains? (dream-resp-body (host-bl-app (host-bl-req "/nope/"))) "nope")
true)
;; ── route precedence: domain routes win over the :slug catch-all ────
(feed/reset!)
(host-bl-test
"/feed served by feed, not blog 404"
(dream-status (host-bl-app (host-bl-req "/feed")))
200)
(host-bl-test
"/feed body is the feed envelope, not HTML"
(contains? (dream-resp-body (host-bl-app (host-bl-req "/feed"))) "\"ok\":true")
true)
(define
host-bl-tests-run!
(fn
()
{:total (+ host-bl-pass host-bl-fail)
:passed host-bl-pass
:failed host-bl-fail
:fails host-bl-fails}))

View File

@@ -50,8 +50,13 @@
(get (host/ledger-find host/ledger "POST" "/internal/actions/relate") :status)
"proxied")
(host-lg-test
"find migrated blog post -> handler"
(get (host/ledger-find host/ledger "GET" "/:slug") :handler)
"host/blog-post")
;; ── status queries ──────────────────────────────────────────────────
(host-lg-test "migrated count" (len (host/ledger-migrated host/ledger)) 6)
(host-lg-test "migrated count" (len (host/ledger-migrated host/ledger)) 7)
(host-lg-test "native count" (len (host/ledger-native host/ledger)) 1)
(host-lg-test "proxied count" (len (host/ledger-proxied host/ledger)) 7)
@@ -72,7 +77,7 @@
;; ── domain queries ──────────────────────────────────────────────────
(host-lg-test "relations domain count" (len (host/ledger-by-domain host/ledger "relations")) 7)
(host-lg-test "likes domain count" (len (host/ledger-by-domain host/ledger "likes")) 4)
(host-lg-test "domains count" (len (host/ledger-domains host/ledger)) 4)
(host-lg-test "domains count" (len (host/ledger-domains host/ledger)) 5)
(host-lg-test
"domains has relations"
(some (fn (d) (= d "relations")) (host/ledger-domains host/ledger))
@@ -84,12 +89,12 @@
;; ── coverage ────────────────────────────────────────────────────────
(define host-lg-cov (host/ledger-coverage host/ledger))
(host-lg-test "coverage total" (get host-lg-cov :total) 14)
(host-lg-test "coverage migrated" (get host-lg-cov :migrated) 6)
(host-lg-test "coverage total" (get host-lg-cov :total) 15)
(host-lg-test "coverage migrated" (get host-lg-cov :migrated) 7)
(host-lg-test "coverage proxied" (get host-lg-cov :proxied) 7)
(host-lg-test "coverage native" (get host-lg-cov :native) 1)
(host-lg-test "coverage served" (get host-lg-cov :served) 7)
(host-lg-test "coverage percent" (get host-lg-cov :percent) 50)
(host-lg-test "coverage served" (get host-lg-cov :served) 8)
(host-lg-test "coverage percent" (get host-lg-cov :percent) 53)
(define
host-lg-tests-run!