tests: standardize on embedded tests (migrate all from tests/ to lisp/ sources)
Some checks failed
Deploy (Gitea) / deploy (push) Failing after 2s

This commit is contained in:
2026-05-05 10:29:09 -04:00
parent 817d1c5fec
commit 9dd0ed2f78
17 changed files with 269 additions and 38 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,5 @@ test_input.txt
# Generated artifacts (source of truth is .org)
/skills/*.lisp
/tests/*.lisp
/tmp/*.lisp
*.fasl

View File

@@ -100,10 +100,7 @@
(string= n "system-model-router")
(string= n "system-model-embedding")
(string= n "system-model-explorer")
(string= n "gateway-tui")
(string= n "gateway-tui-model")
(string= n "gateway-tui-view")
(string= n "gateway-tui-main"))))
(string= n "gateway-tui"))))
all-files))
(adj (make-hash-table :test 'equal))
(name-to-file (make-hash-table :test 'equal))
@@ -284,3 +281,29 @@
(load-skill-from-lisp file)
(load-skill-from-org file)))
(log-message "LOADER: Boot Complete."))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))
(defpackage :passepartout-boot-tests
(:use :cl :fiveam :passepartout)
(:export #:boot-suite))
(in-package :passepartout-boot-tests)
(def-suite boot-suite :description "Verification of the Skill Engine loader")
(in-suite boot-suite)
(test test-topological-sort-basic
(let ((tmp-dir "/tmp/passepartout-boot-test/"))
(uiop:ensure-all-directories-exist (list tmp-dir))
(with-open-file (out (merge-pathnames "org-skill-a.org" tmp-dir) :direction :output :if-exists :supersede)
(format out "#+DEPENDS_ON: skill-b-id~%"))
(with-open-file (out (merge-pathnames "org-skill-b.org" tmp-dir) :direction :output :if-exists :supersede)
(format out ":PROPERTIES:~%:ID: skill-b-id~%:END:~%"))
(unwind-protect
(let ((sorted (passepartout::skill-topological-sort tmp-dir)))
(let ((pos-a (position "org-skill-a" sorted :key #'pathname-name :test #'string-equal))
(pos-b (position "org-skill-b" sorted :key #'pathname-name :test #'string-equal)))
(is (< pos-b pos-a))))
(uiop:delete-directory-tree (uiop:ensure-directory-pathname tmp-dir) :validate t))))

View File

@@ -163,3 +163,24 @@
(refresh scr)
(sleep 0.03))
(disconnect-daemon))))
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))
(defpackage :passepartout-tui-tests
(:use :cl :passepartout)
(:export #:tui-suite))
(in-package :passepartout-tui-tests)
(fiveam:def-suite tui-suite :description "Verification of the TUI parsing and styling logic")
(fiveam:in-suite tui-suite)
(fiveam:test test-tui-connection-drop
"Tier 2 Chaos: Verify that handle-return degrades gracefully when the daemon connection is lost."
(let ((passepartout.gateway-tui::*incoming-msgs* nil)
(passepartout.gateway-tui::*input-buffer* (make-array 5 :element-type 'character :initial-contents "hello" :fill-pointer 5 :adjustable t))
(mock-stream (make-string-output-stream)))
(close mock-stream)
(passepartout.gateway-tui::handle-return mock-stream)
(fiveam:is (member "ERROR: Connection to daemon lost." passepartout.gateway-tui::*incoming-msgs* :test #'string=))))

View File

@@ -1,3 +1,5 @@
(in-package :passepartout)
(defun lisp-structural-check (code)
"Checks if parentheses are balanced and the code is readable."
(handler-case
@@ -146,3 +148,78 @@
(defskill :passepartout-programming-lisp
:priority 400
:trigger (lambda (ctx) (declare (ignore ctx)) nil))
(defpackage :passepartout-utils-lisp-tests
(:use :cl :fiveam :passepartout)
(:export #:utils-lisp-suite))
(in-package :passepartout-utils-lisp-tests)
(def-suite utils-lisp-suite
:description "Tests for the Lisp Validator structural, syntactic, and semantic gates")
(in-suite utils-lisp-suite)
(test structural-balanced
(is (eq t (passepartout:lisp-structural-check "(+ 1 2)"))))
(test structural-unbalanced-open
(multiple-value-bind (ok reason) (passepartout:lisp-structural-check "(+ 1 2")
(is (null ok))
(is (search "Reader Error" reason))))
(test structural-unbalanced-close
(multiple-value-bind (ok reason) (passepartout:lisp-structural-check "+ 1 2)")
(is (null ok))
(is (search "Reader Error" reason))))
(test syntactic-valid
(is (eq t (passepartout:lisp-syntactic-check "(+ 1 2)"))))
(test semantic-safe
(is (eq t (passepartout:lisp-semantic-check "(+ 1 2)"))))
(test semantic-blocked-eval
(multiple-value-bind (ok reason) (passepartout:lisp-semantic-check "(eval '(+ 1 2))")
(is (null ok))
(is (search "Unsafe" reason))))
(test unified-success
(let ((result (passepartout:lisp-validate "(+ 1 2)" :strict t)))
(is (eq (getf result :status) :success))))
(test unified-failure
(let ((result (passepartout:lisp-validate "(+ 1 2" :strict nil)))
(is (eq (getf result :status) :error))))
(test eval-basic
(let ((result (passepartout:lisp-eval "(+ 1 2)")))
(is (eq (getf result :status) :success))
(is (string= (getf result :result) "3"))))
(test structural-extract
(let* ((code "(defun hello () (print \"hi\")) (defun bye () (print \"bye\"))")
(extracted (passepartout:lisp-extract code "hello")))
(is (not (null extracted)))
(let ((form (read-from-string extracted)))
(is (eq (car form) 'DEFUN))
(is (eq (second form) 'HELLO)))))
(test list-definitions
(let ((code "(defun foo () t) (defmacro bar () nil) (defparameter *baz* 10)"))
(let ((names (passepartout:lisp-list-definitions code)))
(is (member 'FOO names))
(is (member 'BAR names))
(is (member '*BAZ* names)))))
(test structural-inject
(let* ((code "(defun my-fun (x) (print x))")
(injected (passepartout:lisp-inject code "my-fun" "(finish-output)")))
(let ((form (read-from-string injected)))
(is (equal (last form) '((FINISH-OUTPUT)))))))
(test structural-slurp
(let* ((code "(defun work () (step-1))")
(slurped (passepartout:lisp-slurp code "work" "(step-2)")))
(let ((form (read-from-string slurped)))
(is (equal (last form) '((STEP-2)))))))

View File

@@ -1,3 +1,5 @@
(in-package :passepartout)
(defun org-filetags-extract (content)
"Extracts the list of tags from a #+FILETAGS: line."
(let ((lines (uiop:split-string content :separator '(#\Newline))))
@@ -242,3 +244,41 @@ AST format: (:TYPE :HEADLINE :properties (:ID ... :TITLE ... :TAGS (...))
(defskill :passepartout-programming-org
:priority 100
:trigger (lambda (ctx) (declare (ignore ctx)) nil))
(eval-when (:compile-toplevel :load-toplevel :execute)
(ignore-errors (ql:quickload :fiveam :silent t)))
(defpackage :passepartout-utils-org-tests
(:use :cl :fiveam :passepartout)
(:export #:utils-org-suite))
(in-package :passepartout-utils-org-tests)
(def-suite utils-org-suite
:description "Tests for Utils Org skill.")
(in-suite utils-org-suite)
(test id-generation
(let ((id1 (org-id-generate))
(id2 (org-id-generate)))
(is (plusp (length id1)))
(is (not (string= id1 id2)))))
(test id-format
(let ((formatted (org-id-format "abc12345")))
(is (search "id:" formatted))))
(test property-setter
(let ((ast (list :type :HEADLINE
:properties (list :ID "id:test123" :TITLE "Test")
:contents nil)))
(org-property-set ast "id:test123" :STATUS "ACTIVE")
(is (string= (getf (getf ast :properties) :STATUS) "ACTIVE"))))
(test todo-setter
(let ((ast (list :type :HEADLINE
:properties (list :ID "id:todo001" :TITLE "Task")
:contents nil)))
(org-todo-set ast "id:todo001" "DONE")
(is (string= (getf (getf ast :properties) :TODO) "DONE"))))

View File

@@ -172,6 +172,34 @@
(uiop:quit 0)
(uiop:quit 1)))
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))
(defpackage :passepartout-diagnostics-tests
(:use :cl :fiveam :passepartout)
(:export #:diagnostics-suite))
(in-package :passepartout-diagnostics-tests)
(def-suite diagnostics-suite :description "Verification of the System Diagnostics logic")
(in-suite diagnostics-suite)
(test test-diagnostics-dependency-fail
"Verify that missing binaries are correctly identified as failures."
(let ((passepartout::*diagnostics-binaries* '("non-existent-binary-123")))
(is (null (diagnostics-dependencies-check)))))
(test test-diagnostics-env-fail
"Verify that an invalid MEMEX_DIR triggers a critical failure."
(let ((old-m (uiop:getenv "MEMEX_DIR"))
(old-d (uiop:getenv "PASSEPARTOUT_DATA_DIR")))
(unwind-protect
(progn
(setf (uiop:getenv "MEMEX_DIR") "/non/existent/path/999")
(is (null (diagnostics-env-check))))
(setf (uiop:getenv "MEMEX_DIR") (or old-m ""))
(setf (uiop:getenv "PASSEPARTOUT_DATA_DIR") (or old-d "")))))
(defskill :passepartout-system-diagnostics
:priority 100
:trigger (lambda (ctx) (eq (getf (getf ctx :payload) :sensor) :heartbeat))

View File

@@ -67,3 +67,43 @@
(:chat . "Casual conversation, Q&A, creative writing. Prefer balanced quality, low latency.\nRecommend: Llama 3.3 70B (strong generalist) or Gemma 4 31B (thinking mode).")
(:plan . "Strategic planning, architecture design, complex multi-step reasoning.\nRecommend: Owl Alpha (free, tool use, 1M ctx) or Hermes 3 405B (strongest free reasoning).")
(:background . "Heartbeat summaries, delegation responses, tool output filtering. Must be small + fast.\nRecommend: Llama 3.2 3B (131K ctx, fast) or LFM 2.5 1.2B (edge-ready).")))
;; REPL-verified: 2026-05-04
(eval-when (:compile-toplevel :load-toplevel :execute)
(ignore-errors (ql:quickload :fiveam :silent t)))
(defpackage :passepartout-system-model-explorer-tests
(:use :cl :passepartout)
(:export #:model-explorer-suite))
(in-package :passepartout-system-model-explorer-tests)
(fiveam:def-suite model-explorer-suite :description "Tests for the model explorer skill")
(fiveam:in-suite model-explorer-suite)
(fiveam:test model-explorer-recommend-slots
"model-explorer-recommend should return models for all standard slots"
(dolist (slot '(:code :chat :plan :background))
(let ((recs (passepartout::model-explorer-recommend slot)))
(fiveam:is (listp recs))
(fiveam:is (>= (length recs) 1)))))
(fiveam:test model-explorer-recommend-format
"Each recommendation should have :id and :name"
(dolist (rec (passepartout::model-explorer-recommend :chat))
(fiveam:is (getf rec :id))
(fiveam:is (getf rec :name))))
(fiveam:test model-explorer-recommend-unknown-slot
"Unknown slot should return fallback"
(let ((recs (passepartout::model-explorer-recommend :unknown)))
(fiveam:is (listp recs))
(fiveam:is (>= (length recs) 1))))
(fiveam:test model-explorer-fetch-openrouter-count
"OpenRouter API should return at least 300 models"
(let ((models (passepartout::model-explorer-fetch :openrouter)))
(if models
(fiveam:is (>= (length models) 300))
(fiveam:skip "API unreachable"))))

View File

@@ -116,3 +116,20 @@ If API-KEY is nil, reads from environment."
(defskill :passepartout-system-model-provider
:priority 50
:trigger (lambda (ctx) (declare (ignore ctx)) nil))
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))
(defpackage :passepartout-llm-gateway-tests
(:use :cl :passepartout)
(:export #:llm-gateway-suite))
(in-package :passepartout-llm-gateway-tests)
(fiveam:def-suite llm-gateway-suite :description "Tests for the LLM provider backend")
(fiveam:in-suite llm-gateway-suite)
(fiveam:test test-provider-rejects-bad-keyword
"Verify that provider-openai-request returns :error for an unregistered provider."
(let ((result (provider-openai-request "hello" "test" :provider :not-a-real-provider)))
(fiveam:is (eq (getf result :status) :error))))

View File

@@ -40,21 +40,7 @@ Components are loaded in sequence (~:serial t~): package first (defines the publ
** Test System
The test system loads on top of ~opencortex~ and adds FiveAM (the test framework). Each test file is tangled from a ~:tangle ../tests/...~ block in the parent org file.
Note: not every harness or skill file has a corresponding test file. Tests exist only for the parts of the system where deterministic verification is most critical — the pipeline stages, the loader, the memory Merkle tree, and the peripheral vision model.
#+begin_src lisp
(defsystem :passepartout/tests
:depends-on (:passepartout :fiveam)
:components ((:file "tests/programming-org-tests")
(:file "tests/programming-lisp-tests")
(:file "tests/boot-sequence-tests")
(:file "tests/model-explorer-tests")
(:file "tests/diagnostics-tests")
(:file "tests/tui-tests")
(:file "tests/llm-gateway-tests")))
#+end_src
Tests are embedded directly in each module's source file — see the `* Test Suite` section at the end of each `.org` file. No separate test system is needed.
** TUI System

View File

@@ -411,7 +411,7 @@ files live after tangling. The org source files live in ~org/~.
* Test Suite
Verifies that the topological sorter correctly orders skills by their ~#+DEPENDS_ON:~ declarations.
#+begin_src lisp :tangle ../tests/boot-sequence-tests.lisp
#+begin_src lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))

View File

@@ -185,7 +185,7 @@ Event handlers + daemon I/O + main loop.
#+end_src
* Test Suite
#+begin_src lisp :tangle ../tests/tui-tests.lisp
#+begin_src lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))

View File

@@ -17,6 +17,11 @@ The skill has four layers:
* Implementation
** Package Context
#+begin_src lisp
(in-package :passepartout)
#+end_src
** Structural Validation
;; REPL-VERIFIED: 2026-05-03T13:00:00
#+begin_src lisp
@@ -215,7 +220,7 @@ The skill has four layers:
* Test Suite
Tests for the Lisp Validator structural, syntactic, and semantic gates.
#+begin_src lisp :tangle ../tests/programming-lisp-tests.lisp
#+begin_src lisp
(defpackage :passepartout-utils-lisp-tests
(:use :cl :fiveam :passepartout)
(:export #:utils-lisp-suite))

View File

@@ -8,6 +8,11 @@ Structural manipulation tools for Org-mode files. This skill handles reading, wr
* Implementation
** Package Context
#+begin_src lisp
(in-package :passepartout)
#+end_src
** Reading Files (with Privacy Filter)
;; REPL-VERIFIED: 2026-05-03T13:00:00
#+begin_src lisp
@@ -331,7 +336,7 @@ AST format: (:TYPE :HEADLINE :properties (:ID ... :TITLE ... :TAGS (...))
* Test Suite
Verification of the structural manipulation for Org-mode files and their AST representation.
#+begin_src lisp :tangle ../tests/programming-org-tests.lisp
#+begin_src lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
(ignore-errors (ql:quickload :fiveam :silent t)))

View File

@@ -243,7 +243,7 @@ The doctor checks all supported LLM providers and detects local Ollama instances
* Phase D: Verification (Testing)
#+begin_src lisp :tangle ../tests/diagnostics-tests.lisp
#+begin_src lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))

View File

@@ -104,7 +104,7 @@ Recommended models are curated per task slot — code generation needs different
* Tests
#+begin_src lisp :tangle ../tests/model-explorer-tests.lisp
#+begin_src lisp
;; REPL-verified: 2026-05-04
(eval-when (:compile-toplevel :load-toplevel :execute)
(ignore-errors (ql:quickload :fiveam :silent t)))

View File

@@ -163,7 +163,7 @@ If API-KEY is nil, reads from environment."
#+end_src
* Test Suite
#+begin_src lisp :tangle ../tests/llm-gateway-tests.lisp
#+begin_src lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :fiveam :silent t))

View File

@@ -16,16 +16,6 @@
(:file "lisp/core-loop-act")
(:file "lisp/core-loop")))
(defsystem :passepartout/tests
:depends-on (:passepartout :fiveam)
:components ((:file "tests/programming-org-tests")
(:file "tests/programming-lisp-tests")
(:file "tests/boot-sequence-tests")
(:file "tests/model-explorer-tests")
(:file "tests/diagnostics-tests")
(:file "tests/tui-tests")
(:file "tests/llm-gateway-tests")))
(defsystem :passepartout/tui
:depends-on (:passepartout :croatoan :usocket :bordeaux-threads)
:components ((:file "lisp/gateway-tui")))