From e16c08aa9f868ce8b529a227c716a14fe27e525a Mon Sep 17 00:00:00 2001 From: "shaggy (mosaic-dev box)" Date: Sun, 9 Aug 2026 20:20:30 -0500 Subject: [PATCH] test(web): align jsdom abort signals with Node --- apps/web/src/test/setup.spec.ts | 14 ++++++++ apps/web/src/test/setup.ts | 23 ++++++++++++ apps/web/vitest.config.ts | 2 ++ docs/scratchpads/webui-p2-data-auth.md | 50 ++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 apps/web/src/test/setup.spec.ts create mode 100644 apps/web/src/test/setup.ts diff --git a/apps/web/src/test/setup.spec.ts b/apps/web/src/test/setup.spec.ts new file mode 100644 index 00000000..7d370530 --- /dev/null +++ b/apps/web/src/test/setup.spec.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from 'vitest'; + +describe('Vitest abort-controller realm', () => { + it('provides a global signal accepted by Node native Request', () => { + const controller = new AbortController(); + const request = new Request('https://mosaic.invalid/navigation', { + signal: controller.signal, + }); + + expect(request.signal).toBeInstanceOf(AbortSignal); + controller.abort(); + expect(request.signal.aborted).toBe(true); + }); +}); diff --git a/apps/web/src/test/setup.ts b/apps/web/src/test/setup.ts new file mode 100644 index 00000000..751a20d7 --- /dev/null +++ b/apps/web/src/test/setup.ts @@ -0,0 +1,23 @@ +import { transferableAbortController } from 'node:util'; + +// jsdom installs realm-local abort constructors while Node's undici Request +// remains native. React Router passes a global AbortSignal to Request, so both +// constructors must come from Node's native realm during tests. +const nativeController = transferableAbortController(); +const nativeAbortController = nativeController.constructor; +const nativeAbortSignal = nativeController.signal.constructor; + +for (const target of [globalThis, window]) { + Object.defineProperties(target, { + AbortController: { + configurable: true, + writable: true, + value: nativeAbortController, + }, + AbortSignal: { + configurable: true, + writable: true, + value: nativeAbortSignal, + }, + }); +} diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts index 3166181f..effe0be1 100644 --- a/apps/web/vitest.config.ts +++ b/apps/web/vitest.config.ts @@ -14,6 +14,8 @@ export default defineConfig({ test: { globals: true, environment: 'jsdom', + setupFiles: ['./src/test/setup.ts'], + isolate: true, exclude: ['e2e/**', 'node_modules/**'], }, }); diff --git a/docs/scratchpads/webui-p2-data-auth.md b/docs/scratchpads/webui-p2-data-auth.md index 5fec0d29..a1355926 100644 --- a/docs/scratchpads/webui-p2-data-auth.md +++ b/docs/scratchpads/webui-p2-data-auth.md @@ -110,3 +110,53 @@ Run fresh after remediation from the locations required by the brief: - `.mosaic/orchestrator/session.lock` is modified by the active harness and must remain unstaged. - P2 changes shared `src/lib/` modules consumed by both Vite and Next, so the Next build is a required compatibility gate. + +## Remediation 1 — independent Node 26 verification failure + +**Correction received:** 2026-08-09 + +The orchestrator rejected the P2 verification claim after an independent run under Node 26.4.0 produced 2 failed redirect tests and 2 unhandled errors. React Router passed jsdom's realm-local `AbortSignal` to Node 26's native undici `Request`, which rejects non-native signals. Production guard behavior is correct and must not change. + +### Remediation constraints + +- Preserve both redirect assertions and all `guards.tsx` behavior; no skips, weakening, or test deletion. +- Add the smallest Vitest environment repair so global `AbortController` / `AbortSignal` are constructors accepted by native undici `Request`. +- Run all six required gates, commit named files locally without pushing, leave `.mosaic/orchestrator/session.lock` untouched, then run `apps/web: pnpm test` as the final worktree action. +- Completion requires zero failed tests, zero test errors, and zero unhandled rejections. + +### Remediation plan + +1. Reproduce under Node 26.4.0 if an ephemeral matching runtime is available. +2. Add a Vitest `setupFiles` module deriving Node-native abort constructors from `node:util` and register it in `apps/web/vitest.config.ts`. +3. Add a direct regression assertion that a global controller's signal is accepted by native `Request`, while retaining the existing redirect behavior tests unchanged. +4. Run focused Node 22 and Node 26 tests, independent review, all required gates, and local commit(s). +5. After every edit/commit/status check is complete, run `pnpm test` from `apps/web` as the last command. + +### Remediation implementation and evidence + +- Reproduced under ephemeral Node `v26.4.0`: `Test Files 1 failed (1)`, `Tests 2 failed | 4 passed (6)`, `Errors 2 errors`, with the exact undici `AbortSignal` realm rejection from the remediation brief. +- Added `apps/web/src/test/setup.ts`, registered through `vitest.config.ts#setupFiles`. It derives Node-native abort constructors from the built-in `node:util.transferableAbortController()` and aligns both `globalThis` and jsdom `window`; it does not replace `Request`, `Response`, or `fetch`. +- Explicitly pinned Vitest `isolate: true` so the test-only global constructors cannot leak between test-file environments. +- Added `src/test/setup.spec.ts`, which proves a global controller signal is accepted by Node's native `Request` and that abort propagation remains functional. +- Existing `guards.spec.tsx` and production `guards.tsx` remain unchanged. +- Focused Node 26.4.0 remediation run: `Test Files 2 passed (2)` and `Tests 7 passed (7)`, with zero errors/unhandled rejections. +- Preliminary full Node 26.4.0 run: `Test Files 9 passed (9)` and `Tests 27 passed (27)`, with zero errors/unhandled rejections before the direct setup regression was added. +- No dependency was added; `node:util` is a Node built-in. + +### Remediation independent review + +- First code review: approve, 0 blockers, 0 should-fix; suggested a direct Request regression and version-neutral comment. +- First security/integrity review: low risk; suggested pinning test isolation explicitly. +- All suggestions were applied. +- Fresh code re-review: approve, 0 blockers, 0 should-fix, no findings. +- Fresh security/integrity re-review: low risk, 0 findings. + +### Remediation pre-commit gate evidence + +- Node 26.4.0 `pnpm test`: `Test Files 10 passed (10)`; `Tests 28 passed (28)`; zero errors and zero unhandled rejections. +- `pnpm typecheck`: `tsc --noEmit` exited 0. +- `pnpm build:vite`: `✓ 113 modules transformed`; `✓ built in 960ms`. +- `pnpm build` (Next): `✓ Compiled successfully in 4.8s`; static pages generated 10/10. +- `pnpm lint`: `eslint src` exited 0. +- Root `pnpm format:check`: `All matched files use Prettier code style!`. +- Final post-commit non-test gates and final-action Node 26 `pnpm test` remain required before reporting completion.