test(fence): switch the record over the page's own connection

FENCE-AC-07 drove its external switch through the browser context's API
request context, which shares one process-wide keep-alive agent with every
other API context in the runner. A POST there can be handed a connection the
server has already closed on its five-second idle timeout, and a POST is never
replayed on a fresh one — twice this line ended the spec with
`read ECONNRESET`, on all three attempts.

The switch is sent with the page's own `fetch`. It stays external in the sense
the case needs: the switch UI is what runs the cache wipe and a bare `fetch` is
not it, and the service worker returns without touching any non-GET, so the
request reaches the network unaltered. The positive control below still proves
the cached entry survived.
This commit is contained in:
Marc-André Bombeck committed 2026-09-09 23:03:05 +02:00
1 parent 0bf54b48eb
commit 2b3c01738e
1 file changed
+21 -5
+21 -5
View File
@@ -411,12 +411,28 @@ test.describe.serial("FENCE-AC-07 record-fence disk layers", () => {
expect(stored!.epoch).toMatch(/^\d+$/);
expect(stored!.scope).not.toBe("self");
// The EXTERNAL switch: driven from a request context, so the app never
// runs its cache wipe and the entry above survives.
const left = await context.request.post("/api/account/switch", {
data: { accountId: null },
// The EXTERNAL switch: nothing in the app initiates it, so
// `clearOfflineCachesForRecordSwitch` never runs and the entry above
// survives — the positive control below is what proves that.
//
// Sent with the page's own `fetch` rather than through
// `context.request`. Both are outside the app: the switch UI is what runs
// the wipe, and a bare `fetch` is not it (the service worker returns
// without touching any non-GET, so the request reaches the network
// unaltered). What differs is the socket. Every Playwright API context in
// the runner shares one process-wide keep-alive agent, so this POST could
// be handed a connection the server had already closed on its five-second
// idle timeout, and a POST is never replayed on a fresh one — twice this
// line ended the spec with `read ECONNRESET`, on all three attempts.
const left = await page.evaluate(async () => {
const res = await fetch("/api/account/switch", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ accountId: null }),
});
return res.status;
});
expect(left.status()).toBe(200);
expect(left).toBe(200);
const me = await context.request.get("/api/auth/me");
const now = (await me.json()).data.recordSession as {
epoch: number;