Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions backend/__tests__/unit/services/pgBootService.sentryWrap.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* TASK-172 — the PREMISE behind #1958's wrap clause, checked against the real
* library instead of a stand-in.
*
* THE GAP THIS CLOSES. `pgBootService.test.js` proves the wrap clause works:
* it hands `routerIsMounted` a layer whose handle it has wrapped by hand
* (`wrapLikeSentry`). It cannot do better, because jest owns module loading and
* `@sentry/node` patches express through a require-hook — so in-suite
* `layer.handle === router` is TRUE even with Sentry initialised. The
* consequence is that the suite pins the FIX and never the REASON: if a Sentry
* upgrade stops exposing `.stack` on the wrapper, every case stays green and
* production goes back to refusing a pod that has the PG routes mounted, which
* is the rollout hang of 2026-09-27 (deploy `4e60f240`: `deployment.apps/backend`
* did not become ready within 8m, `/api/pg/messages` 401 on a pod whose
* `/ready` said "not mounted").
*
* HOW IT ESCAPES THE TIER. The probe is a spawned plain-`node` child
* (`__tests__/utils/sentryLayerWrapProbe.js`), so the module registry is the
* real one: `Sentry.init()` runs before `require('express')` and the patch
* lands. `process.execPath` is used, so the child runs whatever node the suite
* runs — the premise is checked under the interpreter that would run it.
*
* WHY THREE ASSERTIONS AND NOT ONE. "It reads true" can be true for the wrong
* reason, so the probe reports the shape of the instrumentation as well as the
* answer, and the suite pins both directions of the failure:
*
* - `wrapped` false under Sentry → the check is VACUOUS. Identity alone
* answers the question, so the suite would pass with the clause deleted and
* a future reader would be told the clause is load-bearing when it is not.
* (Or the instrumentation failed to install in the child, which is worth
* knowing too — the message names both.)
* - `wrapped` true, `handleHasStack` false → the exact production break: the
* clause can no longer match a mounted router, so readiness refuses a pod
* that has chat. Red here is a deploy-blocking regression caught pre-merge
* instead of at rollout.
* - no-wrong-answer direction: an unmounted router must be false, and a
* router mounted on a DIFFERENT app must be false. The clause WIDENS a
* match (it accepts `handle.stack === routerStack`), so this is the
* direction that would silently start reporting "mounted" for a router the
* app does not serve.
*
* The `PROBE_SENTRY=0` run is the positive control: identical construction with
* no instrumentation. `wrapped` must be false there, which is what makes the
* instrumented run's `wrapped: true` attributable to Sentry rather than to the
* express version — and what proves the probe can see a difference rather than
* reporting a blind null.
*/
const { spawnSync } = require('child_process');
const path = require('path');

const PROBE = path.join(__dirname, '..', '..', 'utils', 'sentryLayerWrapProbe.js');
const BACKEND_ROOT = path.join(__dirname, '..', '..', '..');

const runProbe = (withSentry) => {
const proc = spawnSync(process.execPath, [PROBE], {
cwd: BACKEND_ROOT,
encoding: 'utf8',
timeout: 120000,
env: { ...process.env, PROBE_SENTRY: withSentry ? '1' : '0' },
});

const lines = (proc.stdout || '').split('\n').map((l) => l.trim()).filter(Boolean);
let result = null;
try {
result = JSON.parse(lines[lines.length - 1]);
} catch (error) {
result = null;
}

// Reported rather than asserted here, so each case can name what it expected
// and the reader gets the child's own output when the probe could not run.
return {
result,
status: proc.status,
signal: proc.signal,
stderr: (proc.stderr || '').trim(),
stdout: (proc.stdout || '').trim(),
};
};

const describeProbe = (label, run) => {
const detail = `[${label}] exit=${run.status} signal=${run.signal} stdout=${run.stdout} stderr=${run.stderr}`;
if (!run.result) throw new Error(`the probe produced no JSON result. ${detail}`);
if (run.result.error) {
throw new Error(`the probe threw in the child: ${run.result.error}. ${detail}`);
}
return run.result;
};

