Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s
20 end-to-end tests via pl-query-* API: permission system, graph reachability, quicksort, dynamic KB, fibonacci. Total 571/571. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
173 lines
5.0 KiB
Plaintext
173 lines
5.0 KiB
Plaintext
;; lib/prolog/tests/integration.sx — end-to-end integration tests via pl-query-* API
|
|
;;
|
|
;; Tests the full source→parse→load→solve pipeline with real programs.
|
|
;; Covers: permission system, graph reachability, quicksort, fibonacci, dynamic KB.
|
|
|
|
(define pl-int-test-count 0)
|
|
(define pl-int-test-pass 0)
|
|
(define pl-int-test-fail 0)
|
|
(define pl-int-test-failures (list))
|
|
|
|
(define
|
|
pl-int-test!
|
|
(fn
|
|
(name got expected)
|
|
(begin
|
|
(set! pl-int-test-count (+ pl-int-test-count 1))
|
|
(if
|
|
(= got expected)
|
|
(set! pl-int-test-pass (+ pl-int-test-pass 1))
|
|
(begin
|
|
(set! pl-int-test-fail (+ pl-int-test-fail 1))
|
|
(append!
|
|
pl-int-test-failures
|
|
(str name "\n expected: " expected "\n got: " got)))))))
|
|
|
|
;; ── Permission system ──
|
|
;; role/2 + permission/2 facts, allowed/2 rule
|
|
|
|
(define
|
|
pl-int-perm-src
|
|
"role(alice, admin). role(bob, editor). role(charlie, viewer). permission(admin, read). permission(admin, write). permission(admin, delete). permission(editor, read). permission(editor, write). permission(viewer, read). allowed(U, A) :- role(U, R), permission(R, A).")
|
|
|
|
(define pl-int-perm-db (pl-load pl-int-perm-src))
|
|
|
|
(pl-int-test!
|
|
"alice can read"
|
|
(len (pl-query-all pl-int-perm-db "allowed(alice, read)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"alice can delete"
|
|
(len (pl-query-all pl-int-perm-db "allowed(alice, delete)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"charlie cannot write"
|
|
(len (pl-query-all pl-int-perm-db "allowed(charlie, write)"))
|
|
0)
|
|
|
|
(pl-int-test!
|
|
"alice has 3 permissions"
|
|
(len (pl-query-all pl-int-perm-db "allowed(alice, A)"))
|
|
3)
|
|
|
|
(pl-int-test!
|
|
"only one user can delete"
|
|
(len (pl-query-all pl-int-perm-db "allowed(U, delete)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"the deleter is alice"
|
|
(dict-get (first (pl-query-all pl-int-perm-db "allowed(U, delete)")) "U")
|
|
"alice")
|
|
|
|
;; ── Graph reachability ──
|
|
;; Directed edges; path/2 transitive closure via two clauses
|
|
|
|
(define
|
|
pl-int-graph-src
|
|
"edge(a, b). edge(b, c). edge(c, d). edge(b, d). path(X, Y) :- edge(X, Y). path(X, Y) :- edge(X, Z), path(Z, Y).")
|
|
|
|
(define pl-int-graph-db (pl-load pl-int-graph-src))
|
|
|
|
(pl-int-test!
|
|
"direct edge a→b is a path"
|
|
(len (pl-query-all pl-int-graph-db "path(a, b)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"transitive path a→c"
|
|
(len (pl-query-all pl-int-graph-db "path(a, c)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"no path d→a (no back-edges)"
|
|
(len (pl-query-all pl-int-graph-db "path(d, a)"))
|
|
0)
|
|
|
|
(pl-int-test!
|
|
"4 derivations from a (b,c,d via two routes to d)"
|
|
(len (pl-query-all pl-int-graph-db "path(a, Y)"))
|
|
4)
|
|
|
|
;; ── Quicksort ──
|
|
;; Partition-and-recurse; uses its own append/3 to avoid DB pollution
|
|
|
|
(define
|
|
pl-int-qs-src
|
|
"partition(_, [], [], []). partition(Piv, [H|T], [H|Less], Greater) :- H =< Piv, !, partition(Piv, T, Less, Greater). partition(Piv, [H|T], Less, [H|Greater]) :- partition(Piv, T, Less, Greater). append([], L, L). append([H|T], L, [H|R]) :- append(T, L, R). quicksort([], []). quicksort([H|T], Sorted) :- partition(H, T, Less, Greater), quicksort(Less, SL), quicksort(Greater, SG), append(SL, [H|SG], Sorted).")
|
|
|
|
(define pl-int-qs-db (pl-load pl-int-qs-src))
|
|
|
|
(pl-int-test!
|
|
"quicksort([]) = [] (ground check)"
|
|
(len (pl-query-all pl-int-qs-db "quicksort([], [])"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"quicksort([3,1,2]) = [1,2,3] (ground check)"
|
|
(len (pl-query-all pl-int-qs-db "quicksort([3,1,2], [1,2,3])"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"quicksort([5,3,1,4,2]) = [1,2,3,4,5] (ground check)"
|
|
(len (pl-query-all pl-int-qs-db "quicksort([5,3,1,4,2], [1,2,3,4,5])"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"quicksort([3,1,2], [3,1,2]) fails — unsorted order rejected"
|
|
(len (pl-query-all pl-int-qs-db "quicksort([3,1,2], [3,1,2])"))
|
|
0)
|
|
|
|
;; ── Fibonacci ──
|
|
;; Naive recursive; ground checks avoid list-format uncertainty
|
|
|
|
(define
|
|
pl-int-fib-src
|
|
"fib(0, 0). fib(1, 1). fib(N, F) :- N > 1, N1 is N - 1, N2 is N - 2, fib(N1, F1), fib(N2, F2), F is F1 + F2.")
|
|
|
|
(define pl-int-fib-db (pl-load pl-int-fib-src))
|
|
|
|
(pl-int-test!
|
|
"fib(0, 0) succeeds"
|
|
(len (pl-query-all pl-int-fib-db "fib(0, 0)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"fib(5, 5) succeeds"
|
|
(len (pl-query-all pl-int-fib-db "fib(5, 5)"))
|
|
1)
|
|
|
|
(pl-int-test!
|
|
"fib(7, 13) succeeds"
|
|
(len (pl-query-all pl-int-fib-db "fib(7, 13)"))
|
|
1)
|
|
|
|
;; ── Dynamic knowledge base ──
|
|
;; Assert and retract facts; the DB dict is mutable so mutations persist
|
|
|
|
(define pl-int-dyn-src "color(red). color(green). color(blue).")
|
|
(define pl-int-dyn-db (pl-load pl-int-dyn-src))
|
|
|
|
(pl-int-test!
|
|
"initial KB: 3 colors"
|
|
(len (pl-query-all pl-int-dyn-db "color(X)"))
|
|
3)
|
|
|
|
(pl-int-test!
|
|
"after assert(color(yellow)): 4 colors"
|
|
(begin
|
|
(pl-query-all pl-int-dyn-db "assert(color(yellow))")
|
|
(len (pl-query-all pl-int-dyn-db "color(X)")))
|
|
4)
|
|
|
|
(pl-int-test!
|
|
"after retract(color(red)): back to 3 colors"
|
|
(begin
|
|
(pl-query-all pl-int-dyn-db "retract(color(red))")
|
|
(len (pl-query-all pl-int-dyn-db "color(X)")))
|
|
3)
|
|
|
|
(define pl-integration-tests-run! (fn () {:failed pl-int-test-fail :passed pl-int-test-pass :total pl-int-test-count :failures pl-int-test-failures}))
|