;; ========================================================================== ;; test-router.sx — Tests for client-side route matching ;; ;; Requires: test-framework.sx loaded first. ;; Modules tested: router.sx ;; ;; No additional platform functions needed — router.sx is pure. ;; ========================================================================== ;; -------------------------------------------------------------------------- ;; split-path-segments ;; -------------------------------------------------------------------------- (defsuite "split-path-segments" (deftest "root path" (assert-equal (list) (split-path-segments "/"))) (deftest "single segment" (assert-equal (list "docs") (split-path-segments "/docs"))) (deftest "multiple segments" (assert-equal (list "docs" "hello") (split-path-segments "/docs/hello"))) (deftest "trailing slash stripped" (assert-equal (list "docs") (split-path-segments "/docs/"))) (deftest "deep path" (assert-equal (list "a" "b" "c" "d") (split-path-segments "/a/b/c/d")))) ;; -------------------------------------------------------------------------- ;; parse-route-pattern ;; -------------------------------------------------------------------------- (defsuite "parse-route-pattern" (deftest "static pattern" (let ((segs (parse-route-pattern "/docs/intro"))) (assert-length 2 segs) (assert-equal "literal" (get (first segs) "type")) (assert-equal "docs" (get (first segs) "value")) (assert-equal "literal" (get (nth segs 1) "type")) (assert-equal "intro" (get (nth segs 1) "value")))) (deftest "pattern with param" (let ((segs (parse-route-pattern "/docs/"))) (assert-length 2 segs) (assert-equal "literal" (get (first segs) "type")) (assert-equal "docs" (get (first segs) "value")) (assert-equal "param" (get (nth segs 1) "type")) (assert-equal "slug" (get (nth segs 1) "value")))) (deftest "multiple params" (let ((segs (parse-route-pattern "/users//posts/"))) (assert-length 4 segs) (assert-equal "param" (get (nth segs 1) "type")) (assert-equal "uid" (get (nth segs 1) "value")) (assert-equal "param" (get (nth segs 3) "type")) (assert-equal "pid" (get (nth segs 3) "value")))) (deftest "root pattern" (assert-equal (list) (parse-route-pattern "/")))) ;; -------------------------------------------------------------------------- ;; match-route ;; -------------------------------------------------------------------------- (defsuite "match-route" (deftest "exact match returns empty params" (let ((result (match-route "/docs/intro" "/docs/intro"))) (assert-true (not (nil? result))) (assert-length 0 (keys result)))) (deftest "param match extracts value" (let ((result (match-route "/docs/hello" "/docs/"))) (assert-true (not (nil? result))) (assert-equal "hello" (get result "slug")))) (deftest "no match returns nil" (assert-nil (match-route "/docs/hello" "/essays/")) (assert-nil (match-route "/docs" "/docs/"))) (deftest "segment count mismatch returns nil" (assert-nil (match-route "/a/b/c" "/a/")) (assert-nil (match-route "/a" "/a/b"))) (deftest "root matches root" (let ((result (match-route "/" "/"))) (assert-true (not (nil? result))))) (deftest "multiple params extracted" (let ((result (match-route "/users/42/posts/99" "/users//posts/"))) (assert-true (not (nil? result))) (assert-equal "42" (get result "uid")) (assert-equal "99" (get result "pid"))))) ;; -------------------------------------------------------------------------- ;; find-matching-route ;; -------------------------------------------------------------------------- (defsuite "find-matching-route" (deftest "finds first matching route" (let ((routes (list {:pattern "/docs/" :parsed (parse-route-pattern "/docs/") :name "docs-index"} {:pattern "/docs/" :parsed (parse-route-pattern "/docs/") :name "docs-page"}))) (let ((result (find-matching-route "/docs/hello" routes))) (assert-true (not (nil? result))) (assert-equal "docs-page" (get result "name")) (assert-equal "hello" (get (get result "params") "slug"))))) (deftest "returns nil for no match" (let ((routes (list {:pattern "/docs/" :parsed (parse-route-pattern "/docs/") :name "docs-page"}))) (assert-nil (find-matching-route "/essays/hello" routes)))) (deftest "matches exact routes before param routes" (let ((routes (list {:pattern "/docs/" :parsed (parse-route-pattern "/docs/") :name "docs-index"} {:pattern "/docs/" :parsed (parse-route-pattern "/docs/") :name "docs-page"}))) ;; /docs/ should match docs-index, not docs-page (let ((result (find-matching-route "/docs/" routes))) (assert-true (not (nil? result))) (assert-equal "docs-index" (get result "name"))))) (deftest "propagates stream flag from route" (let ((routes (list {:pattern "/demo/streaming" :parsed (parse-route-pattern "/demo/streaming") :name "streaming-demo" :stream true :has-data true}))) (let ((result (find-matching-route "/demo/streaming" routes))) (assert-true (not (nil? result))) (assert-equal true (get result "stream")) (assert-equal true (get result "has-data"))))) (deftest "non-streaming route has no stream flag" (let ((routes (list {:pattern "/about" :parsed (parse-route-pattern "/about") :name "about" :has-data false}))) (let ((result (find-matching-route "/about" routes))) (assert-true (not (nil? result))) (assert-nil (get result "stream"))))) (deftest "streaming route with params propagates all properties" (let ((routes (list {:pattern "/stream/" :parsed (parse-route-pattern "/stream/") :name "stream-page" :stream true :has-data true :content "expr"}))) (let ((result (find-matching-route "/stream/fast" routes))) (assert-true (not (nil? result))) (assert-equal true (get result "stream")) (assert-equal "fast" (get (get result "params") "id")) (assert-equal "expr" (get result "content"))))))