/* eslint-disable jest/valid-expect-in-promise */ import { createNext } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' import { fetchViaHTTP } from 'next-test-utils' describe('edge api can use async local storage', () => { let next: NextInstance const cases = [ { title: 'a single instance', code: ` export const config = { runtime: 'edge' } const storage = new AsyncLocalStorage() export default async function handler(request) { const id = request.headers.get('req-id') return storage.run({ id }, async () => { await getSomeData() return Response.json(storage.getStore()) }) } async function getSomeData() { try { const response = await fetch('https://example.vercel.sh') await response.text() } finally { return true } } `, expectResponse: (response, id) => expect(response).toMatchObject({ status: 200, json: { id } }), }, { title: 'multiple instances', code: ` export const config = { runtime: 'edge' } const topStorage = new AsyncLocalStorage() export default async function handler(request) { const id = request.headers.get('req-id') return topStorage.run({ id }, async () => { const nested = await getSomeData(id) return Response.json({ ...nested, ...topStorage.getStore() }) }) } async function getSomeData(id) { const nestedStorage = new AsyncLocalStorage() return nestedStorage.run('nested-' + id, async () => { try { const response = await fetch('https://example.vercel.sh') await response.text() } finally { return { nestedId: nestedStorage.getStore() } } }) } `, expectResponse: (response, id) => expect(response).toMatchObject({ status: 200, json: { id: id, nestedId: `nested-${id}` }, }), }, ] afterEach(() => next.destroy()) it.each(cases)( 'cans use $title per request', async ({ code, expectResponse }) => { next = await createNext({ files: { 'pages/index.js': ` export default function () { return
Hello, world!
} `, 'pages/api/async.js': code, }, }) const ids = Array.from({ length: 100 }, (_, i) => `req-${i}`) const responses = await Promise.all( ids.map((id) => fetchViaHTTP( next.url, '/api/async', {}, { headers: { 'req-id': id } } ).then((response) => response.headers.get('content-type')?.startsWith('application/json') ? response.json().then((json) => ({ status: response.status, json, text: null, })) : response.text().then((text) => ({ status: response.status, json: null, text, })) ) ) ) const rankById = new Map(ids.map((id, rank) => [id, rank])) const errors: Error[] = [] for (const [rank, response] of responses.entries()) { try { expectResponse(response, ids[rank]) } catch (error) { const received = response.json?.id console.log( `response #${rank} has id from request #${rankById.get(received)}` ) errors.push(error as Error) } } if (errors.length) { throw errors[0] } } ) })