Skip to content

test(ws): add a harness for the WS controller macro paths - #182

Merged
birme merged 2 commits into
Eyevinn:mainfrom
wagenet:wagenet/ws-macro-pip-harness
Sep 3, 2026
Merged

birme merged 2 commits into
Eyevinn:mainfrom
wagenet:wagenet/ws-macro-pip-harness

Conversation

@wagenet

@wagenet wagenet commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Root of the PiP work. Three PRs build on this one:

Each lands green: every case that needs a behaviour change to pass ships with the commit that makes it pass, so no PR in the set puts a red suite on main. Because these are fork PRs they all target main and each carries this commit in its own diff until it merges — so this one wants reviewing first, on its own.

Why

The WS controller has no test coverage. activation.test.ts and macros.test.ts both mock ../ws/controller.js out entirely, so there is nowhere to assert what a macro action sends to Strom or broadcasts to subscribers — which is most of what the controller does.

What this adds

handleMessage driven directly, with the real StromClient pointed at a throwaway HTTP server that records every request. The assertions therefore cover the URL, the verb, and the body the server actually puts on the wire. A hand-written client stand-in would have been less work, but it can drift from StromClient without anything failing — vi.mock returns an untyped object, so neither tsc nor the tests would notice a changed signature. Going through the real client also exercises the bits a stand-in cannot get wrong because it never gets them right either, like selectPreview being a PUT while transition is a POST.

CouchDB is mocked via vi.mock('../db/index.js'), as elsewhere in this suite.

PiP state is established through real inbound messages (SELECT_PVW_PIP, TAKE) rather than by reaching into the module-level maps, so each case exercises the same state machine the server runs in production.

One case here: a macro CUT with no PiP anywhere. It pins the behaviour the PiP work must leave untouched — no PIP_STATE, no pip-addressed preview select, from_input and to_input as before. It passes on main today, which is what makes it a control rather than a vacuous assertion.

Production change

One export:

/** Exported for tests: drives one inbound message against a production. */
export async function handleMessage(

handleMessage was module-private and the plugin exposes only a websocket route, so there was no seam. The alternative — a fastify + websocket fixture — would drive the connect handler too and test considerably more than these paths. Happy to switch if you would rather have that; this felt like the smaller commitment to make on your behalf. Nothing else in controller.ts moves.

Testing

  • npx tsc --noEmit clean.
  • npx vitest run — the new file passes 1/1. The suite's 27 pre-existing failures in activation.test.ts and macros.test.ts are unchanged and present on main too (all 500s from the HTTP layer).

The WS controller has no test coverage — `activation.test.ts` and
`macros.test.ts` both mock `../ws/controller.js` out entirely — so there is
nowhere to assert what a macro action sends to Strom or broadcasts to
subscribers.

Adds that harness: `handleMessage` driven directly with CouchDB and
StromClient mocked, asserting the Strom call sequence and the broadcast
payloads. State is established through real inbound messages rather than by
reaching into the module-level maps, so each case exercises the same state
machine the server runs in production.

One case here, a macro CUT with no PiP anywhere, which pins the behaviour the
PiP work must leave untouched. Cases that require a behaviour change to pass
belong with the commits that make them pass.

`handleMessage` is exported for this and is the only production change; the
plugin exposes just a websocket route, so there was no other seam.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uhnqt88WrXHGV7VF4D65x3

@birme birme left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified by actually running it, not just reading it: cloned the branch, merged onto current main (clean, no conflicts), npx tsc --noEmit clean, npx vitest run — 106/106 passing across 10 files (up from 105/105 across 9 on main), including the new harness's 1 test. Note: the PR description's '27 pre-existing failures in activation.test.ts/macros.test.ts, present on main too' is now stale — current main is fully green (105/105), presumably fixed by other work merged since this branch was cut on Aug 27; not a defect in this PR.

Production diff is exactly what the description says: one line, exporting the already-private handleMessage for the test to call directly — no logic change. clearPipState and setTally, which the new test imports, both already exist and are already exported on main (not fabricated). The harness itself is well-built: it points the real StromClient at a throwaway HTTP server rather than a hand-rolled mock, so a signature drift in StromClient would actually be caught (an untyped vi.mock stand-in wouldn't). PiP state is set up via real inbound messages (SELECT_PVW_PIP, TAKE) rather than reaching into module internals, so the test exercises the same state machine production runs. The one seeded case (macro CUT with no PiP, asserting no PIP_STATE broadcast and unchanged from/to inputs) is a legitimate control — it passes today and pins exactly the invariant the dependent PRs (#156/#179/#180) need to not break.

APPROVE. Solid foundation for the PiP stack — happy to review #156 next now that this harness is in place.

@birme
birme merged commit 1471c80 into Eyevinn:main Sep 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants