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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 | # tests
`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
ships to a browser) into a throwaway `target/check-app`, so the gate costs
nothing but a compile and writes nothing into `dist/`.
This link exists because of a real defect: the scaffold vendors
`lib/mailbox.cljs` from chat, that file requires `promesa.core`,
`banca.fx` and `banca.wire` — and the scaffold shipped none of them. The
`:app` build therefore did not compile from day zero, and **the whole suite
was green the entire time**, because step 3 builds only `:testlib`. A gate
that never compiles what ships is a gate that passes by not looking. If you
add a namespace to a build that the tests do not exercise, add it here too.
3. `shadow-cljs release testlib` — the `:testlib` ESM build. Everything below
asserts against `test-dist/testlib.js`, never against `src/`: a vector that
reads the source pins the source rather than what ships.
4. `TZ=UTC node --test test/*.test.mjs`.
Note the unquoted glob: `node --test 'test/*.test.mjs'` and `node --test test/`
both misbehave on some node versions. Point it at files, unquoted.
## The suites
**`source-hygiene.test.mjs`** — a source lint over `src/banca`, for the CLJS
traps that each shipped a bug in this suite: a local or a `defn` shadowing a core
macro/var, 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, the JS façade freeze, and the raw-HTML
surface as an exact budget. Every check carries a probe beside it that runs the
detector over code which *does* offend. Its header records which of chat's
checks were kept, adapted and dropped on the way into this repo, and why.
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.
**`protocol.test.mjs`** — BANK/1's op union: the key order of every builder
(against a table written by `independent.mjs`'s own hand-written builders, so the
two must agree with each other rather than with themselves), and a 44-row
validator reject table with the non-vacuity probe that every unmutated sample
validates.
**`order.test.mjs`** — the wallet-kit seam. banca transcribes wallet-kit's
preimage assembly and shape grammar because that kit is unreleased and cannot be
sha-pinned; this checks the transcription against wallet-kit's **own published**
golden vector and against a by-hand canonical-JSON template.
**`receipts.test.mjs`** — the double-signed portable receipt, including the
honest asymmetry: a banker can ack with a wrong-but-well-formed `bsig`, the fold
still calls the payment final, and the exported receipt does not verify. Both
halves are asserted.
**`fold.test.mjs`** — the settlement fold, 28 hand-computed scenarios plus the
order-independence property over three fixed insertion permutations of every one
of them.
## Methodology, and it is not optional
**A vector generated from the implementation it pins proves nothing.**
`test/vectors/independent.mjs` imports nothing from `test-dist/`: canonical JSON
is re-implemented from its spec sentence, the two preimages are written out as
literal templates a reader can check character by character, Ed25519 comes from
`@noble/curves` and SHA-256 from `node:crypto`. Every fold expectation in
`fold.json` is **hand-computed** and written as a literal beside an `input_desc`
sentence a reader can check by adding up four numbers.
That is not ceremony. The by-hand receipt template was wrong on its first draft —
it dropped `po.sig` from the preimage — and wallet-kit's published string is what
settled which side was right. A fixture recorded from the implementation would
have been green and wrong.
**Probe every gate by deliberately breaking what it guards.** An unprobed gate is
a claim, not a check. `dev/docs/CLJS.md` — "Test-harness notes", "Crypto and wire
compatibility", "Gates: write them, then try to break them" — is the canon.
## Regenerating the vectors
```
node test/vectors/build-vectors.mjs
```
Run it **deliberately**, never as part of `npm test`. It rewrites
`order.json`, `receipts.json` and `fold.json` from `independent.mjs` and the
hand-written expectations inside the generator. If a scenario's expectation is
wrong, the fixture is the claim and the implementation is the thing on trial —
change the expectation only when you have worked out on paper why it was wrong.
`test/vectors/facade.json` has **no** generator on purpose. Changing the façade
is a decision, not a refresh: the failing `deepEqual` prints the whole diff, and
a change you meant to make is applied by editing the file to match, reading every
moved line as you go.
## Still to add
- **`test/orbit-address.test.mjs`**, if you care that bank 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`).
- **An i18n placeholder test** once there is a UI: every `(t "key" {…})` call
site must supply exactly the placeholders its English string uses. 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.
|