describe('routerIsMounted under real @sentry/node instrumentation (TASK-172)', () => {
let plain;
let instrumented;

beforeAll(() => {
plain = runProbe(false);
instrumented = runProbe(true);
});

it('control: with no instrumentation the layer handle IS the router, and the answer is true', () => {
const result = describeProbe('control', plain);
expect(result.sentry).toBe(false);
// The control is what gives the instrumented run meaning: if this were true
// there too, the probe would be blind to the patch rather than measuring it.
expect(result.wrapped).toBe(false);
expect(result.self).toBe(true);
});

it('instrumentation really replaces the layer handle, so this suite is not vacuous', () => {
const result = describeProbe('instrumented', instrumented);
expect(result.sentry).toBe(true);
expect(result.layerFound).toBe(true);
expect(result.wrapped).toBe(true);
expect(result.handleName).not.toBe('router');
// The property #1958's clause keys on. If this goes false, the clause
// cannot match and readiness refuses a pod that has the routes mounted —
// the production shape this file exists to catch before a rollout.
expect(result.handleHasStack).toBe(true);
});

it('answers true for a mounted router while instrumentation has wrapped its layer', () => {
const result = describeProbe('instrumented', instrumented);
expect(result.self).toBe(true);
});

it('answers false for a router that is not mounted, and for one mounted on another app', () => {
const result = describeProbe('instrumented', instrumented);
expect(result.unmounted).toBe(false);
expect(result.foreign).toBe(false);
});
});
103 changes: 103 additions & 0 deletions backend/__tests__/utils/sentryLayerWrapProbe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* TASK-172 — the real-Sentry proof of the premise behind #1958's wrap clause.
*
* WHY THIS IS A SPAWNED CHILD AND NOT A SUITE. Jest cannot host this check.
* `@sentry/node` patches `express` through OpenTelemetry's require-hook, which
* installs itself on `Module._load` when `Sentry.init()` runs; jest owns module
* loading instead, so express is never patched in-suite and
* `layer.handle === router` stays TRUE there. That is exactly why
* `pgBootService.test.js` has to fake the wrapper with `wrapLikeSentry` — and
* exactly why the faked wrapper pins the FIX and can never pin the PREMISE.
* The premise is a fact about the real library, so it needs a real `node`.
*
* It lives under `__tests__/utils/` because jest's `testMatch` collects
* everything under `__tests__/` and would otherwise fail this directory-shaped
* way ("your test suite must contain at least one test" — the same reason
* `utils/` is already ignored in `backend/jest.config.js`). This file is data
* for a test, never a test.
*
* WHAT IT MEASURES. Three facts, because "the guard reads true" alone can be
* true for the wrong reason:
* 1. `wrapped` — did instrumentation actually replace the layer handle with a
* wrapper? If this is false, the suite is vacuous: identity alone answers
* the question, and a future Sentry that stops wrapping would silently
* retire the premise while this probe kept reporting `self: true`.
* 2. `handleHasStack` — does the wrapper expose the router's own stack array?
* That is the property #1958's clause keys on.
* 3. `self` / `unmounted` / `foreign` — the real `routerIsMounted` answering
* true for the mounted router, false for a router that is not mounted, and
* false for an app that mounted a DIFFERENT router (the false-positive
* direction, which matters because the clause widens a match).
*
* `PROBE_SENTRY=0` runs the identical construction with no instrumentation, as
* the positive control: `wrapped` must be false there, so the difference in
* `self` is attributable to Sentry rather than to the express version.
*
* Prints one JSON line and always exits 0; the suite asserts on the fields, so
* a crash is reported as a missing result with stderr attached rather than as
* an opaque non-zero status.
*/
const withSentry = process.env.PROBE_SENTRY === '1';
const out = { sentry: withSentry };

try {
// ts-node first: nothing else may load express before Sentry.init().
require('ts-node').register({
transpileOnly: true,
skipProject: true,
compilerOptions: { module: 'commonjs', target: 'es2020', esModuleInterop: true },
});

if (withSentry) {
const Sentry = require('@sentry/node');
Sentry.init({
// A syntactically valid DSN with a non-resolving host: init() must run for
// the express instrumentation to install, and no event should ever leave.
dsn: 'https://0123456789abcdef0123456789abcdef@example.invalid/1',
tracesSampleRate: 0,
});
}

const express = require('express');
// A literal path, both so eslint can see the target and so ts-node's hook is
// the only thing that has to understand the extension.
const { routerIsMounted } = require('../../services/pgBootService.ts');

const app = express();
const router = express.Router();
const other = express.Router();
const neverMounted = express.Router();
router.get('/', (req, res) => res.json({ ok: true }));
other.get('/', (req, res) => res.json({ ok: true }));
app.use('/api/pg/messages', router);
app.use('/api/other', other);

// A second app that mounts only the other router: asking it about `router`
// must be false, whether or not the layers are wrapped.
const foreignApp = express();
foreignApp.use('/api/other', other);

const stackOf = (a) => (a._router && a._router.stack) || (a.router && a.router.stack) || [];
const layerFor = (a, target) => stackOf(a).find(
(l) => l.handle === target || (l.handle && l.handle.stack === target.stack),
);

const layer = layerFor(app, router);
const foreignLayer = layerFor(foreignApp, other);

out.layerFound = Boolean(layer);
out.wrapped = Boolean(layer) && layer.handle !== router;
out.handleType = layer ? typeof layer.handle : null;
out.handleName = (layer && layer.handle && layer.handle.name) || null;
out.handleHasStack = Boolean(layer && layer.handle && layer.handle.stack);
out.foreignWrapped = Boolean(foreignLayer) && foreignLayer.handle !== other;
out.self = routerIsMounted(app, router);
out.unmounted = routerIsMounted(app, neverMounted);
out.foreign = routerIsMounted(foreignApp, router);
} catch (error) {
out.error = (error && error.message) || String(error);
out.stack = (error && error.stack) || null;
}

process.stdout.write(`${JSON.stringify(out)}\n`);
process.exit(0);
Loading