test(dashboard): lowercase header keys + the client stamp (1 red → 0) — and 88 dashboard failures CI never runs (#2732)
## The fix
`app/api/client.ts` builds request headers through a `Headers` object —
which lower-cases every key — and stamps `x-fusion-client: dashboard-ui`
on every dashboard-originated request so the server can attribute the
caller (the FN-8609 delete-attribution surface).
This test asserted `"Content-Type"`, and `expect.objectContaining`
compares keys **case-sensitively**, so it failed on casing alone.
Rather than only lower-casing the key, it now pins the **client stamp**
too — that header is the point of the feature and nothing else in this
file covered it.
| Check | Result |
|---|---|
| `plugin-setup-api.test.ts` | 1 failed → **3 passed** |
| client stamp neutered in `client.ts` | **1 failed** / 2 passed —
load-bearing |
| `pnpm lint`, dashboard app `tsc` | clean |
## Method correction — I nearly filed 40 phantom failures
Measuring this package with a raw `vitest run` reports **~40 failing
files**. That number is worthless: `@fusion/dashboard`'s own `test`
script is `node scripts/run-quality-tests.mjs`, which runs the quality
projects as **separate invocations** with per-group heap sizes and
exclusions. Running every project in one process fails en masse for
reasons unrelated to the code.
Correct command — `pnpm --filter @fusion/dashboard test` — gives **9
failing files / 88 failing tests**, exit 1.
## The finding: those 88 failures are never executed in CI
I first concluded "CI shows no dashboard failures, so these are
local-only." **That was wrong, and the reason matters.**
CI does schedule the dashboard quality groups — they are distributed
across all four shards as individual `test:quality:*` invocations. Shard
2's plan, for example:
```
[ci-test-shard] shard 2/4: @fusion/core [2/2], @fusion/dashboard run test:quality:app:components-b,
@fusion/dashboard run test:quality:app:backfill-1, ...
```
But only two invocations ever get a `(watchdog budget 1500s)` start
line: the plugins group and `@fusion/core [2/2]`. `components-b` never
starts, because the shard aborts on the first failing package —
`ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL`, present in shards 1, 2 and 4.
**So the dashboard quality groups are not passing — they are unrun**,
behind a package that fails first. Consequences:
1. **Fixing core/engine/CLI will unmask 88 dashboard failures.** My
merged PRs move shards 1/2/4 toward green; as each earlier package stops
failing, these groups begin executing for the first time. Expect the
shard counts to *rise* before they fall — that is progress, not
regression.
2. **Reading shard conclusions is misleading.** A shard says "core
failed"; it does not say "and everything scheduled after core never
ran."
Where the 88 live (all files currently unowned):
| File | Failures | Lane / shard |
|---|---:|---|
| `TaskDetailModal.inline-editing-and-integrations` | 50 |
`components-b` / shard 2 |
| `auto-merge-toggle-blank.mobile-integration` | 13 | `components-a` /
shard 3 |
| `auto-merge-toggle-blank.mobile` | 8 | `components-a` / shard 3 |
| `TaskDetailModal` | 6 | `components-b` / shard 2 |
| `SecretsView` | 4 | `components-b` / shard 2 |
| `WorkflowNodeEditor` | 3 | `components-b` / shard 2 |
| `TaskCard` + `TaskCard.badge-wrap` | 3 | `components-b` / shard 2 |
| `board-mobile` | 1 | `components-a` / shard 3 |
Dominant symptoms: `Unable to fire a "click" event - please provide a
DOM element` (29), `Unable to find an accessible element with the role
"checkbox" and name "Auto-merge"` (13), `expected null to be truthy` (7)
— consistent with a small number of shared render/affordance causes
rather than 88 independent bugs, but I have not isolated them.
**I am not starting that repair in this PR.** It is a 9-file, 88-test
area needing per-cluster diagnosis, and bundling it behind a one-line
header fix would produce exactly the shallow work this program keeps
rejecting. Filed here with the correct measurement command, the
lane/shard mapping, and the reason CI has been silent about it.
This commit is contained in:
@@ -17,10 +17,24 @@ describe("plugin setup API helpers", () => {
|
||||
const result = await fetchPluginSetupStatus("plugin/id");
|
||||
|
||||
expect(result).toEqual({ hasSetup: true, status: "installed" });
|
||||
/*
|
||||
FNXC:TaskDeleteAttribution 2026-07-30-06:20:
|
||||
Header keys are LOWERCASE and now include the client stamp. `app/api/client.ts` builds request
|
||||
headers through a `Headers` object (which lower-cases every key) and sets
|
||||
`x-fusion-client: dashboard-ui` on every dashboard-originated request so the server can attribute
|
||||
the caller — the FN-8609 delete-attribution surface.
|
||||
|
||||
This asserted `"Content-Type"`, and `objectContaining` compares keys case-SENSITIVELY, so it broke
|
||||
on the casing alone. Rather than just lower-casing the key, this now pins the client stamp too:
|
||||
that header is the point of the feature, and nothing else in this file covered it.
|
||||
*/
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"/api/plugins/plugin%2Fid/setup-status",
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({ "Content-Type": "application/json" }),
|
||||
headers: expect.objectContaining({
|
||||
"content-type": "application/json",
|
||||
"x-fusion-client": "dashboard-ui",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user