Testing
Both tools read the same YAML this site is built from, so neither can drift away from the contract.
The mock sender
Section titled “The mock sender”Fires valid packets at SuperCollider. This is what unblocks you completely: the entire SuperCollider side can be built and tested before the engine exists.
node scripts/mock.mjs --listnode scripts/mock.mjs --mode synthIt runs the startup sequence from the lifecycle page
— handshake, asset root, the reference wall’s /wall/config bundle, the mix —
then /mode/load with that mode’s init parameters in order, the mode’s opening
state, /mode/start, and then touches, the mode’s streams at their declared
rates, and its events. Every value it sends is inside the declared range and of the
declared type, because it is generated from the declaration.
# One specific message, once.node scripts/mock.mjs --send /wall/hold/touch
# Every message in the protocol, once each — a smoke test for your handlers.node scripts/mock.mjs --all
# Somewhere other than localhost.node scripts/mock.mjs --mode chase --host 192.168.1.40 --port 57120
# Stop after 30 seconds.node scripts/mock.mjs --mode arcade --seconds 30The monitor
Section titled “The monitor”The reverse, and the tool that will actually catch integration bugs. It listens and logs every packet, checking each one against the schema.
node scripts/monitor.mjs✓ /wall/hold/touch iff [42, 0.81, 12.334]✗ /wall/hold/touch iif [42, 1, 12] arg 1 "velocity" expects f, got i✗ /wall/hold/touch iff [60, 0.5, 1.0] arg 0 "holdId" = 60 is outside 0..59 on this wall✗ /mode/chase/hit iiii [2, 12, 1, 3] arg 0 "zone" = 2 is outside 0..1 (zoneCount 2)✗ /mode/synth/notes iiiii [0, 2, 4, 5, 7] "keys" carries 5 values, at most 4 allowed (steps)✗ /wall/hold/hover not in protocol v0.2.0It learns from the traffic: hold indices are checked against the last
/wall/config it saw, zones against the zoneCount of the last /mode/load,
and the synth pattern against that load’s steps. Until those arrive it uses
the reference installation, and says so.
Hand this to the engine developer on day one. It turns “it doesn’t work” into a specific line with a specific argument, and it enforces the float policy automatically — which is the single most valuable thing it does, because a wrong-typed float is otherwise completely silent.
# Watch one group only.node scripts/monitor.mjs --filter /wall
# Errors only, for leaving open in a corner during a long session.node scripts/monitor.mjs --errors-only
# Listen where the engine listens, to watch SuperCollider's replies instead.node scripts/monitor.mjs --port 9000Run the monitor on a different port from SuperCollider and point the engine at
it, or run it on 57120 while SuperCollider is stopped. Two processes cannot
bind the same UDP port.
Validating the schema itself
Section titled “Validating the schema itself”npm run validateStructure, then the semantics a JSON Schema cannot express: type tag strings
that disagree with their argument list, duplicate addresses, examples that do
not conform to their own declared types, hold, zone or speaker indices without
a ref, zone counts the wall cannot hold, an inconsistent reference wall,
streamed arrays that would exceed the packet budget, ackedBy pointing at a
message that does not exist.
It runs in CI on every push, and it is worth wiring to a pre-commit hook:
echo 'npm run validate --silent' > .git/hooks/pre-commitchmod +x .git/hooks/pre-commitChecking for breaking changes
Section titled “Checking for breaking changes”npm run diff -- --against-tagCompares the working tree against the last git tag and classifies every change as major, minor or patch. CI fails a pull request that lands a breaking change without a major version bump, which is the mechanism that keeps the version number honest — and the changelog on this site is generated from the same output, so it is always accurate.
A sensible test loop
Section titled “A sensible test loop”- Terminal one:
node scripts/monitor.mjs --port 9000— watch what SuperCollider sends back. - Terminal two: SuperCollider, with the generated responders loaded.
- Terminal three:
node scripts/mock.mjs --mode synth.
You now have the whole protocol exercised in both directions with no engine and no wall, and anything that goes wrong is named on a specific line.