Split out of #2, which closed with everything else it named addressed:
tools.py 29% → 100%, cli.py 34% → 82%, tunnel.py 41% → 96%,
plugin_api.py 64% → 83%, total 67% → 86%.
hookdeck/dashboard/dist/index.js is the one item that was deprioritised and
is still at nothing. It is 194 lines of hand-written IIFE with no tests of
any kind, and it is invisible in the 86% — that figure is Python only, so
the number reads better than the coverage is.
It ships in the wheel, so this is not dead weight in the repo: it is code
installed on every gateway.
Why it is worth more than "it's just UI"
The original reasoning for deprioritising was that its failure mode is visible
rather than silent. That holds for rendering. It does not hold for the two
buttons, which mutate a live Hookdeck project:
onClick: function () {
act("/connections/" + c.id + (c.paused ? "/resume" : "/pause"));
}
Both the button's label and the endpoint it posts to are derived from the same
c.paused flag. Invert that expression and the UI reads correctly while doing
the opposite — a button labelled "Pause" that resumes. Nothing in the repo
would catch it, and the operator finds out from traffic, not from the screen.
The same shape applies to retry: act("/events/" + e.id + "/retry") is a
redelivery, and this plugin exists to avoid running the agent twice for one
event.
Worth covering, roughly in this order
- The pause/resume toggle agrees with its own label.
paused: true must
render "Resume" and post to /resume; paused: false the mirror. One test,
and it is the one that stops a mislabelled button pausing production traffic.
busy disables every action button. load, both retry buttons and the
pause/resume button all take disabled: busy. Losing that on the retry path
turns a double-click into a double redelivery.
act() clears busy when the POST fails. The .catch sets it back;
without that the panel is permanently disabled after one failed retry and the
only fix is a page reload.
- The SDK guard returns quietly.
if (!SDK || !PLUGINS || !PLUGINS.register) return; is what stops this throwing into the host's bundle loader on a
dashboard whose SDK predates what it uses — i.e. an older Hermes with a newer
plugin, which is the normal upgrade order. A test that loads the bundle with
an empty window and asserts nothing throws is cheap.
- Error rendering handles a non-
Error rejection.
String((e && e.message) || e) is written for both shapes; only one of them
is likely to have been tried.
- Empty states.
failed.length === 0 and conns.length === 0 have their
own branches, and an empty queue is the common case.
How, given there is no JS toolchain here
There is no package.json, no test runner, and no build step — deliberately,
which is why dist/index.js is committed rather than generated. Adding a
toolchain to test one file is most of the cost of this issue, so it is worth
choosing before starting.
Preferred: no dependencies. The bundle takes React, its hooks and its
components from window.__HERMES_PLUGIN_SDK__, and talks to the server only
through SDK.fetchJSON. Everything it touches is injected. So it can be loaded
under node:vm with a hand-written fake SDK whose createElement returns plain
objects and whose fetchJSON records calls — then assert on the recorded tree
and the recorded requests. No DOM, no React, no npm install. The fiddly part is
faking useState/useEffect/useCallback well enough for a component this
shape; for one component with three pieces of state that is a small amount of
code, and it keeps the "no build step" property the file was written for.
Alternative: vitest + jsdom + real React as devDependencies. More faithful,
and the hooks come for free. The cost is a package.json, a lockfile, a Node
version to pin, and a second ecosystem in a repo that currently has one.
Either way it needs a job in ci.yml; today nothing runs against this file at
all.
Not covered by this issue
plugin_api.py (the Python half serving /api/plugins/hookdeck) is at 83% and
was addressed under #2. This is only the browser half.
Split out of #2, which closed with everything else it named addressed:
tools.py29% → 100%,cli.py34% → 82%,tunnel.py41% → 96%,plugin_api.py64% → 83%, total 67% → 86%.hookdeck/dashboard/dist/index.jsis the one item that was deprioritised andis still at nothing. It is 194 lines of hand-written IIFE with no tests of
any kind, and it is invisible in the 86% — that figure is Python only, so
the number reads better than the coverage is.
It ships in the wheel, so this is not dead weight in the repo: it is code
installed on every gateway.
Why it is worth more than "it's just UI"
The original reasoning for deprioritising was that its failure mode is visible
rather than silent. That holds for rendering. It does not hold for the two
buttons, which mutate a live Hookdeck project:
Both the button's label and the endpoint it posts to are derived from the same
c.pausedflag. Invert that expression and the UI reads correctly while doingthe opposite — a button labelled "Pause" that resumes. Nothing in the repo
would catch it, and the operator finds out from traffic, not from the screen.
The same shape applies to retry:
act("/events/" + e.id + "/retry")is aredelivery, and this plugin exists to avoid running the agent twice for one
event.
Worth covering, roughly in this order
paused: truemustrender "Resume" and post to
/resume;paused: falsethe mirror. One test,and it is the one that stops a mislabelled button pausing production traffic.
busydisables every action button.load, both retry buttons and thepause/resume button all take
disabled: busy. Losing that on the retry pathturns a double-click into a double redelivery.
act()clearsbusywhen the POST fails. The.catchsets it back;without that the panel is permanently disabled after one failed retry and the
only fix is a page reload.
if (!SDK || !PLUGINS || !PLUGINS.register) return;is what stops this throwing into the host's bundle loader on adashboard whose SDK predates what it uses — i.e. an older Hermes with a newer
plugin, which is the normal upgrade order. A test that loads the bundle with
an empty
windowand asserts nothing throws is cheap.Errorrejection.String((e && e.message) || e)is written for both shapes; only one of themis likely to have been tried.
failed.length === 0andconns.length === 0have theirown branches, and an empty queue is the common case.
How, given there is no JS toolchain here
There is no
package.json, no test runner, and no build step — deliberately,which is why
dist/index.jsis committed rather than generated. Adding atoolchain to test one file is most of the cost of this issue, so it is worth
choosing before starting.
Preferred: no dependencies. The bundle takes React, its hooks and its
components from
window.__HERMES_PLUGIN_SDK__, and talks to the server onlythrough
SDK.fetchJSON. Everything it touches is injected. So it can be loadedunder
node:vmwith a hand-written fake SDK whosecreateElementreturns plainobjects and whose
fetchJSONrecords calls — then assert on the recorded treeand the recorded requests. No DOM, no React, no npm install. The fiddly part is
faking
useState/useEffect/useCallbackwell enough for a component thisshape; for one component with three pieces of state that is a small amount of
code, and it keeps the "no build step" property the file was written for.
Alternative: vitest + jsdom + real React as devDependencies. More faithful,
and the hooks come for free. The cost is a
package.json, a lockfile, a Nodeversion to pin, and a second ecosystem in a repo that currently has one.
Either way it needs a job in
ci.yml; today nothing runs against this file atall.
Not covered by this issue
plugin_api.py(the Python half serving/api/plugins/hookdeck) is at 83% andwas addressed under #2. This is only the browser half.