host: Phase 2 — middleware (auth+ACL+error) + guarded POST /feed, 43/43
Composable handler->handler layers over Dream's primitives, with auth and permission POLICY injected so the layer is policy-free and testable: - middleware.sx: host/wrap-errors (JSON 500 via dream-catch-with), host/require-auth (bearer->principal via dream-bearer-token, JSON 401, injected token resolver), host/require-permission (lib/acl acl/permit? gate, JSON 403, injected resource extractor), host/pipeline (first = outermost) - feed.sx: POST /feed via host/feed-write-routes — auth ∘ ACL(post,feed) ∘ wrap-errors over host/feed-create (parse JSON body -> feed/post -> 201; non-object -> 400). Created activity reads back via GET /feed. - middleware suite (9) + feed write tests (6 new); conformance preloads now include the Datalog engine + ACL subsystem + Dream auth/error. ACL works with string atoms (no symbol coercion). Mute/prefs layer and sxtp.sx deferred to the next tick. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
;; lib/host/tests/feed.sx — the first migrated endpoint, GET /feed. Includes a
|
||||
;; golden test: the host response body must equal the feed subsystem's own
|
||||
;; recent-first stream wrapped in the standard envelope — the endpoint adds the
|
||||
;; HTTP/JSON shell and nothing else.
|
||||
;; lib/host/tests/feed.sx — the migrated feed endpoints, GET /feed (read) and
|
||||
;; POST /feed (guarded write). Includes a golden test: the host read response
|
||||
;; body must equal the feed subsystem's own recent-first stream wrapped in the
|
||||
;; standard envelope — the endpoint adds the HTTP/JSON shell and nothing else.
|
||||
|
||||
(define host-fd-pass 0)
|
||||
(define host-fd-fail 0)
|
||||
@@ -43,7 +43,7 @@
|
||||
(feed/post {:actor "bob" :verb "post" :object "p2" :at 2})
|
||||
(feed/post {:actor "alice" :verb "like" :object "p2" :at 3})
|
||||
|
||||
;; recent-first: newest activity (at 3) leads, so its object p2 appears before p1.
|
||||
;; recent-first: newest activity (at 3) leads, so its marker precedes the oldest.
|
||||
(host-fd-test
|
||||
"timeline recent-first"
|
||||
(let ((body (dream-resp-body (host-fd-app (host-fd-req "/feed")))))
|
||||
@@ -88,6 +88,47 @@
|
||||
(feed/items (feed/by-actor (feed/recent (feed/all)) "alice")))
|
||||
"}"))
|
||||
|
||||
;; ── write: POST /feed (auth + ACL + action) ────────────────────────
|
||||
(acl/load! (list (acl-grant "alice" "post" "feed")))
|
||||
(define host-fd-resolve (fn (tok) (if (= tok "good") "alice" nil)))
|
||||
(define
|
||||
host-fd-wapp
|
||||
(host/make-app
|
||||
(list host/feed-routes (host/feed-write-routes host-fd-resolve))))
|
||||
(define
|
||||
host-fd-post
|
||||
(fn (auth body)
|
||||
(dream-request "POST" "/feed" (if auth {:authorization auth} {}) body)))
|
||||
|
||||
(feed/reset!)
|
||||
(host-fd-test
|
||||
"post no auth -> 401"
|
||||
(dream-status (host-fd-wapp (host-fd-post nil "{}")))
|
||||
401)
|
||||
(host-fd-test
|
||||
"post unchanged feed after 401"
|
||||
(feed/size)
|
||||
0)
|
||||
(host-fd-test
|
||||
"post authed+permitted -> 201"
|
||||
(dream-status
|
||||
(host-fd-wapp
|
||||
(host-fd-post
|
||||
"Bearer good"
|
||||
"{\"actor\":\"alice\",\"verb\":\"post\",\"object\":\"p9\",\"at\":9}")))
|
||||
201)
|
||||
(host-fd-test "post grew feed" (feed/size) 1)
|
||||
(host-fd-test
|
||||
"created activity visible in timeline"
|
||||
(contains?
|
||||
(dream-resp-body (host-fd-wapp (host-fd-req "/feed")))
|
||||
"p9")
|
||||
true)
|
||||
(host-fd-test
|
||||
"post non-object body -> 400"
|
||||
(dream-status (host-fd-wapp (host-fd-post "Bearer good" "[1,2]")))
|
||||
400)
|
||||
|
||||
(define
|
||||
host-fd-tests-run!
|
||||
(fn
|
||||
|
||||
Reference in New Issue
Block a user