All checks were successful
/ test (push) Successful in 9s
Safsaf is a Guile web framework, written using Claude Code running Claude Opus 4.6, based off of the Guix Data Service, Nar Herder and Guix Build Coordinator codebases.
2.1 KiB
2.1 KiB
Testing
Framework
Tests use a minimal SRFI-269 implementation in (tests support). Three
primitives — is, test, suite — build first-class test entities and
deliver them to a pluggable runner. Definition is separated from execution.
Running tests
All tests (via Automake):
make check
Single file:
./pre-inst-env guile tests/test-router.scm
Writing tests
(use-modules (tests support)
(safsaf router)) ; module under test
(define-suite router-tests
(suite "route construction"
(test "creates route with method and pattern"
(let ((r (route 'GET '("users") identity)))
(is (route? r))
(is (eq? 'GET (route-method r))))))
(suite "matching"
(test "exact path match"
...)))
(run-tests router-tests)
Key points:
(is expr)— assert expr is truthy. Returns the value on success.(is (pred arg ...))— predicate form; on failure shows evaluated args.(test "desc" body ...)— a single test case with one or more assertions.(suite "desc" body ...)— group tests and nested suites.(define-suite name body ...)— bind a suite-thunk to a variable.(run-tests thunk)— run with the simple runner, print summary, exit.- Tests should be self-contained: don't depend on ordering or side effects from other tests.
- Use
defineinsidetestbodies for local setup.
Synthetic requests
Many tests need Guile <request> objects without a real HTTP server.
Build them with build-request from (web request):
(use-modules (web request) (web uri))
(define* (make-request method path #:optional (headers '()))
(build-request (build-uri 'http #:host "localhost" #:path path)
#:method method
#:headers headers))
Handler signature is (request body-port) → (values response body). When
calling handlers or wrapped handlers in tests, pass #f as the body-port:
(let ((resp body (wrapped (make-request 'GET "/" '()) #f)))
(is (= 200 (response-code resp))))
For handlers that read current-route-params, parameterize it directly.