blogimport: Q-M4 live source — internal-data query adapter (75/75)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m5s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m5s
source.sx: live-source adapter resolving Q-M4 (internal-data query, not direct PG). Injected fetch-fn transport port (hexagonal seam); parse-row maps a blog post-row to the importer post dict and parses the :lexical JSON string via dream-json-parse. End-to-end drivers: backfill! (enumerate->fetch->import) and sync-verify (enumerate->fetch->verify), + backfill-ids! explicit-id fallback. Tests mock the transport against the documented response contract incl. a real lexical JSON string. README flags the one blog-side gap (add a published-posts enumeration query) + production fetch_data wiring (lives in lib/host). source 20/20; total 75/75. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
83
lib/blogimport/tests/source.sx
Normal file
83
lib/blogimport/tests/source.sx
Normal file
@@ -0,0 +1,83 @@
|
||||
; lib/blogimport/tests/source.sx — live-source adapter (Q-M4 internal-data query)
|
||||
(st-bootstrap-classes!)
|
||||
(content-bootstrap-blocks!)
|
||||
(content-bootstrap-doc!)
|
||||
(content-bootstrap-callout!)
|
||||
(content-bootstrap-media!)
|
||||
|
||||
; ---- canned service responses (lexical arrives as a JSON STRING, the DB column) ----
|
||||
(define
|
||||
lex1
|
||||
"{\"root\":{\"children\":[{\"type\":\"heading\",\"tag\":\"h2\",\"children\":[{\"type\":\"text\",\"text\":\"Live\"}]},{\"type\":\"paragraph\",\"children\":[{\"type\":\"text\",\"text\":\"from db\"}]}]}}")
|
||||
(define
|
||||
row1
|
||||
{:uuid "post-1" :slug "live" :title "Live" :status "published"
|
||||
:visibility "public" :tags (list "x") :authors (list "u") :lexical lex1})
|
||||
(define
|
||||
row2
|
||||
{:uuid "post-2" :slug "two" :title "Two" :status "published"
|
||||
:lexical "{\"children\":[{\"type\":\"paragraph\",\"children\":[{\"type\":\"text\",\"text\":\"second\"}]}]}"})
|
||||
|
||||
; ---- mock transport: (fetch-fn query params) -> response ----
|
||||
(define
|
||||
mock-fetch
|
||||
(fn (query params)
|
||||
(cond
|
||||
((equal? query "published-posts") (list "post-1" "post-2"))
|
||||
((equal? query "post-by-id")
|
||||
(cond
|
||||
((equal? (get params :id) "post-1") row1)
|
||||
((equal? (get params :id) "post-2") row2)
|
||||
(else nil)))
|
||||
(else nil))))
|
||||
|
||||
; ---- parse-row maps fields + parses the lexical JSON string ----
|
||||
(define post1 (blogimport/parse-row row1))
|
||||
(bi-test "parse-row id from uuid" (get post1 :id) "post-1")
|
||||
(bi-test "parse-row title" (get post1 :title) "Live")
|
||||
(bi-test "parse-row tags" (get post1 :tags) (list "x"))
|
||||
(bi-test "parse-row lexical parsed to blocks"
|
||||
(map blk-type (blogimport/lex-blocks (get post1 :lexical))) (list "heading" "text"))
|
||||
|
||||
; ---- id fallback (:id when no :uuid) + structured (non-string) lexical ----
|
||||
(define
|
||||
post3
|
||||
(blogimport/parse-row
|
||||
{:id "post-3" :slug "s3"
|
||||
:lexical {:children (list {:type "paragraph" :children (list {:type "text" :text "x"})})}}))
|
||||
(bi-test "parse-row id fallback" (get post3 :id) "post-3")
|
||||
(bi-test "parse-row structured lexical used as-is"
|
||||
(map blk-type (blogimport/lex-blocks (get post3 :lexical))) (list "text"))
|
||||
|
||||
; ---- enumeration + source-posts ----
|
||||
(bi-test "published-ids" (blogimport/published-ids mock-fetch) (list "post-1" "post-2"))
|
||||
(bi-test "source-posts ids"
|
||||
(map (fn (p) (get p :id)) (blogimport/source-posts mock-fetch))
|
||||
(list "post-1" "post-2"))
|
||||
|
||||
; ---- end-to-end backfill from the live source ----
|
||||
(define B (persist/open))
|
||||
(define cov (blogimport/backfill! B mock-fetch 10))
|
||||
(bi-test "backfill total" (get cov :total) 2)
|
||||
(bi-test "backfill imported" (get cov :imported) 2)
|
||||
(bi-test "backfill post-1 version-count" (content/version-count B "post-1") 2)
|
||||
(bi-test "backfill post-1 head ids" (doc-ids (content/head B "post-1")) (list "b0" "b1"))
|
||||
(bi-test "backfill post-1 body text"
|
||||
(str (blk-send (doc-find (content/head B "post-1") "b1") "text")) "from db")
|
||||
(bi-test "backfill meta title" (get (blogimport/load-meta B "post-1") :title) "Live")
|
||||
|
||||
; ---- backfill is idempotent (one-way sync re-run) ----
|
||||
(define cov2 (blogimport/backfill! B mock-fetch 11))
|
||||
(bi-test "backfill rerun skipped" (get cov2 :skipped) 2)
|
||||
|
||||
; ---- sync-verify: persisted streams match the live-source oracle ----
|
||||
(define sv (blogimport/sync-verify B mock-fetch))
|
||||
(bi-test "sync-verify total" (get sv :total) 2)
|
||||
(bi-test "sync-verify ok" (get sv :ok) 2)
|
||||
(bi-test "sync-verify no mismatch" (get sv :mismatched) (list))
|
||||
|
||||
; ---- explicit-id fallback path (before the enumeration query lands) ----
|
||||
(define B2 (persist/open))
|
||||
(define covx (blogimport/backfill-ids! B2 mock-fetch (list "post-2") 10))
|
||||
(bi-test "backfill-ids imported" (get covx :imported) 1)
|
||||
(bi-test "backfill-ids post-2 ids" (doc-ids (content/head B2 "post-2")) (list "b0"))
|
||||
Reference in New Issue
Block a user