From 427bc739b9c63dfa2b09a8c6b977b24ad16c0229 Mon Sep 17 00:00:00 2001 From: Fernando Frizzatti Date: Mon, 18 May 2026 19:36:15 -0300 Subject: [PATCH] fix(test): unbreak vtex_segment cookie-forward tests on Node 22 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two failing cases in `client-segment-cookie.test.ts` had been silently broken since the test was first written — they pass the default NOOP `RequestStore` so `RequestContext.current` returns null inside the test callback and the production code never sees the cookie under test. The five passing cases happen to expect `headers.cookie === undefined`, which the broken setup also produces, masking the bug. The release pipeline on `next` finally caught it because the merged observability work bumped the Node target enough that another spec wedged the runner into surfacing all failures instead of one. There are two compounding issues: 1. `new Request(url, { headers: { cookie } })` silently drops the `cookie` header under Node 22 / undici because the Request headers guard is "request", which strips forbidden request headers. The cookie never reached `request.headers.get("cookie")`. 2. `RequestContext` is backed by a `RequestStore` that defaults to a NOOP. The ALS-backed store is wired only at worker boot in site code; in unit tests the run callback executes without any context propagation, so `RequestContext.current` returns null. Fix replaces the `RequestContext.run(new Request(...))` fixture with a fresh `Headers` (which uses the "none" guard, so `set("cookie", ...)` works) wrapped in a minimal Ctx-shaped object, and overrides the `RequestContext.current` getter via `vi.spyOn`. The spy is restored after `fn` resolves to keep tests isolated. No reliance on undici internals or ALS plumbing. Result: 413/413 tests pass. Unblocks the Release pipeline on `next`, which runs `npm test` before `npx semantic-release` and was failing on the post-merge run of PR #48 (the observability work). Co-authored-by: Cursor --- vtex/__tests__/client-segment-cookie.test.ts | 43 +++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/vtex/__tests__/client-segment-cookie.test.ts b/vtex/__tests__/client-segment-cookie.test.ts index f165d20..f0de476 100644 --- a/vtex/__tests__/client-segment-cookie.test.ts +++ b/vtex/__tests__/client-segment-cookie.test.ts @@ -18,12 +18,45 @@ function mockResponse(body: unknown = {}, status = 200): Response { } as Response; } -/** Run `fn` inside a fake request context with the given cookie header. */ +/** + * Run `fn` inside a fake request context with the given cookie header. + * + * Two problems prevented a naive `RequestContext.run(new Request(...))` + * approach from working: + * + * 1. Under the Fetch spec a Request's headers are in the "request" + * guard mode, which silently drops forbidden request headers — + * including `cookie` — at construction time. Node 22 / undici + * enforces this strictly, so the cookie never reaches + * `request.headers.get("cookie")`. + * 2. `@decocms/start`'s `RequestContext` is backed by a + * `RequestStore` that defaults to a NOOP implementation. The + * ALS-backed store is installed by site code at worker boot, not + * in unit tests. So `RequestContext.run(req, fn)` calls + * `fn()` without any propagation, and `RequestContext.current` + * inside `fn` still returns `null` — production code under test + * never sees the test's cookie. + * + * Fix: build a fresh `Headers` object (which uses the "none" guard, + * so `set("cookie", ...)` works), wrap it in a minimal `Ctx`-shaped + * object, and override the `RequestContext.current` getter via + * `vi.spyOn`. The spy is restored after `fn` resolves to keep tests + * isolated. Nothing here depends on undici or ALS internals. + */ function withRequest(cookieHeader: string | null, fn: () => Promise): Promise { - const request = new Request("http://localhost/", { - headers: cookieHeader ? { cookie: cookieHeader } : {}, - }); - return RequestContext.run(request, fn); + const headers = new Headers(); + if (cookieHeader) headers.set("cookie", cookieHeader); + const fakeCtx = { + request: { headers } as unknown as Request, + signal: new AbortController().signal, + responseHeaders: new Headers(), + bag: new Map(), + startedAt: Date.now(), + }; + const spy = vi + .spyOn(RequestContext, "current", "get") + .mockReturnValue(fakeCtx as unknown as ReturnType); + return fn().finally(() => spy.mockRestore()); } describe("vtexFetchResponse — vtex_segment cookie forwarding", () => {