safsaf/tests/CLAUDE.md
Christopher Baines 5b0e6397dc
All checks were successful
/ test (push) Successful in 9s
Initial commit
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.
2026-04-13 14:24:19 +03:00

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 define inside test bodies 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.