bursa / client / test / README.md
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# tests — what the scaffold ships, and what you have to add

`npm test` runs, in order:

1. `clojure -M:cljfmt check src` — the formatting gate, config in
   `client/.cljfmt.edn`. It is the FIRST link of the `test` script rather than a
   `pretest` hook on purpose: an npm lifecycle hook is skipped outright under
   `ignore-scripts=true`, which is a gate that passes by not running.
2. `npm run check:app` — compiles the `:app` build, the one that actually
   reaches a browser, into a throwaway `target/check-app`. It costs a compile
   and writes nothing into `dist/`.

   This gate is here because of a real defect. The scaffold vendors
   `lib/mailbox.cljs` from chat; that file requires `promesa.core`, `<name>.fx`
   and `<name>.wire`, and for a long while the template shipped none of them —
   so every rooms scaffold's `:app` build did not compile, and every scaffold's
   `npm test` was green anyway, because nothing in the suite ever built `:app`.
   **A gate that never compiles what ships is a gate that passes by not
   looking.** Keep this link first among the compiling ones: it is the cheapest
   check that the app is still buildable at all.
3. `node --test test/*.test.mjs` — today that is exactly one file,
   `source-hygiene.test.mjs`, the template's own app-agnostic copy of the lint
   whose canon is chat's (chat's file also carries chat-only façade checks a
   fresh app cannot pass, so it is not vendored verbatim). Note the
   **unquoted** glob: `'test/*.test.mjs'` is not expanded by the shell, and node
   does not expand it on every version either, so the quoted form can run ZERO
   tests and still exit 0 — the same failure mode as the paragraph above.

All three are real gates and all three pass on a fresh scaffold. None is a test
of **your** app, and the scaffold does not pretend otherwise: there are no
golden vectors here, because there is nothing yet to pin.

## source-hygiene.test.mjs

A source lint over `src/`, for the CLJS traps that each shipped a bug in this
suite — a local or a `defn` shadowing a core macro/var (core PREDICATES
included), a `#js {}` or `js-obj` literal with pairs (key order silently
becomes hash order at nine pairs), a sibling form dedented below the one above
it (the paren-depth error that took chat and board down in production), an
interop call whose target is a function literal (always that same bug), and
the raw-HTML gate: an allowlist-with-budgets over `innerHTML`-family sinks and
`esc` definitions that ships EMPTY, because the demo builds DOM nodes
(`app/dom.cljs`) and never markup. Every check carries a "would have caught
the bug that motivated it" case beside it.

When you add a `:testlib` build, also add a façade freeze —
`helpers/facade.mjs` is already vendored, and chat's copy of this file shows
the canon shape (exact-match against a recorded vector, a non-vacuousness
probe, by-name `:exports` resolution).

It scans **this repo's** namespaces only. The shared rooms core
(`ardegazu.rooms.*`) comes off the classpath from `ardegazu-rooms-kit` and is
linted there, by that repo's copy of this file. Between the two, every `.cljs`
this app compiles is covered — so if you ever move code the other way, ask what
was scanning it yesterday and whether that still reaches.

## What to add, roughly in this order

1. **A `:testlib` build** in `client/shadow-cljs.edn`: `:target :esm`,
   `:output-dir "test-dist"`, `:optimizations :simple`, `:source-map false`,
   `:js-options {:js-provider :import}`, and an `:exports` map naming the pure
   logic you want to drive from node. chat's `shadow-cljs.edn` is the canon
   shape. Then put `shadow-cljs release testlib` back into the `test` script,
   between the cljfmt check and `node --test`.
2. **Golden vectors** under `test/vectors/*.json` for every op builder and every
   canonicalised object — byte-exact, because key order is the wire. Assert them
   with `js/JSON.stringify` output, never `pr-str`.
3. **Order-independence pairs**: feed the same entry set to your projector in
   two arrival orders and assert the two `JSON.stringify`s are equal. One line
   per pair, and it catches fold divergences instantly.
4. **An i18n placeholder test** asserting that every `(t "key" {…})` call site
   supplies exactly the placeholders its English string uses — no missing, no
   extra. board's `source-hygiene.test.mjs` has the canon copy; board shipped a
   raw `{cur}` on every returning user's screen for want of it.
5. **`test/orbit-address.test.mjs`** if you care that your room addresses are
   deterministic. It boots the real stack, which needs the libdatachannel native
   addon — that is what `scripts/ensure-native.mjs` is for; re-add it as the
   `pretest` step (and note the point above about `ignore-scripts`).

`dev/docs/CLJS.md` — "Test-harness notes", "Crypto and wire compatibility" and
"Gates: write them, then try to break them" — is the canon for all of it. The
one rule worth repeating here: **probe every gate by deliberately breaking what
it guards.** An unprobed gate is a claim, not a check.

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/bursa.git