Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 37s
dl-demo-shortest-path-rules: path enumerates X→Z with cost W = sum of edge weights via is/+; shortest filters to the minimum cost path per (X, Y) pair via min aggregation. 3 demo tests cover direct/multi-hop choice, multi-hop wins on cheaper route, and unreachable-empty. Note: cycles produce infinite distance values without a depth filter; the rule docstring flags this and suggests adding (<, D, MAX) for graphs that may cycle.
286 lines
8.7 KiB
Plaintext
286 lines
8.7 KiB
Plaintext
;; lib/datalog/tests/demo.sx — Phase 10 demo programs.
|
|
|
|
(define dl-demo-pass 0)
|
|
(define dl-demo-fail 0)
|
|
(define dl-demo-failures (list))
|
|
|
|
(define
|
|
dl-demo-deep=?
|
|
(fn
|
|
(a b)
|
|
(cond
|
|
((and (list? a) (list? b))
|
|
(and (= (len a) (len b)) (dl-demo-deq-l? a b 0)))
|
|
((and (dict? a) (dict? b))
|
|
(let ((ka (keys a)) (kb (keys b)))
|
|
(and (= (len ka) (len kb)) (dl-demo-deq-d? a b ka 0))))
|
|
((and (number? a) (number? b)) (= a b))
|
|
(else (equal? a b)))))
|
|
|
|
(define
|
|
dl-demo-deq-l?
|
|
(fn
|
|
(a b i)
|
|
(cond
|
|
((>= i (len a)) true)
|
|
((not (dl-demo-deep=? (nth a i) (nth b i))) false)
|
|
(else (dl-demo-deq-l? a b (+ i 1))))))
|
|
|
|
(define
|
|
dl-demo-deq-d?
|
|
(fn
|
|
(a b ka i)
|
|
(cond
|
|
((>= i (len ka)) true)
|
|
((let ((k (nth ka i)))
|
|
(not (dl-demo-deep=? (get a k) (get b k))))
|
|
false)
|
|
(else (dl-demo-deq-d? a b ka (+ i 1))))))
|
|
|
|
(define
|
|
dl-demo-set=?
|
|
(fn
|
|
(a b)
|
|
(and
|
|
(= (len a) (len b))
|
|
(dl-demo-subset? a b)
|
|
(dl-demo-subset? b a))))
|
|
|
|
(define
|
|
dl-demo-subset?
|
|
(fn
|
|
(xs ys)
|
|
(cond
|
|
((= (len xs) 0) true)
|
|
((not (dl-demo-contains? ys (first xs))) false)
|
|
(else (dl-demo-subset? (rest xs) ys)))))
|
|
|
|
(define
|
|
dl-demo-contains?
|
|
(fn
|
|
(xs target)
|
|
(cond
|
|
((= (len xs) 0) false)
|
|
((dl-demo-deep=? (first xs) target) true)
|
|
(else (dl-demo-contains? (rest xs) target)))))
|
|
|
|
(define
|
|
dl-demo-test-set!
|
|
(fn
|
|
(name got expected)
|
|
(if
|
|
(dl-demo-set=? got expected)
|
|
(set! dl-demo-pass (+ dl-demo-pass 1))
|
|
(do
|
|
(set! dl-demo-fail (+ dl-demo-fail 1))
|
|
(append!
|
|
dl-demo-failures
|
|
(str
|
|
name
|
|
"\n expected (set): " expected
|
|
"\n got: " got))))))
|
|
|
|
(define
|
|
dl-demo-run-all!
|
|
(fn
|
|
()
|
|
(do
|
|
;; ── Federation ──────────────────────────────────────────
|
|
(dl-demo-test-set! "mutuals"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((follows alice bob) (follows bob alice)
|
|
(follows bob carol) (follows carol dave)))
|
|
dl-demo-federation-rules)
|
|
(quote (mutual alice X)))
|
|
(list {:X (quote bob)}))
|
|
|
|
(dl-demo-test-set! "reachable transitive"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((follows alice bob) (follows bob carol) (follows carol dave)))
|
|
dl-demo-federation-rules)
|
|
(quote (reachable alice X)))
|
|
(list {:X (quote bob)} {:X (quote carol)} {:X (quote dave)}))
|
|
|
|
(dl-demo-test-set! "foaf"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((follows alice bob) (follows bob carol) (follows alice dave)))
|
|
dl-demo-federation-rules)
|
|
(quote (foaf alice X)))
|
|
(list {:X (quote carol)}))
|
|
|
|
;; ── Content ─────────────────────────────────────────────
|
|
(dl-demo-test-set! "popular posts"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((authored alice p1) (authored bob p2) (authored carol p3)
|
|
(liked u1 p1) (liked u2 p1) (liked u3 p1)
|
|
(liked u1 p2)))
|
|
dl-demo-content-rules)
|
|
(quote (popular P)))
|
|
(list {:P (quote p1)}))
|
|
|
|
(dl-demo-test-set! "interesting feed"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((follows me alice) (follows me bob)
|
|
(authored alice p1) (authored bob p2)
|
|
(liked u1 p1) (liked u2 p1) (liked u3 p1)
|
|
(liked u4 p2)))
|
|
dl-demo-content-rules)
|
|
(quote (interesting me P)))
|
|
(list {:P (quote p1)}))
|
|
|
|
(dl-demo-test-set! "post likes count"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((authored alice p1)
|
|
(liked u1 p1) (liked u2 p1) (liked u3 p1)))
|
|
dl-demo-content-rules)
|
|
(quote (post-likes p1 N)))
|
|
(list {:N 3}))
|
|
|
|
;; ── Permissions ─────────────────────────────────────────
|
|
(dl-demo-test-set! "direct group access"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((member alice editors)
|
|
(allowed editors blog)))
|
|
dl-demo-perm-rules)
|
|
(quote (can-access X blog)))
|
|
(list {:X (quote alice)}))
|
|
|
|
(dl-demo-test-set! "subgroup access"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((member bob writers)
|
|
(subgroup writers editors)
|
|
(allowed editors blog)))
|
|
dl-demo-perm-rules)
|
|
(quote (can-access X blog)))
|
|
(list {:X (quote bob)}))
|
|
|
|
(dl-demo-test-set! "transitive subgroup"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((member carol drafters)
|
|
(subgroup drafters writers)
|
|
(subgroup writers editors)
|
|
(allowed editors blog)))
|
|
dl-demo-perm-rules)
|
|
(quote (can-access X blog)))
|
|
(list {:X (quote carol)}))
|
|
|
|
;; ── Cooking posts (canonical Phase 10 example) ─────────
|
|
(dl-demo-test-set! "cooking posts by network"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((follows me alice) (follows alice bob) (follows alice carol)
|
|
(authored alice p1) (authored bob p2)
|
|
(authored carol p3) (authored carol p4)
|
|
(tagged p1 travel) (tagged p2 cooking)
|
|
(tagged p3 cooking) (tagged p4 books)))
|
|
dl-demo-cooking-rules)
|
|
(quote (cooking-post-by-network me P)))
|
|
(list {:P (quote p2)} {:P (quote p3)}))
|
|
|
|
(dl-demo-test-set! "cooking — direct follow only"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((follows me bob)
|
|
(authored bob p1) (authored bob p2)
|
|
(tagged p1 cooking) (tagged p2 books)))
|
|
dl-demo-cooking-rules)
|
|
(quote (cooking-post-by-network me P)))
|
|
(list {:P (quote p1)}))
|
|
|
|
(dl-demo-test-set! "cooking — none in network"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((follows me bob)
|
|
(authored bob p1) (tagged p1 books)))
|
|
dl-demo-cooking-rules)
|
|
(quote (cooking-post-by-network me P)))
|
|
(list))
|
|
|
|
;; ── Tag co-occurrence ──────────────────────────────────
|
|
(dl-demo-test-set! "cotagged posts"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((tagged p1 cooking) (tagged p1 vegetarian)
|
|
(tagged p2 cooking) (tagged p2 quick)
|
|
(tagged p3 vegetarian)))
|
|
dl-demo-tag-cooccur-rules)
|
|
(quote (cotagged P cooking vegetarian)))
|
|
(list {:P (quote p1)}))
|
|
|
|
(dl-demo-test-set! "tag pair count"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote
|
|
((tagged p1 cooking) (tagged p1 vegetarian)
|
|
(tagged p2 cooking) (tagged p2 quick)
|
|
(tagged p3 cooking) (tagged p3 vegetarian)))
|
|
dl-demo-tag-cooccur-rules)
|
|
(quote (tag-pair-count cooking vegetarian N)))
|
|
(list {:N 2}))
|
|
|
|
;; ── Shortest path on a weighted DAG ──────────────────
|
|
(dl-demo-test-set! "shortest a→d via DAG"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((edge a b 5) (edge b c 3) (edge a c 10) (edge c d 2)))
|
|
dl-demo-shortest-path-rules)
|
|
(quote (shortest a d W)))
|
|
(list {:W 10}))
|
|
|
|
(dl-demo-test-set! "shortest a→c picks 2-hop"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((edge a b 5) (edge b c 3) (edge a c 10)))
|
|
dl-demo-shortest-path-rules)
|
|
(quote (shortest a c W)))
|
|
(list {:W 8}))
|
|
|
|
(dl-demo-test-set! "shortest unreachable empty"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((edge a b 5) (edge b c 3)))
|
|
dl-demo-shortest-path-rules)
|
|
(quote (shortest a d W)))
|
|
(list))
|
|
|
|
(dl-demo-test-set! "no access without grant"
|
|
(dl-query
|
|
(dl-demo-make
|
|
(quote ((member dave outsiders) (allowed editors blog)))
|
|
dl-demo-perm-rules)
|
|
(quote (can-access X blog)))
|
|
(list)))))
|
|
|
|
(define
|
|
dl-demo-tests-run!
|
|
(fn
|
|
()
|
|
(do
|
|
(set! dl-demo-pass 0)
|
|
(set! dl-demo-fail 0)
|
|
(set! dl-demo-failures (list))
|
|
(dl-demo-run-all!)
|
|
{:passed dl-demo-pass
|
|
:failed dl-demo-fail
|
|
:total (+ dl-demo-pass dl-demo-fail)
|
|
:failures dl-demo-failures})))
|