// Shared cookie-jar test helper for the e2e suites (Phase 2A/2B/3A). Node's fetch doesn't // persist cookies across calls the way a browser does, and Phase 3A's CSRF double-submit // pattern means every state-changing request needs an X-CSRF-Token header echoing whichever // rn_csrf_* cookie the jar is currently holding — this centralizes both instead of repeating // slightly-different versions in every test file. function makeSession(baseUrl) { const jar = new Map(); // cookie name -> value function cookieHeader() { return [...jar].map(([k, v]) => `${k}=${v}`).join('; '); } function csrfToken() { return jar.get('rn_csrf_s') || jar.get('rn_csrf_i') || ''; } function absorbSetCookie(res) { const cookies = typeof res.headers.getSetCookie === 'function' ? res.headers.getSetCookie() : []; for (const c of cookies) { const [pair] = c.split(';'); const eq = pair.indexOf('='); if (eq === -1) continue; const name = pair.slice(0, eq).trim(); const value = pair.slice(eq + 1).trim(); if (value === '') jar.delete(name); else jar.set(name, value); } } return { async request(method, path, body) { const headers = { 'Content-Type': 'application/json' }; const cookies = cookieHeader(); if (cookies) headers.Cookie = cookies; if (!['GET', 'HEAD'].includes(method)) headers['X-CSRF-Token'] = csrfToken(); const res = await fetch(`${baseUrl}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined }); absorbSetCookie(res); const text = await res.text(); let json; try { json = JSON.parse(text); } catch { json = text; } return { status: res.status, body: json }; }, rawFetch(path) { const cookies = cookieHeader(); return fetch(`${baseUrl}${path}`, { headers: cookies ? { Cookie: cookies } : {} }); }, hasCookie(name) { return jar.has(name); }, getCookie(name) { return jar.get(name); }, // Lets a test simulate "this browser was never issued a CSRF cookie" or "attach the wrong // one" for a negative CSRF test, without hand-rolling a second fetch call. setCookie(name, value) { jar.set(name, value); }, clearCookie(name) { jar.delete(name); }, }; } module.exports = { makeSession };