;; ========================================================================== ;; test-deps.sx — Tests for component dependency analysis (deps.sx) ;; ;; Requires: test-framework.sx loaded first. ;; Platform functions: scan-refs, transitive-deps, components-needed, ;; component-pure?, scan-io-refs, transitive-io-refs, ;; scan-components-from-source, test-env ;; (loaded from bootstrapped output by test runners) ;; ========================================================================== ;; -------------------------------------------------------------------------- ;; Test component definitions — these exist in the test env for dep analysis ;; -------------------------------------------------------------------------- (defcomp ~dep-leaf () (span "leaf")) (defcomp ~dep-branch () (div (~dep-leaf))) (defcomp ~dep-trunk () (div (~dep-branch) (~dep-leaf))) (defcomp ~dep-conditional (&key show?) (if show? (~dep-leaf) (~dep-branch))) (defcomp ~dep-nested-cond (&key mode) (cond (= mode "a") (~dep-leaf) (= mode "b") (~dep-branch) :else (~dep-trunk))) (defcomp ~dep-island () (div "no deps")) ;; -------------------------------------------------------------------------- ;; 1. scan-refs — finds component references in AST nodes ;; -------------------------------------------------------------------------- (defsuite "scan-refs" (deftest "empty for string literal" (assert-equal (list) (scan-refs "hello"))) (deftest "empty for number" (assert-equal (list) (scan-refs 42))) (deftest "finds component symbol" (let ((refs (scan-refs (quote (~dep-leaf))))) (assert-contains "~dep-leaf" refs))) (deftest "finds in nested list" (let ((refs (scan-refs (quote (div (span (~dep-leaf))))))) (assert-contains "~dep-leaf" refs))) (deftest "finds multiple refs" (let ((refs (scan-refs (quote (div (~dep-leaf) (~dep-branch)))))) (assert-contains "~dep-leaf" refs) (assert-contains "~dep-branch" refs))) (deftest "deduplicates" (let ((refs (scan-refs (quote (div (~dep-leaf) (~dep-leaf)))))) (assert-equal 1 (len refs)))) (deftest "walks if branches" (let ((refs (scan-refs (quote (if true (~dep-leaf) (~dep-branch)))))) (assert-contains "~dep-leaf" refs) (assert-contains "~dep-branch" refs))) (deftest "walks cond branches" (let ((refs (scan-refs (quote (cond (= x 1) (~dep-leaf) :else (~dep-trunk)))))) (assert-contains "~dep-leaf" refs) (assert-contains "~dep-trunk" refs))) (deftest "ignores non-component symbols" (let ((refs (scan-refs (quote (div class "foo"))))) (assert-equal 0 (len refs))))) ;; -------------------------------------------------------------------------- ;; 2. scan-components-from-source — regex-based source string scanning ;; -------------------------------------------------------------------------- (defsuite "scan-components-from-source" (deftest "finds single component" (let ((refs (scan-components-from-source "(~dep-leaf)"))) (assert-contains "~dep-leaf" refs))) (deftest "finds multiple components" (let ((refs (scan-components-from-source "(div (~dep-leaf) (~dep-branch))"))) (assert-contains "~dep-leaf" refs) (assert-contains "~dep-branch" refs))) (deftest "no false positives on plain text" (let ((refs (scan-components-from-source "(div \"hello world\")"))) (assert-equal 0 (len refs)))) (deftest "handles hyphenated names" (let ((refs (scan-components-from-source "(~my-component :key val)"))) (assert-contains "~my-component" refs)))) ;; -------------------------------------------------------------------------- ;; 3. transitive-deps — transitive dependency closure ;; -------------------------------------------------------------------------- (defsuite "transitive-deps" (deftest "leaf has no deps" (let ((deps (transitive-deps "~dep-leaf" (test-env)))) (assert-equal 0 (len deps)))) (deftest "direct dependency" (let ((deps (transitive-deps "~dep-branch" (test-env)))) (assert-contains "~dep-leaf" deps))) (deftest "transitive closure" (let ((deps (transitive-deps "~dep-trunk" (test-env)))) (assert-contains "~dep-branch" deps) (assert-contains "~dep-leaf" deps))) (deftest "excludes self" (let ((deps (transitive-deps "~dep-trunk" (test-env)))) (assert-false (contains? deps "~dep-trunk")))) (deftest "walks conditional branches" (let ((deps (transitive-deps "~dep-conditional" (test-env)))) (assert-contains "~dep-leaf" deps) (assert-contains "~dep-branch" deps))) (deftest "walks all cond branches" (let ((deps (transitive-deps "~dep-nested-cond" (test-env)))) (assert-contains "~dep-leaf" deps) (assert-contains "~dep-branch" deps) (assert-contains "~dep-trunk" deps))) (deftest "island has no deps" (let ((deps (transitive-deps "~dep-island" (test-env)))) (assert-equal 0 (len deps)))) (deftest "accepts name without tilde" (let ((deps (transitive-deps "dep-branch" (test-env)))) (assert-contains "~dep-leaf" deps)))) ;; -------------------------------------------------------------------------- ;; 4. components-needed — page bundle computation ;; -------------------------------------------------------------------------- (defsuite "components-needed" (deftest "finds direct and transitive" (let ((needed (components-needed "(~dep-trunk)" (test-env)))) (assert-contains "~dep-trunk" needed) (assert-contains "~dep-branch" needed) (assert-contains "~dep-leaf" needed))) (deftest "deduplicates" (let ((needed (components-needed "(div (~dep-leaf) (~dep-leaf))" (test-env)))) ;; ~dep-leaf should appear only once (assert-true (contains? needed "~dep-leaf")))) (deftest "handles leaf page" (let ((needed (components-needed "(~dep-island)" (test-env)))) (assert-contains "~dep-island" needed) (assert-equal 1 (len needed)))) (deftest "handles multiple top-level components" (let ((needed (components-needed "(div (~dep-leaf) (~dep-island))" (test-env)))) (assert-contains "~dep-leaf" needed) (assert-contains "~dep-island" needed)))) ;; -------------------------------------------------------------------------- ;; 5. IO detection — scan-io-refs, component-pure? ;; -------------------------------------------------------------------------- ;; Define components that reference "io" functions for testing (defcomp ~dep-pure () (div (~dep-leaf) "static")) (defcomp ~dep-io () (div (fetch-data "/api"))) (defcomp ~dep-io-indirect () (div (~dep-io))) (defsuite "scan-io-refs" (deftest "no IO in pure AST" (let ((refs (scan-io-refs (quote (div "hello" (span "world"))) (list "fetch-data")))) (assert-equal 0 (len refs)))) (deftest "finds IO reference" (let ((refs (scan-io-refs (quote (div (fetch-data "/api"))) (list "fetch-data")))) (assert-contains "fetch-data" refs))) (deftest "multiple IO refs" (let ((refs (scan-io-refs (quote (div (fetch-data "/a") (query-db "x"))) (list "fetch-data" "query-db")))) (assert-contains "fetch-data" refs) (assert-contains "query-db" refs))) (deftest "ignores non-IO symbols" (let ((refs (scan-io-refs (quote (div (map str items))) (list "fetch-data")))) (assert-equal 0 (len refs))))) (defsuite "component-pure?" (deftest "pure component is pure" (assert-true (component-pure? "~dep-pure" (test-env) (list "fetch-data")))) (deftest "IO component is not pure" (assert-false (component-pure? "~dep-io" (test-env) (list "fetch-data")))) (deftest "indirect IO is not pure" (assert-false (component-pure? "~dep-io-indirect" (test-env) (list "fetch-data")))) (deftest "leaf component is pure" (assert-true (component-pure? "~dep-leaf" (test-env) (list "fetch-data")))))