;; 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) (define host-fd-fails (list)) (define host-fd-test (fn (name actual expected) (if (= actual expected) (set! host-fd-pass (+ host-fd-pass 1)) (begin (set! host-fd-fail (+ host-fd-fail 1)) (append! host-fd-fails {:name name :actual actual :expected expected}))))) (define host-fd-req (fn (target) (dream-request "GET" target {} ""))) (define host-fd-app (host/make-app (list host/feed-routes))) ;; ── empty feed ───────────────────────────────────────────────────── (feed/reset!) (host-fd-test "empty feed 200" (dream-status (host-fd-app (host-fd-req "/feed"))) 200) (host-fd-test "empty feed data:[]" (contains? (dream-resp-body (host-fd-app (host-fd-req "/feed"))) "\"data\":[]") true) ;; ── seeded feed ──────────────────────────────────────────────────── (feed/reset!) (feed/post {:actor "alice" :verb "post" :object "p1" :at 1}) (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 marker precedes the oldest. (host-fd-test "timeline recent-first" (let ((body (dream-resp-body (host-fd-app (host-fd-req "/feed"))))) (< (index-of body "\"at\":3") (index-of body "\"at\":1"))) true) ;; actor filter: only alice's two activities. (host-fd-test "actor filter count" (feed/count (feed/by-actor (feed/recent (feed/all)) "alice")) 2) (host-fd-test "actor filter excludes bob" (contains? (dream-resp-body (host-fd-app (host-fd-req "/feed?actor=alice"))) "bob") false) ;; limit: cap to a single activity (the most recent). (host-fd-test "limit caps results" (contains? (dream-resp-body (host-fd-app (host-fd-req "/feed?limit=1"))) "\"at\":1") false) ;; ── golden: endpoint = subsystem recent stream + envelope ─────────── (host-fd-test "golden full timeline" (dream-resp-body (host-fd-app (host-fd-req "/feed"))) (str "{\"ok\":true,\"data\":" (dream-json-encode (feed/items (feed/recent (feed/all)))) "}")) (host-fd-test "golden actor-filtered" (dream-resp-body (host-fd-app (host-fd-req "/feed?actor=alice"))) (str "{\"ok\":true,\"data\":" (dream-json-encode (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 () {:total (+ host-fd-pass host-fd-fail) :passed host-fd-pass :failed host-fd-fail :fails host-fd-fails}))