@dotpress/testing 2.0.0-alpha.3 → 2.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-MUWNILMA.js +51 -0
- package/dist/chunk-MUWNILMA.js.map +1 -0
- package/dist/conformance.d.ts +28 -0
- package/dist/conformance.js +1266 -0
- package/dist/conformance.js.map +1 -0
- package/dist/index.d.ts +17 -52
- package/dist/index.js +43 -1310
- package/dist/index.js.map +1 -1
- package/dist/target-BZC-J2SE.d.ts +25 -0
- package/package.json +7 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/assertions.ts","../src/conformance.ts","../src/logger.ts","../src/request.ts","../src/lambda.ts","../src/memory.ts","../src/events.ts","../src/mail.ts"],"sourcesContent":["import { expect } from 'vitest'\n\n/** Error of a failed assertion, shown by the test runner. */\nexport class ExpectationError extends Error {\n override name = 'AssertionError'\n}\n\n/**\n * Whether a value matches an expected value: objects partially (like\n * `toMatchObject`), other values exactly (like `toEqual`); asymmetric\n * matchers (`expect.any()`, `expect.stringContaining()`…) are accepted.\n */\nexport const matches = (actual: unknown, expected: unknown): boolean => {\n try {\n if (\n typeof expected === 'object' &&\n expected !== null &&\n typeof actual === 'object' &&\n actual !== null\n ) {\n expect(actual).toMatchObject(expected)\n } else {\n expect(actual).toEqual(expected)\n }\n return true\n } catch {\n return false\n }\n}\n\n/** Short JSON of a value, for messages. */\nexport const show = (value: unknown): string => {\n let json: string | undefined\n try {\n // Asymmetric matchers are shown as in vitest: Any<String>.\n json = JSON.stringify(value, (_key, item: unknown) =>\n typeof item === 'object' && item !== null && 'asymmetricMatch' in item\n ? ((\n item as { toAsymmetricMatcher?: () => string }\n ).toAsymmetricMatcher?.() ?? String(item))\n : item\n )\n } catch {\n json = undefined\n }\n json ??= String(value)\n return json.length > 300 ? `${json.slice(0, 297)}...` : json\n}\n\n/** Checks the `times` option of an assertion. */\nexport const checkTimes = (label: string, times: unknown): void => {\n if (\n times !== undefined &&\n (!Number.isInteger(times) || (times as number) < 0)\n ) {\n throw new TypeError(`${label}: times must be a non-negative integer.`)\n }\n}\n\n/**\n * Throws unless `found` is `times`, or at least 1 without `times`.\n * `description` names what was expected, with the phrase for none;\n * `recorded` lists what was found.\n */\nexport const assertCount = (\n description: { readonly some: string; readonly none: string },\n found: number,\n times: number | undefined,\n recorded: string\n): void => {\n if (times === undefined ? found > 0 : found === times) return\n const expected =\n times === undefined\n ? description.some\n : times === 0\n ? description.none\n : `${description.some} ${times === 1 ? 'once' : `${times} times`}`\n throw new ExpectationError(\n `Expected ${expected}, found ${found}.\\n${recorded}`\n )\n}\n","import {\n DEFAULT_BODY_LIMIT,\n createRouter,\n file,\n json,\n notFoundError,\n redirect,\n sse,\n stream,\n text,\n unauthorizedError,\n type AppOptions,\n type Middleware,\n type Router,\n} from '@dotpress/core'\nimport { describe, expect, it, vi } from 'vitest'\nimport { createMemoryLogger } from './logger.js'\nimport { toRequest } from './request.js'\n\n/**\n * Running application under test.\n */\nexport interface ConformanceTarget {\n /** Sends a request to the application. `path` includes the query. */\n fetch(path: string, init?: RequestInit): Promise<Response>\n /** Stops the application. */\n close(): Promise<void>\n}\n\nexport type ConformanceAppOptions = Omit<AppOptions, 'adapter'>\n\nexport interface ConformanceSuiteOptions {\n /** Adapter name, used in test titles. */\n name: string\n /**\n * Creates an app with the adapter under test and the given options, and\n * starts it. Called once per test.\n */\n setup(options: ConformanceAppOptions): Promise<ConformanceTarget>\n /**\n * The adapter sends responses once complete (AWS Lambda without response\n * streaming). The scenarios that need incremental streaming or client\n * disconnects are skipped.\n */\n bufferedResponses?: boolean\n}\n\n/**\n * Target for adapters listening on an HTTP port.\n */\nexport const createHttpTarget = (\n baseUrl: string,\n close: () => Promise<void>\n): ConformanceTarget => ({\n fetch: (path, init) => fetch(new URL(path, baseUrl), init),\n close,\n})\n\n/**\n * Target for adapters handling Web requests in memory (`fetch()` of the\n * adapter, serverless handlers). `path` is relative to `http://localhost`.\n */\nexport const createFetchTarget = (\n handle: (request: Request) => Promise<Response>,\n close: () => Promise<void>\n): ConformanceTarget => ({\n fetch: async (path, init) => handle(await toRequest(path, init)),\n close,\n})\n\nconst TIMEOUT = 2000\n\nconst within = <T>(promise: Promise<T>, label: string): Promise<T> =>\n Promise.race([\n promise,\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(`Timed out waiting for: ${label}`)),\n TIMEOUT\n )\n ),\n ])\n\nconst deferred = () => {\n let resolve!: () => void\n const promise = new Promise<void>((done) => {\n resolve = done\n })\n return { promise, resolve }\n}\n\nconst waitForAbort = (signal: AbortSignal) =>\n new Promise<void>((resolve) => {\n if (signal.aborted) resolve()\n signal.addEventListener('abort', () => resolve(), { once: true })\n })\n\nconst readChunk = async (reader: ReadableStreamDefaultReader<Uint8Array>) => {\n const { done, value } = await reader.read()\n return done ? undefined : new TextDecoder().decode(value)\n}\n\n/**\n * Registers the adapter conformance suite (vitest). An adapter is ready\n * when it passes every test.\n */\nexport const runConformanceSuite = (options: ConformanceSuiteOptions) => {\n const serve = async (\n define: (router: Router) => void,\n appOptions: Omit<ConformanceAppOptions, 'routers'> = {}\n ) => {\n const router = createRouter()\n define(router)\n return options.setup({\n logger: createMemoryLogger(),\n ...appOptions,\n routers: [router],\n })\n }\n\n const withApp = async (\n define: (router: Router) => void,\n run: (target: ConformanceTarget) => Promise<void>,\n appOptions?: Omit<ConformanceAppOptions, 'routers'>\n ) => {\n const target = await serve(define, appOptions)\n try {\n await run(target)\n } finally {\n await target.close()\n }\n }\n\n const postJson = (body: unknown): RequestInit => ({\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n\n describe(`${options.name} adapter conformance`, () => {\n describe('routing', () => {\n it('serves JSON results', () =>\n withApp(\n (router) => {\n router.defineRoute({ path: '/hello', handler: () => ({ hi: 1 }) })\n },\n async ({ fetch }) => {\n const res = await fetch('/hello')\n\n expect(res.status).toBe(200)\n expect(res.headers.get('content-type')).toBe(\n 'application/json; charset=utf-8'\n )\n expect(await res.json()).toEqual({ hi: 1 })\n }\n ))\n\n it('routes every HTTP method', () =>\n withApp(\n (router) => {\n for (const method of [\n 'get',\n 'post',\n 'put',\n 'patch',\n 'delete',\n 'options',\n ] as const) {\n router.defineRoute({\n method,\n path: '/resource',\n handler: ({ req }) => ({ method: req.method }),\n })\n }\n router.defineRoute({\n method: 'head',\n path: '/resource',\n handler: ({ res }) => {\n res.setHeader('x-head', 'yes')\n },\n })\n },\n async ({ fetch }) => {\n for (const method of [\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n ]) {\n const res = await fetch('/resource', { method })\n expect(await res.json()).toEqual({ method })\n }\n\n const head = await fetch('/resource', { method: 'HEAD' })\n expect(head.headers.get('x-head')).toBe('yes')\n }\n ))\n\n it('extracts decoded path parameters', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/orgs/:orgId/files/:name',\n handler: ({ req }) => req.params,\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/orgs/acme/files/a%20b%2Fc%C3%A9')\n expect(await res.json()).toEqual({ orgId: 'acme', name: 'a b/cé' })\n }\n ))\n\n it('gives static segments priority over parameters', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/users/:id',\n handler: ({ req }) => ({ id: req.params.id }),\n })\n router.defineRoute({ path: '/users/me', handler: () => 'me' })\n },\n async ({ fetch }) => {\n expect(await (await fetch('/users/me')).json()).toBe('me')\n expect(await (await fetch('/users/42')).json()).toEqual({\n id: '42',\n })\n }\n ))\n\n it('ignores trailing slashes and matches case-sensitively', () =>\n withApp(\n (router) => {\n router.defineRoute({ path: '/users', handler: () => 'users' })\n },\n async ({ fetch }) => {\n expect((await fetch('/users/')).status).toBe(200)\n expect((await fetch('/Users')).status).toBe(404)\n }\n ))\n\n it('returns the standard 404 error for unknown paths', () =>\n withApp(\n (router) => {\n router.defineRoute({ path: '/items', handler: () => [] })\n },\n async ({ fetch }) => {\n for (const method of ['GET', 'POST']) {\n const res = await fetch('/missing', { method })\n\n expect(res.status).toBe(404)\n expect(res.headers.get('x-request-id')).toBeTruthy()\n expect(await res.json()).toEqual({\n status: 404,\n error: 'NOT_FOUND',\n message: 'Route not found',\n })\n }\n }\n ))\n\n it('returns 405 with an Allow header for unsupported methods', () =>\n withApp(\n (router) => {\n router.defineRoute({ path: '/items/:id', handler: () => ({}) })\n router.defineRoute({\n method: 'delete',\n path: '/items/:id',\n handler: () => {},\n })\n },\n async ({ fetch }) => {\n for (const method of ['POST', 'OPTIONS']) {\n const res = await fetch('/items/1', { method })\n\n expect(res.status).toBe(405)\n expect(res.headers.get('allow')).toBe('GET, HEAD, DELETE')\n expect(res.headers.get('x-request-id')).toBeTruthy()\n expect(await res.json()).toEqual({\n status: 405,\n error: 'METHOD_NOT_ALLOWED',\n message: 'Method not allowed',\n })\n }\n }\n ))\n\n it('answers HEAD requests with the GET route, without body', () =>\n withApp(\n (router) => {\n router.defineRoute({ path: '/data', handler: () => ({ a: 1 }) })\n router.defineRoute({\n path: '/stream',\n handler: () => stream(['never sent']),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/data', { method: 'HEAD' })\n expect(res.status).toBe(200)\n expect(res.headers.get('content-type')).toBe(\n 'application/json; charset=utf-8'\n )\n expect(await res.text()).toBe('')\n\n const streamed = await fetch('/stream', { method: 'HEAD' })\n expect(streamed.status).toBe(200)\n expect(await streamed.text()).toBe('')\n }\n ))\n\n it('mounts groups with their prefix and middlewares', () => {\n const calls: string[] = []\n const track =\n (name: string): Middleware =>\n () => {\n calls.push(name)\n }\n\n return withApp(\n (router) => {\n const api = router.createGroup('/api', [track('api')])\n api.createGroup('/admin', [track('admin')]).defineRoute({\n path: '/stats',\n middlewares: [track('route')],\n handler: () => ({ ok: true }),\n })\n },\n async ({ fetch }) => {\n expect(await (await fetch('/api/admin/stats')).json()).toEqual({\n ok: true,\n })\n expect(calls).toEqual(['global', 'api', 'admin', 'route'])\n },\n { middlewares: [track('global')] }\n )\n })\n\n it('serves the routes under the base path only', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/users/:id',\n handler: ({ req }) => ({ id: req.params.id, path: req.path }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/my-app/api/users/42')\n expect(res.status).toBe(200)\n expect(await res.json()).toEqual({\n id: '42',\n path: '/my-app/api/users/42',\n })\n\n for (const path of ['/users/42', '/my-app/users/42']) {\n const outside = await fetch(path)\n expect(outside.status, path).toBe(404)\n expect(await outside.json()).toMatchObject({\n error: 'NOT_FOUND',\n })\n }\n },\n { basePath: '/my-app/api' }\n ))\n })\n\n describe('request', () => {\n it('parses the query string', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/search',\n handler: ({ req }) => req.query,\n })\n },\n async ({ fetch }) => {\n const res = await fetch(\n '/search?q=hello+world&tag=a&tag=b&name=%C3%A9&empty='\n )\n expect(await res.json()).toEqual({\n q: 'hello world',\n tag: ['a', 'b'],\n name: 'é',\n empty: '',\n })\n }\n ))\n\n it('exposes the method, path, URL, headers and cookies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/inspect',\n handler: ({ req }) => ({\n method: req.method,\n path: req.path,\n url:\n req.url &&\n new URL(req.url).pathname + new URL(req.url).search,\n custom: req.header('X-Custom'),\n lowercase: req.headers['x-custom'],\n cookies: req.cookies,\n }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/inspect?a=1', {\n method: 'POST',\n headers: {\n 'X-Custom': 'value',\n cookie: 'session=abc; theme=dark%20blue',\n },\n })\n\n expect(await res.json()).toEqual({\n method: 'POST',\n path: '/inspect',\n url: '/inspect?a=1',\n custom: 'value',\n lowercase: 'value',\n cookies: { session: 'abc', theme: 'dark blue' },\n })\n }\n ))\n\n it('exposes the client IP address', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/ip',\n handler: ({ req }) => ({ ip: req.ip }),\n })\n },\n async ({ fetch }) => {\n const { ip } = (await (await fetch('/ip')).json()) as { ip: string }\n // Loopback address, in IPv4, IPv4-mapped IPv6 or IPv6 form.\n expect(ip).toMatch(/^((::ffff:)?127\\.0\\.0\\.1|::1)$/)\n }\n ))\n\n it('parses JSON bodies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/echo',\n handler: ({ req }) => ({ body: req.body }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/echo', postJson({ a: [1, 'é'], b: null }))\n expect(await res.json()).toEqual({ body: { a: [1, 'é'], b: null } })\n }\n ))\n\n it('rejects malformed JSON bodies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/echo',\n handler: () => ({ unreachable: true }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/echo', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: '{\"broken\":',\n })\n\n expect(res.status).toBe(400)\n expect(res.headers.get('x-request-id')).toBeTruthy()\n expect(await res.json()).toEqual({\n status: 400,\n error: 'MALFORMED_BODY',\n message: 'Malformed request body',\n })\n }\n ))\n\n it('parses URL-encoded, text and binary bodies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/echo',\n handler: ({ req }) =>\n req.body instanceof Uint8Array\n ? { bytes: [...req.body] }\n : { body: req.body },\n })\n },\n async ({ fetch }) => {\n const form = await fetch('/echo', {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: 'name=Ada+Lovelace&tag=a&tag=b',\n })\n expect(await form.json()).toEqual({\n body: { name: 'Ada Lovelace', tag: ['a', 'b'] },\n })\n\n const plain = await fetch('/echo', {\n method: 'POST',\n headers: { 'content-type': 'text/plain; charset=utf-8' },\n body: 'héllo',\n })\n expect(await plain.json()).toEqual({ body: 'héllo' })\n\n const binary = await fetch('/echo', {\n method: 'POST',\n headers: { 'content-type': 'application/octet-stream' },\n body: new Uint8Array([0, 1, 254, 255]),\n })\n expect(await binary.json()).toEqual({ bytes: [0, 1, 254, 255] })\n }\n ))\n\n it('exposes the raw body on rawBody routes', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/webhooks',\n rawBody: true,\n schema: (z) => ({ body: z.object({ type: z.string() }) }),\n handler: ({ req }) => ({\n raw: new TextDecoder().decode(req.rawBody),\n type: req.body.type,\n }),\n })\n router.defineRoute({\n method: 'post',\n path: '/ping',\n rawBody: true,\n handler: ({ req }) => ({ length: req.rawBody.byteLength }),\n })\n },\n async ({ fetch }) => {\n // Whitespace and key order must be preserved for signatures.\n const payload = '{ \"type\" : \"invoice.paid\", \"id\": \"é\" }'\n const res = await fetch('/webhooks', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: payload,\n })\n expect(await res.json()).toEqual({\n raw: payload,\n type: 'invoice.paid',\n })\n\n const empty = await fetch('/ping', { method: 'POST' })\n expect(await empty.json()).toEqual({ length: 0 })\n }\n ))\n\n it('gives an undefined body when there is none', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/empty',\n handler: ({ req }) => ({ hasBody: req.body !== undefined }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/empty', { method: 'POST' })\n expect(await res.json()).toEqual({ hasBody: false })\n }\n ))\n\n it('rejects bodies larger than the default limit', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/upload',\n handler: () => ({ unreachable: true }),\n })\n },\n async ({ fetch }) => {\n const payload = new Uint8Array(DEFAULT_BODY_LIMIT + 1)\n\n const declared = await fetch('/upload', {\n method: 'POST',\n headers: { 'content-type': 'application/octet-stream' },\n body: payload,\n })\n expect(declared.status).toBe(413)\n expect(await declared.json()).toEqual({\n status: 413,\n error: 'PAYLOAD_TOO_LARGE',\n message: 'Request body too large',\n })\n\n // Chunked body, without Content-Length.\n const chunked = await fetch('/upload', {\n method: 'POST',\n headers: { 'content-type': 'application/octet-stream' },\n body: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(payload.subarray(0, DEFAULT_BODY_LIMIT))\n controller.enqueue(new Uint8Array(1))\n controller.close()\n },\n }),\n duplex: 'half',\n } as RequestInit)\n expect(chunked.status).toBe(413)\n }\n ))\n\n it('accepts bodies up to the default limit', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/upload',\n handler: ({ req }) => ({\n size: (req.body as Uint8Array).byteLength,\n }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/upload', {\n method: 'POST',\n headers: { 'content-type': 'application/octet-stream' },\n body: new Uint8Array(DEFAULT_BODY_LIMIT),\n })\n expect(await res.json()).toEqual({ size: DEFAULT_BODY_LIMIT })\n }\n ))\n\n it('rejects compressed request bodies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/echo',\n handler: () => ({ unreachable: true }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/echo', {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'content-encoding': 'gzip',\n },\n body: new Uint8Array([0x1f, 0x8b, 0x08, 0x00]),\n })\n\n expect(res.status).toBe(415)\n expect(await res.json()).toMatchObject({\n error: 'UNSUPPORTED_MEDIA_TYPE',\n })\n }\n ))\n })\n\n describe('uploads', () => {\n const formWith = (entries: [string, string | File][]) => {\n const form = new FormData()\n for (const [name, value] of entries) form.append(name, value)\n return form\n }\n\n it('parses multipart bodies into fields and files', () => {\n const bytes = Uint8Array.from({ length: 256 }, (_, index) => index)\n\n return withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/documents',\n schema: (z) => ({\n body: z.object({ title: z.string() }),\n files: z.object({\n document: z.file().mime(['application/pdf']),\n }),\n }),\n handler: async ({ req }) => ({\n title: req.body.title,\n name: req.files.document.name,\n type: req.files.document.type,\n bytes: [\n ...new Uint8Array(await req.files.document.arrayBuffer()),\n ],\n }),\n })\n router.defineRoute({\n method: 'post',\n path: '/gallery',\n handler: ({ req }) => ({\n fields: req.body,\n files: Object.fromEntries(\n Object.entries(req.files).map(([name, value]) => [\n name,\n [value].flat().map((file) => file.name),\n ])\n ),\n }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/documents', {\n method: 'POST',\n body: formWith([\n ['title', 'Contrat signé'],\n [\n 'document',\n new File([bytes], 'contrat été.pdf', {\n type: 'application/pdf',\n }),\n ],\n ]),\n })\n expect(await res.json()).toEqual({\n title: 'Contrat signé',\n name: 'contrat été.pdf',\n type: 'application/pdf',\n bytes: [...bytes],\n })\n\n const gallery = await fetch('/gallery', {\n method: 'POST',\n body: formWith([\n ['tag', 'sea'],\n ['tag', 'sun'],\n ['photos', new File(['a'], 'a.jpg')],\n ['photos', new File(['b'], 'b.jpg')],\n ['cover', new File(['c'], 'c.jpg')],\n ]),\n })\n expect(await gallery.json()).toEqual({\n fields: { tag: ['sea', 'sun'] },\n files: { photos: ['a.jpg', 'b.jpg'], cover: ['c.jpg'] },\n })\n }\n )\n })\n\n it('validates files', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/avatar',\n schema: (z) => ({\n files: z.object({\n avatar: z.file().max(10).mime(['image/png']),\n }),\n }),\n handler: () => ({ ok: true }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/avatar', {\n method: 'POST',\n body: formWith([\n [\n 'avatar',\n new File([new Uint8Array(11)], 'a.gif', {\n type: 'image/gif',\n }),\n ],\n ]),\n })\n\n expect(res.status).toBe(400)\n expect(await res.json()).toMatchObject({\n error: 'VALIDATION_FAILED',\n data: {\n issues: [\n { source: 'files', path: ['avatar'], code: 'too_big' },\n { source: 'files', path: ['avatar'], code: 'invalid_value' },\n ],\n },\n })\n }\n ))\n\n it('applies the route body limit', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/large',\n bodyLimit: 3 * DEFAULT_BODY_LIMIT,\n handler: ({ req }) => ({\n size: (req.files.file as File).size,\n }),\n })\n router.defineRoute({\n method: 'post',\n path: '/small',\n bodyLimit: 100,\n handler: () => ({ unreachable: true }),\n })\n },\n async ({ fetch }) => {\n const size = 2 * DEFAULT_BODY_LIMIT\n const large = await fetch('/large', {\n method: 'POST',\n body: formWith([\n ['file', new File([new Uint8Array(size)], 'big.bin')],\n ]),\n })\n expect(await large.json()).toEqual({ size })\n\n const small = await fetch('/small', {\n method: 'POST',\n body: formWith([\n ['file', new File([new Uint8Array(200)], 'a.bin')],\n ]),\n })\n expect(small.status).toBe(413)\n }\n ))\n\n it('rejects malformed multipart bodies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/upload',\n handler: () => ({ unreachable: true }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/upload', {\n method: 'POST',\n headers: { 'content-type': 'multipart/form-data; boundary=xyz' },\n body: 'not a multipart body',\n })\n\n expect(res.status).toBe(400)\n expect(await res.json()).toMatchObject({ error: 'MALFORMED_BODY' })\n }\n ))\n })\n\n describe('validation', () => {\n it('returns VALIDATION_FAILED errors and parsed values', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/users/:id',\n schema: (z) => ({\n params: z.object({ id: z.coerce.number().int() }),\n query: z.object({ notify: z.enum(['yes', 'no']) }),\n body: z.object({ name: z.string().trim() }),\n }),\n handler: ({ req }) => ({\n id: req.params.id,\n notify: req.query.notify,\n name: req.body.name,\n }),\n })\n },\n async ({ fetch }) => {\n const invalid = await fetch('/users/abc?notify=maybe', postJson({}))\n expect(invalid.status).toBe(400)\n expect(await invalid.json()).toMatchObject({\n status: 400,\n error: 'VALIDATION_FAILED',\n data: {\n issues: [\n { source: 'params', path: ['id'] },\n { source: 'query', path: ['notify'] },\n { source: 'body', path: ['name'] },\n ],\n },\n })\n\n const valid = await fetch(\n '/users/42?notify=yes',\n postJson({ name: ' Ada ' })\n )\n expect(await valid.json()).toEqual({\n id: 42,\n notify: 'yes',\n name: 'Ada',\n })\n }\n ))\n })\n\n describe('errors', () => {\n it('serializes returned and thrown HTTP errors', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/returned',\n handler: () => notFoundError('User not found', { id: '1' }),\n })\n router.defineRoute({\n path: '/thrown',\n handler: ({ res }) => {\n res.setHeader('www-authenticate', 'Bearer')\n throw unauthorizedError()\n },\n })\n },\n async ({ fetch }) => {\n const returned = await fetch('/returned')\n expect(returned.status).toBe(404)\n expect(await returned.json()).toEqual({\n status: 404,\n error: 'NOT_FOUND',\n message: 'User not found',\n data: { id: '1' },\n })\n\n const thrown = await fetch('/thrown')\n expect(thrown.status).toBe(401)\n expect(thrown.headers.get('www-authenticate')).toBe('Bearer')\n expect(await thrown.json()).toEqual({\n status: 401,\n error: 'UNAUTHORIZED',\n message: 'Unauthorized',\n })\n }\n ))\n\n it('hides unexpected errors behind a 500 error and reports them', async () => {\n const onError = vi.fn()\n\n await withApp(\n (router) => {\n router.defineRoute({\n path: '/crash',\n handler: () => {\n throw new Error('Database password is hunter2')\n },\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/crash')\n\n expect(res.status).toBe(500)\n expect(res.headers.get('x-request-id')).toBeTruthy()\n expect(await res.json()).toEqual({\n status: 500,\n error: 'INTERNAL_ERROR',\n message: 'Internal Server Error',\n })\n expect(onError).toHaveBeenCalledOnce()\n },\n { onError }\n )\n })\n\n it('exposes error details when exposeErrors is enabled', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/crash',\n handler: () => {\n throw new Error('Boom')\n },\n })\n },\n async ({ fetch }) => {\n expect(await (await fetch('/crash')).json()).toMatchObject({\n status: 500,\n data: { name: 'Error', message: 'Boom' },\n })\n },\n { exposeErrors: true }\n ))\n\n it('returns a 500 error when the response does not match its schema', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/invalid',\n schema: (z) => ({ response: z.object({ id: z.string() }) }),\n // @ts-expect-error the result does not match the schema\n handler: () => ({ id: 1 }),\n })\n },\n async ({ fetch }) => {\n expect((await fetch('/invalid')).status).toBe(500)\n }\n ))\n })\n\n describe('middlewares and filters', () => {\n it('stops the pipeline when a middleware returns a response', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/private',\n middlewares: [\n ({ req }) =>\n req.header('authorization') ? undefined : unauthorizedError(),\n ],\n handler: () => ({ secret: true }),\n })\n router.defineRoute({\n path: '/legacy',\n middlewares: [() => redirect('/modern', 301)],\n handler: () => ({ unreachable: true }),\n })\n },\n async ({ fetch }) => {\n expect((await fetch('/private')).status).toBe(401)\n expect(\n await (\n await fetch('/private', { headers: { authorization: 'x' } })\n ).json()\n ).toEqual({ secret: true })\n\n const legacy = await fetch('/legacy', { redirect: 'manual' })\n expect(legacy.status).toBe(301)\n expect(legacy.headers.get('location')).toBe('/modern')\n }\n ))\n\n it('applies app and router response filters', () =>\n withApp(\n (router) => {\n router.addResponseFilter((_ctx, result) => ({\n ...(result as object),\n router: true,\n }))\n router.defineRoute({ path: '/data', handler: () => ({ a: 1 }) })\n },\n async ({ fetch }) => {\n expect(await (await fetch('/data')).json()).toEqual({\n a: 1,\n app: true,\n router: true,\n })\n },\n {\n responseFilters: [\n (_ctx, result) => ({ ...(result as object), app: true }),\n ],\n }\n ))\n })\n\n describe('responses', () => {\n it('applies statuses, headers and cookies', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'post',\n path: '/created',\n status: 201,\n handler: ({ res }) => {\n res.setHeader('location', '/items/1')\n res.setCookie('session', 'abc', { httpOnly: true })\n res.setCookie('theme', 'dark')\n return { id: 1 }\n },\n })\n router.defineRoute({\n path: '/accepted',\n handler: ({ res }) => {\n res.setStatus(202)\n return json({ queued: true }, { headers: { 'x-queue': 'a' } })\n },\n })\n },\n async ({ fetch }) => {\n const created = await fetch('/created', { method: 'POST' })\n expect(created.status).toBe(201)\n expect(created.headers.get('location')).toBe('/items/1')\n expect(created.headers.getSetCookie()).toEqual([\n 'session=abc; Path=/; HttpOnly',\n 'theme=dark; Path=/',\n ])\n\n const accepted = await fetch('/accepted')\n expect(accepted.status).toBe(202)\n expect(accepted.headers.get('x-queue')).toBe('a')\n expect(await accepted.json()).toEqual({ queued: true })\n }\n ))\n\n it('answers 204 without body when the handler returns nothing', () =>\n withApp(\n (router) => {\n router.defineRoute({\n method: 'delete',\n path: '/items/:id',\n handler: () => {},\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/items/1', { method: 'DELETE' })\n expect(res.status).toBe(204)\n expect(res.headers.get('content-type')).toBeNull()\n expect(await res.text()).toBe('')\n }\n ))\n\n it('does not add framework headers', () =>\n withApp(\n (router) => {\n router.defineRoute({ path: '/plain', handler: () => 'ok' })\n },\n async ({ fetch }) => {\n const res = await fetch('/plain')\n expect(res.headers.get('x-powered-by')).toBeNull()\n expect(res.headers.get('etag')).toBeNull()\n }\n ))\n\n it('sends redirects', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/old',\n handler: () => redirect('/new'),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/old', { redirect: 'manual' })\n expect(res.status).toBe(302)\n expect(res.headers.get('location')).toBe('/new')\n }\n ))\n\n it('sends text responses', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/page',\n handler: () =>\n text('<h1>Héllo</h1>', {\n headers: { 'content-type': 'text/html; charset=utf-8' },\n }),\n })\n router.defineRoute({ path: '/health', handler: () => text('OK') })\n },\n async ({ fetch }) => {\n const page = await fetch('/page')\n expect(page.headers.get('content-type')).toBe(\n 'text/html; charset=utf-8'\n )\n expect(await page.text()).toBe('<h1>Héllo</h1>')\n\n const health = await fetch('/health')\n expect(health.headers.get('content-type')).toBe(\n 'text/plain; charset=utf-8'\n )\n expect(await health.text()).toBe('OK')\n }\n ))\n\n it('sends files without altering their bytes', () => {\n const bytes = Uint8Array.from({ length: 256 }, (_, index) => index)\n\n return withApp(\n (router) => {\n router.defineRoute({\n path: '/download',\n handler: () =>\n file(bytes, {\n filename: 'rapport été.bin',\n contentType: 'application/octet-stream',\n }),\n })\n router.defineRoute({\n path: '/blob',\n handler: () =>\n file(new Blob([bytes], { type: 'image/png' }), {\n disposition: 'inline',\n }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/download')\n expect(res.headers.get('content-length')).toBe('256')\n expect(res.headers.get('content-disposition')).toBe(\n `attachment; filename=\"rapport _t_.bin\"; filename*=UTF-8''rapport%20%C3%A9t%C3%A9.bin`\n )\n expect(new Uint8Array(await res.arrayBuffer())).toEqual(bytes)\n\n const blob = await fetch('/blob')\n expect(blob.headers.get('content-type')).toBe('image/png')\n expect(new Uint8Array(await blob.arrayBuffer())).toEqual(bytes)\n }\n )\n })\n })\n\n describe('cors', () => {\n const APP = 'https://app.example.com'\n const EVIL = 'https://evil.example.org'\n\n const corsRoutes = (router: Router) => {\n router.defineRoute({ path: '/users', handler: () => [] })\n router.defineRoute({\n method: 'post',\n path: '/users',\n handler: () => ({ created: true }),\n })\n router.defineRoute({\n path: '/private',\n middlewares: [() => unauthorizedError()],\n handler: () => ({ secret: true }),\n })\n router.defineRoute({\n method: 'options',\n path: '/custom',\n handler: () => ({ custom: true }),\n })\n router.defineRoute({\n path: '/export.csv',\n handler: () => stream(['a,b\\n'], { contentType: 'text/csv' }),\n })\n }\n\n const preflight = (origin = APP): RequestInit => ({\n method: 'OPTIONS',\n headers: {\n origin,\n 'access-control-request-method': 'POST',\n 'access-control-request-headers': 'content-type, authorization',\n },\n })\n\n it('answers preflight requests before the middlewares', () =>\n withApp(\n corsRoutes,\n async ({ fetch }) => {\n const res = await fetch('/users', preflight())\n expect(res.status).toBe(204)\n expect(res.headers.get('access-control-allow-origin')).toBe(APP)\n expect(res.headers.get('access-control-allow-methods')).toBe(\n 'GET, HEAD, POST'\n )\n expect(res.headers.get('access-control-allow-headers')).toBe(\n 'content-type, authorization'\n )\n expect(res.headers.get('access-control-max-age')).toBe('600')\n expect(res.headers.get('vary')).toContain('Origin')\n expect(await res.text()).toBe('')\n\n const private_ = await fetch('/private', preflight())\n expect(private_.status).toBe(204)\n\n const custom = await fetch('/custom', preflight())\n expect(custom.status).toBe(204)\n expect(custom.headers.get('access-control-allow-methods')).toBe(\n 'OPTIONS'\n )\n\n // Handled as usual.\n const refused = await fetch('/users', preflight(EVIL))\n expect(refused.status).toBe(405)\n expect(\n refused.headers.get('access-control-allow-origin')\n ).toBeNull()\n expect((await fetch('/missing', preflight())).status).toBe(404)\n },\n { cors: { origin: [APP], maxAge: 600 } }\n ))\n\n it('adds the CORS headers to every response, errors included', () =>\n withApp(\n corsRoutes,\n async ({ fetch }) => {\n const requests: [string, RequestInit, number][] = [\n ['/users', {}, 200],\n ['/private', {}, 401],\n ['/missing', {}, 404],\n ['/users', { method: 'DELETE' }, 405],\n [\n '/users',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: '{oops',\n },\n 400,\n ],\n ['/export.csv', {}, 200],\n ]\n\n for (const [path, init, status] of requests) {\n const res = await fetch(path, {\n ...init,\n headers: { ...init.headers, origin: APP },\n })\n expect(res.status).toBe(status)\n expect(res.headers.get('access-control-allow-origin')).toBe(APP)\n expect(res.headers.get('access-control-allow-credentials')).toBe(\n 'true'\n )\n expect(res.headers.get('access-control-expose-headers')).toBe(\n 'x-request-id'\n )\n expect(res.headers.get('vary')).toContain('Origin')\n await res.arrayBuffer()\n }\n\n const refused = await fetch('/users', { headers: { origin: EVIL } })\n expect(refused.status).toBe(200)\n expect(\n refused.headers.get('access-control-allow-origin')\n ).toBeNull()\n expect(refused.headers.get('vary')).toContain('Origin')\n },\n {\n cors: {\n origin: APP,\n credentials: true,\n exposedHeaders: ['x-request-id'],\n },\n }\n ))\n })\n\n describe('streaming', () => {\n const incremental = options.bufferedResponses ? it.skip : it\n\n it('streams chunks', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/export.csv',\n handler: () =>\n stream(\n async function* () {\n yield 'id,name\\n'\n yield new TextEncoder().encode('1,Ada\\n')\n },\n { contentType: 'text/csv' }\n ),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/export.csv')\n expect(res.status).toBe(200)\n expect(res.headers.get('content-type')).toBe('text/csv')\n expect(await res.text()).toBe('id,name\\n1,Ada\\n')\n }\n ))\n\n incremental('flushes Server-Sent Events as they are produced', () => {\n const released = deferred()\n\n return withApp(\n (router) => {\n router.defineRoute({\n path: '/events',\n handler: () =>\n sse(\n async function* ({ signal }) {\n try {\n yield { event: 'ready', data: { ok: true } }\n await waitForAbort(signal)\n } finally {\n released.resolve()\n }\n },\n { heartbeat: false }\n ),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/events')\n expect(res.headers.get('content-type')).toBe(\n 'text/event-stream; charset=utf-8'\n )\n expect(res.headers.get('cache-control')).toBe('no-cache')\n\n const reader = res.body!.getReader()\n // The source is still running: the event must not be buffered.\n expect(await within(readChunk(reader), 'first event')).toBe(\n 'event: ready\\ndata: {\"ok\":true}\\n\\n'\n )\n\n await reader.cancel()\n await within(released.promise, 'source release on disconnect')\n }\n )\n })\n\n incremental(\n 'cancels the response stream when the client disconnects',\n () => {\n const cancelled = deferred()\n\n return withApp(\n (router) => {\n router.defineRoute({\n path: '/download',\n handler: () =>\n file(\n new ReadableStream<Uint8Array>({\n pull: (controller) =>\n controller.enqueue(new Uint8Array(64 * 1024)),\n cancel: () => cancelled.resolve(),\n })\n ),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/download')\n const reader = res.body!.getReader()\n await reader.read()\n await reader.cancel()\n\n await within(cancelled.promise, 'response stream cancel')\n }\n )\n }\n )\n\n incremental('aborts req.signal when the client disconnects', () => {\n const started = deferred()\n const aborted = deferred()\n\n return withApp(\n (router) => {\n router.defineRoute({\n path: '/slow',\n handler: async ({ req }) => {\n started.resolve()\n await waitForAbort(req.signal)\n aborted.resolve()\n return { late: true }\n },\n })\n },\n async ({ fetch }) => {\n const controller = new AbortController()\n const pending = fetch('/slow', { signal: controller.signal }).catch(\n () => undefined\n )\n\n await within(started.promise, 'handler start')\n controller.abort()\n\n await within(aborted.promise, 'req.signal abort')\n await pending\n }\n )\n })\n\n incremental('aborts the connection when a stream fails', () =>\n withApp(\n (router) => {\n router.defineRoute({\n path: '/broken',\n handler: () =>\n stream(async function* () {\n yield 'partial'\n throw new Error('Source failed')\n }),\n })\n },\n async ({ fetch }) => {\n const res = await fetch('/broken')\n expect(res.status).toBe(200)\n const outcome = await within(\n res.text().then(\n () => 'completed',\n () => 'aborted'\n ),\n 'body read'\n )\n // A cleanly ended response would hide the failure from the client.\n expect(outcome).toBe('aborted')\n }\n )\n )\n })\n })\n}\n","import type { Logger } from '@dotpress/core'\n\nexport interface LogEntry {\n readonly level: 'debug' | 'info' | 'warn' | 'error'\n readonly args: readonly unknown[]\n}\n\nexport interface MemoryLogger extends Logger {\n /** Entries logged by this logger and its children. */\n readonly entries: LogEntry[]\n}\n\n/**\n * Logger that records entries instead of printing them.\n */\nexport const createMemoryLogger = (): MemoryLogger => {\n const entries: LogEntry[] = []\n\n const create = (): Logger => ({\n debug: (...args: unknown[]) => void entries.push({ level: 'debug', args }),\n info: (...args: unknown[]) => void entries.push({ level: 'info', args }),\n warn: (...args: unknown[]) => void entries.push({ level: 'warn', args }),\n error: (...args: unknown[]) => void entries.push({ level: 'error', args }),\n child: () => create(),\n })\n\n return Object.assign(create(), { entries })\n}\n","/**\n * Creates the request sent by an in-memory target. Form data bodies are\n * serialized first, as HTTP clients do: on Node.js 24, cancelling a body\n * built from a `FormData` (body limit) raises an unhandled rejection in\n * undici.\n */\nexport const toRequest = async (\n path: string,\n init?: RequestInit\n): Promise<Request> => {\n const request = new Request(new URL(path, 'http://localhost'), init)\n if (!(init?.body instanceof FormData)) return request\n return new Request(request, { body: await request.arrayBuffer() })\n}\n","import { createFetchTarget, type ConformanceTarget } from './conformance.js'\n\n/**\n * Lambda event formats: API Gateway REST API (payload v1), HTTP API and\n * Function URLs (payload v2), Application Load Balancer (with multi-value\n * headers, required for repeated query keys and several cookies).\n */\nexport type LambdaEventFormat = 'v1' | 'v2' | 'alb'\n\nexport interface LambdaTargetOptions {\n format: LambdaEventFormat\n /** Stops the application. */\n close: () => Promise<void>\n /** Client IP address of the events. Defaults to `127.0.0.1`. */\n sourceIp?: string\n}\n\n/** Lambda handler under test (`createLambdaHandler()` of an adapter). */\nexport type LambdaTestHandler = (\n event: never,\n context: never\n) => Promise<unknown>\n\ninterface LambdaResult {\n statusCode: number\n headers?: Record<string, string | string[]>\n multiValueHeaders?: Record<string, string[]>\n cookies?: string[]\n body?: string\n isBase64Encoded?: boolean\n}\n\nconst CHUNK = 0x8000\n\nconst encodeBase64 = (bytes: Uint8Array) => {\n let binary = ''\n for (let index = 0; index < bytes.length; index += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(index, index + CHUNK))\n }\n return btoa(binary)\n}\n\nconst decodeBase64 = (text: string) =>\n Uint8Array.from(atob(text), (char) => char.charCodeAt(0))\n\nconst TEXT_TYPES =\n /^\\s*(text\\/|application\\/(json|x-www-form-urlencoded)\\b|[^;]*\\+json\\b)/i\n\n/** Body of the event: text when possible, else base64, like API Gateway. */\nconst eventBody = (bytes: Uint8Array, contentType: string | null) => {\n if (bytes.byteLength === 0) return { body: null, isBase64Encoded: false }\n if (contentType && TEXT_TYPES.test(contentType)) {\n try {\n const body = new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n return { body, isBase64Encoded: false }\n } catch {\n // Invalid UTF-8: sent in base64.\n }\n }\n return { body: encodeBase64(bytes), isBase64Encoded: true }\n}\n\n/** Query parameters as sent (ALB does not decode them). */\nconst rawQuery = (search: string) => {\n const result: Record<string, string[]> = {}\n for (const pair of search.slice(1).split('&')) {\n if (!pair) continue\n const index = pair.indexOf('=')\n const key = index === -1 ? pair : pair.slice(0, index)\n const value = index === -1 ? '' : pair.slice(index + 1)\n ;(result[key] ??= []).push(value)\n }\n return result\n}\n\nconst lastValues = (values: Record<string, string[]>) =>\n Object.fromEntries(\n Object.entries(values).map(([key, list]) => [key, list.at(-1)!])\n )\n\nconst ACCOUNT = '123456789012'\n\n/** Creates the Lambda event of a request. */\nexport const toLambdaEvent = async (\n request: Request,\n format: LambdaEventFormat,\n sourceIp = '127.0.0.1'\n): Promise<Record<string, unknown>> => {\n const url = new URL(request.url)\n const headers: Record<string, string> = { host: url.host }\n for (const [name, value] of request.headers) headers[name] = value\n const { body, isBase64Encoded } = eventBody(\n new Uint8Array(await request.arrayBuffer()),\n request.headers.get('content-type')\n )\n const requestId = crypto.randomUUID()\n const userAgent = headers['user-agent'] ?? ''\n\n if (format === 'v2') {\n const { cookie, ...rest } = headers\n return {\n version: '2.0',\n routeKey: '$default',\n rawPath: url.pathname,\n rawQueryString: url.search.slice(1),\n ...(cookie ? { cookies: cookie.split(/;\\s*/) } : {}),\n headers: rest,\n requestContext: {\n accountId: ACCOUNT,\n apiId: 'api',\n domainName: url.host,\n domainPrefix: url.hostname.split('.')[0],\n http: {\n method: request.method,\n path: url.pathname,\n protocol: 'HTTP/1.1',\n sourceIp,\n userAgent,\n },\n requestId,\n routeKey: '$default',\n stage: '$default',\n time: new Date().toISOString(),\n timeEpoch: Date.now(),\n },\n ...(body === null ? {} : { body }),\n isBase64Encoded,\n }\n }\n\n const query: Record<string, string[]> = {}\n for (const [key, value] of url.searchParams) (query[key] ??= []).push(value)\n const multiValueHeaders = Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [name, [value]])\n )\n\n if (format === 'alb') {\n return {\n requestContext: {\n elb: {\n targetGroupArn: `arn:aws:elasticloadbalancing:us-east-1:${ACCOUNT}:targetgroup/dotpress/0123456789abcdef`,\n },\n },\n httpMethod: request.method,\n path: url.pathname,\n multiValueQueryStringParameters: rawQuery(url.search),\n // The load balancer appends the client address.\n multiValueHeaders: {\n ...multiValueHeaders,\n 'x-forwarded-for': [\n headers['x-forwarded-for']\n ? `${headers['x-forwarded-for']}, ${sourceIp}`\n : sourceIp,\n ],\n 'x-forwarded-proto': [url.protocol.slice(0, -1)],\n },\n body: body ?? '',\n isBase64Encoded,\n }\n }\n\n const hasQuery = Object.keys(query).length > 0\n return {\n resource: '/{proxy+}',\n path: url.pathname,\n httpMethod: request.method,\n headers,\n multiValueHeaders,\n queryStringParameters: hasQuery ? lastValues(query) : null,\n multiValueQueryStringParameters: hasQuery ? query : null,\n pathParameters: { proxy: url.pathname.slice(1) },\n stageVariables: null,\n requestContext: {\n accountId: ACCOUNT,\n apiId: 'api',\n domainName: url.host,\n httpMethod: request.method,\n identity: { sourceIp, userAgent },\n path: url.pathname,\n protocol: 'HTTP/1.1',\n requestId,\n requestTimeEpoch: Date.now(),\n resourcePath: '/{proxy+}',\n stage: '$default',\n },\n body,\n isBase64Encoded,\n }\n}\n\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304])\n\n/** Converts the result of a Lambda handler into a Web response. */\nexport const fromLambdaResult = (result: unknown): Response => {\n const { statusCode, headers, multiValueHeaders, cookies, body, ...rest } =\n result as LambdaResult\n\n const responseHeaders = new Headers()\n for (const [name, value] of Object.entries(headers ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n responseHeaders.append(name, item)\n }\n }\n for (const [name, values] of Object.entries(multiValueHeaders ?? {})) {\n for (const value of values) responseHeaders.append(name, value)\n }\n for (const cookie of cookies ?? []) {\n responseHeaders.append('set-cookie', cookie)\n }\n\n const content =\n !body || NULL_BODY_STATUSES.has(statusCode)\n ? null\n : rest.isBase64Encoded\n ? decodeBase64(body)\n : body\n\n return new Response(content as BodyInit | null, {\n status: statusCode,\n headers: responseHeaders,\n })\n}\n\nconst lambdaContext = () => ({\n awsRequestId: crypto.randomUUID(),\n functionName: 'dotpress-test',\n functionVersion: '$LATEST',\n invokedFunctionArn: `arn:aws:lambda:us-east-1:${ACCOUNT}:function:dotpress-test`,\n memoryLimitInMB: '128',\n logGroupName: '/aws/lambda/dotpress-test',\n logStreamName: 'test',\n callbackWaitsForEmptyEventLoop: true,\n getRemainingTimeInMillis: () => 30_000,\n})\n\n/**\n * Target for AWS Lambda handlers: each request is sent as a simulated\n * event of the given format, and the result converted back. A failed\n * invocation gets a `502` response, like API Gateway.\n */\nexport const createLambdaTarget = (\n handler: LambdaTestHandler,\n options: LambdaTargetOptions\n): ConformanceTarget => {\n const invoke = handler as unknown as (\n event: unknown,\n context: unknown\n ) => Promise<unknown>\n\n return createFetchTarget(async (request) => {\n const event = await toLambdaEvent(request, options.format, options.sourceIp)\n let result: unknown\n try {\n result = await invoke(event, lambdaContext())\n } catch {\n return Response.json(\n { message: 'Internal server error' },\n { status: 502 }\n )\n }\n return fromLambdaResult(result)\n }, options.close)\n}\n","import {\n DEFAULT_BODY_LIMIT,\n createApp,\n createRouteMatcher,\n fromWebRequest,\n readRequestBody,\n toWebResponse,\n type App,\n type AppOptions,\n type CompiledRoute,\n type DotpressRuntime,\n type HttpAdapter,\n type NormalizedResponse,\n type RouteMatcher,\n} from '@dotpress/core'\nimport { createMemoryLogger, type MemoryLogger } from './logger.js'\nimport { toRequest } from './request.js'\n\nexport interface MemoryAdapterOptions {\n /** Maximum request body size, in bytes. Defaults to 1 MiB. */\n bodyLimit?: number\n /** Client IP address given to requests. Defaults to `127.0.0.1`. */\n ip?: string\n}\n\nexport interface MemoryAdapter extends HttpAdapter<{\n readonly routes: readonly CompiledRoute[]\n}> {\n /** Handles a Web `Request` in memory, without opening a port. */\n fetch(request: Request): Promise<Response>\n}\n\n/**\n * HTTP adapter running requests in memory, for tests. It follows the\n * adapter contract like any other adapter, and passes the conformance\n * suite.\n */\nexport const memoryAdapter = (\n options: MemoryAdapterOptions = {}\n): MemoryAdapter => {\n const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT\n let routes: readonly CompiledRoute[] = []\n let matcher: RouteMatcher | undefined\n let runtime: DotpressRuntime | undefined\n\n const handle = async (request: Request): Promise<NormalizedResponse> => {\n if (!matcher || !runtime) {\n throw new Error('The memory adapter is not mounted: call createApp().')\n }\n\n const normalized = fromWebRequest(request, {\n ip: options.ip ?? '127.0.0.1',\n })\n\n const match = matcher.match(request.method, normalized.path)\n if (!match) {\n await request.body?.cancel()\n return runtime.handleNotFound(normalized)\n }\n\n try {\n const { body, files, rawBody } = await readRequestBody(\n request,\n match.route.definition.bodyLimit ?? bodyLimit\n )\n normalized.body = body\n normalized.files = files\n normalized.rawBody = rawBody\n } catch (error) {\n return runtime.handleError(error, normalized)\n }\n\n normalized.params = match.params\n return match.route.execute(normalized)\n }\n\n return {\n name: 'memory',\n mount(compiledRoutes, dotpressRuntime) {\n if (runtime) throw new Error('The memory adapter is already mounted.')\n routes = compiledRoutes\n matcher = createRouteMatcher(compiledRoutes)\n runtime = dotpressRuntime\n },\n native: () => ({ routes }),\n async fetch(request) {\n return toWebResponse(await handle(request), request.method)\n },\n }\n}\n\nexport interface TestAppOptions extends Omit<AppOptions, 'adapter' | 'logger'> {\n /** Options of the memory adapter. */\n adapter?: MemoryAdapterOptions\n /** Defaults to a new memory logger. */\n logger?: MemoryLogger\n}\n\nexport interface TestApp {\n readonly app: App<{ readonly routes: readonly CompiledRoute[] }>\n readonly adapter: MemoryAdapter\n /** Logger of the app, recording the log entries. */\n readonly logger: MemoryLogger\n /**\n * Sends a request to the app, like `fetch()`. `path` is relative to\n * `http://localhost`.\n */\n fetch(path: string, init?: RequestInit): Promise<Response>\n close(): Promise<void>\n}\n\n/**\n * Creates an app on the memory adapter.\n *\n * @example\n * const { fetch } = await createTestApp({ routers: [router], plugins: [authPlugin(auth)] })\n * const res = await fetch('/auth/me', { headers: { authorization: `Bearer ${token}` } })\n * expect(res.status).toBe(200)\n */\nexport const createTestApp = async (\n options: TestAppOptions = {}\n): Promise<TestApp> => {\n const { adapter: adapterOptions, ...appOptions } = options\n const logger = appOptions.logger ?? createMemoryLogger()\n const adapter = memoryAdapter(adapterOptions)\n const app = await createApp({ ...appOptions, logger, adapter })\n\n return {\n app,\n adapter,\n logger,\n fetch: async (path, init) => adapter.fetch(await toRequest(path, init)),\n close: () => app.close(),\n }\n}\n","import { assertCount, checkTimes, matches, show } from './assertions.js'\n\n/** An emission, as recorded by `createFakeEventBus()` of `@dotpress/events`. */\nexport interface Emission {\n readonly emissionId: string\n readonly event: string\n readonly payload: unknown\n readonly delay: number | undefined\n readonly metadata: Readonly<Record<string, string>>\n readonly emittedAt: Date\n}\n\n/** The fields of an envelope read here (`Envelope` of `@dotpress/events`). */\ninterface EnvelopeLike {\n readonly emissionId: string\n readonly event: string\n readonly payload: unknown\n readonly metadata: Readonly<Record<string, string>>\n readonly emittedAt: string\n}\n\n/**\n * Where emissions are read: a fake event bus (`createFakeEventBus()`), or a\n * memory transport (`createMemoryTransport()`), which only sees the events\n * that have listeners on it.\n */\nexport type EmissionSource<E extends Emission = Emission> =\n | { readonly emitted: readonly E[] }\n | { readonly sent: readonly EnvelopeLike[] }\n\n/** Options of `expectEventEmitted()`. */\nexport interface ExpectEventOptions {\n /** Exact number of matching emissions. Defaults to at least one. */\n times?: number | undefined\n /** Metadata of the emission, matched partially. */\n metadata?: Readonly<Record<string, unknown>> | undefined\n /** Delay of the emission, in seconds (fake event bus only). */\n delay?: number | undefined\n}\n\nconst OPTIONS = ['times', 'metadata', 'delay']\n\n/** Emissions of a source, oldest first. */\nconst emissionsOf = (label: string, source: unknown): Emission[] => {\n if (typeof source === 'object' && source !== null) {\n if ('emitted' in source && Array.isArray(source.emitted)) {\n return source.emitted as Emission[]\n }\n if ('sent' in source && Array.isArray(source.sent)) {\n // One envelope per listener: one emission per emissionId (the\n // envelopes of an emission share its fields).\n const emissions = new Map<string, Emission>()\n for (const envelope of source.sent as EnvelopeLike[]) {\n emissions.set(envelope.emissionId, {\n emissionId: envelope.emissionId,\n event: envelope.event,\n payload: envelope.payload,\n delay: undefined,\n metadata: envelope.metadata,\n emittedAt: new Date(envelope.emittedAt),\n })\n }\n return [...emissions.values()]\n }\n }\n throw new TypeError(\n `${label}: expected a fake event bus (createFakeEventBus()) or a memory transport (createMemoryTransport()).`\n )\n}\n\nconst nameOf = (label: string, event: unknown): string => {\n const name =\n typeof event === 'string'\n ? event\n : (event as { name?: unknown } | null)?.name\n if (typeof name !== 'string' || name === '') {\n throw new TypeError(\n `${label}: expected an event created by defineEvent(), or its name.`\n )\n }\n return name\n}\n\nconst find = (\n label: string,\n source: unknown,\n event: unknown,\n payload: unknown,\n options: unknown\n) => {\n const name = nameOf(label, event)\n if (\n options !== undefined &&\n (typeof options !== 'object' || options === null)\n ) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n const values = (options ?? {}) as ExpectEventOptions\n for (const key of Object.keys(values)) {\n if (!OPTIONS.includes(key)) {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n checkTimes(label, values.times)\n const emissions = emissionsOf(label, source)\n if ('delay' in values && !('emitted' in (source as object))) {\n throw new TypeError(\n `${label}: delay is only recorded by a fake event bus (createFakeEventBus()).`\n )\n }\n const found = emissions.filter(\n (emission) =>\n emission.event === name &&\n (payload === undefined || matches(emission.payload, payload)) &&\n (values.metadata === undefined ||\n matches(emission.metadata, values.metadata)) &&\n (!('delay' in values) || emission.delay === values.delay)\n )\n const details = [\n payload === undefined ? '' : ` with payload ${show(payload)}`,\n values.metadata === undefined\n ? ''\n : ` and metadata ${show(values.metadata)}`,\n 'delay' in values ? ` and delay ${String(values.delay)}` : '',\n ].join('')\n const recorded =\n emissions.length === 0\n ? 'No event was emitted.'\n : `Emitted events:\\n${emissions\n .map(\n (emission) =>\n ` - ${emission.event} ${show(emission.payload)}${emission.delay === undefined ? '' : ` (delay ${emission.delay})`}`\n )\n .join('\\n')}`\n return {\n found,\n description: {\n some: `event \"${name}\" emitted${details}`,\n none: `no event \"${name}\" emitted${details}`,\n },\n recorded,\n times: values.times,\n }\n}\n\n/**\n * Asserts that an event was emitted, at least once or exactly `times`\n * times, with a payload (and metadata) matching the expected values:\n * objects are matched partially, and asymmetric matchers are accepted.\n * Without payload (or with `undefined`), any payload matches.\n * Returns the last matching emission.\n *\n * ```ts\n * const events = createFakeEventBus()\n * await signUp(events, 'ada@example.com')\n * expectEventEmitted(events, userCreated, { email: 'ada@example.com' })\n * ```\n */\nexport function expectEventEmitted<E extends Emission>(\n source: EmissionSource<E>,\n event: { readonly name: string } | string,\n ...rest: [payload?: unknown, options?: ExpectEventOptions]\n): E {\n const label = 'expectEventEmitted()'\n const [payload, options] = rest\n const { found, description, recorded, times } = find(\n label,\n source,\n event,\n payload,\n options\n )\n if (times === 0) {\n throw new TypeError(\n `${label}: times must be at least 1 (use expectEventNotEmitted()).`\n )\n }\n assertCount(description, found.length, times, recorded)\n return found.at(-1) as E\n}\n\n/**\n * Asserts that an event was not emitted (with this payload, if given).\n *\n * ```ts\n * expectEventNotEmitted(events, userCreated)\n * ```\n */\nexport function expectEventNotEmitted(\n source: EmissionSource,\n event: { readonly name: string } | string,\n ...rest: [payload?: unknown]\n): void {\n const label = 'expectEventNotEmitted()'\n const { found, description, recorded } = find(\n label,\n source,\n event,\n rest[0],\n undefined\n )\n assertCount(description, found.length, 0, recorded)\n}\n","import { assertCount, checkTimes, matches, show } from './assertions.js'\n\ninterface Address {\n readonly name?: string\n readonly address: string\n}\n\n/** The fields of a message read here (`SentMail` of `@dotpress/mailer`). */\nexport interface SentMail {\n readonly from: Address\n readonly to: readonly Address[]\n readonly cc: readonly Address[]\n readonly bcc: readonly Address[]\n readonly replyTo: readonly Address[]\n readonly subject: string\n readonly template: string | undefined\n}\n\n/** Where messages are read: `createMemoryProvider()` of `@dotpress/mailer`. */\nexport interface MailOutbox<M extends SentMail = SentMail> {\n readonly sent: readonly M[]\n}\n\n/** Recipients: every expected address must be among them (any case). */\ntype ExpectedAddresses = string | readonly string[]\n\n/**\n * Expected properties of a message. Recipients and `from` are compared by\n * address; the other properties are matched like `toMatchObject()`\n * (asymmetric matchers accepted: `expect.stringContaining()`…).\n */\nexport interface MailCriteria {\n to?: ExpectedAddresses | undefined\n cc?: ExpectedAddresses | undefined\n bcc?: ExpectedAddresses | undefined\n replyTo?: ExpectedAddresses | undefined\n from?: string | undefined\n /** The template, or its name. */\n template?: string | { readonly name: string } | undefined\n locale?: unknown\n subject?: unknown\n html?: unknown\n text?: unknown\n tags?: unknown\n metadata?: unknown\n headers?: unknown\n attachments?: unknown\n}\n\n/** Options of `expectMailSent()`. */\nexport interface ExpectMailOptions {\n /** Exact number of matching messages. Defaults to at least one. */\n times?: number | undefined\n}\n\nconst ADDRESS_KEYS = ['to', 'cc', 'bcc', 'replyTo'] as const\nconst KEYS = new Set([\n ...ADDRESS_KEYS,\n 'from',\n 'template',\n 'locale',\n 'subject',\n 'html',\n 'text',\n 'tags',\n 'metadata',\n 'headers',\n 'attachments',\n])\n\nconst lower = (address: string) => address.trim().toLowerCase()\n\nconst checkAddresses = (label: string, key: string, value: unknown) => {\n const list = typeof value === 'string' ? [value] : value\n if (\n !Array.isArray(list) ||\n list.length === 0 ||\n !list.every((item) => typeof item === 'string' && item.trim() !== '')\n ) {\n throw new TypeError(\n `${label}: ${key} must be an email address, or a non-empty array of them.`\n )\n }\n return (list as string[]).map(lower)\n}\n\n/** A predicate for the criteria; checks them once. */\nconst predicateOf = (label: string, criteria: unknown) => {\n if (typeof criteria !== 'object' || criteria === null) {\n throw new TypeError(`${label}: expected an object of criteria.`)\n }\n const values = criteria as Record<string, unknown>\n for (const key of Object.keys(values)) {\n if (!KEYS.has(key)) {\n throw new TypeError(`${label}: unknown criterion \"${key}\".`)\n }\n }\n const addresses = ADDRESS_KEYS.flatMap((key) =>\n values[key] === undefined\n ? []\n : [{ key, expected: checkAddresses(label, key, values[key]) }]\n )\n const { from, template } = values\n if (from !== undefined && (typeof from !== 'string' || from.trim() === '')) {\n throw new TypeError(`${label}: from must be an email address.`)\n }\n const templateName =\n template === undefined\n ? undefined\n : typeof template === 'string'\n ? template\n : (template as { name?: unknown } | null)?.name\n if (template !== undefined && typeof templateName !== 'string') {\n throw new TypeError(`${label}: template must be a template, or its name.`)\n }\n const others = Object.entries(values).filter(\n ([key, value]) =>\n value !== undefined &&\n !(ADDRESS_KEYS as readonly string[]).includes(key) &&\n key !== 'from' &&\n key !== 'template'\n )\n\n return (message: SentMail) =>\n addresses.every(({ key, expected }) => {\n const actual = (message[key] as readonly Address[]).map(({ address }) =>\n lower(address)\n )\n return expected.every((address) => actual.includes(address))\n }) &&\n (from === undefined ||\n lower(message.from.address) === lower(from as string)) &&\n (templateName === undefined || message.template === templateName) &&\n others.every(([key, value]) =>\n matches((message as unknown as Record<string, unknown>)[key], value)\n )\n}\n\nconst outboxOf = (label: string, outbox: unknown): readonly SentMail[] => {\n const sent = (outbox as MailOutbox | null)?.sent\n if (!Array.isArray(sent)) {\n throw new TypeError(\n `${label}: expected the memory provider of the mailer (createMemoryProvider()).`\n )\n }\n return sent as readonly SentMail[]\n}\n\nconst recipients = (addresses: readonly Address[]) =>\n addresses.map(({ address }) => address).join(', ')\n\nconst recordedOf = (messages: readonly SentMail[]) =>\n messages.length === 0\n ? 'No message was sent.'\n : `Sent messages:\\n${messages\n .map(\n (message) =>\n ` - to ${recipients(message.to)}: ${show(message.subject)}${message.template === undefined ? '' : ` (template ${message.template})`}`\n )\n .join('\\n')}`\n\nconst describeCriteria = (criteria: object) => {\n const shown = Object.keys(criteria).length === 0 ? '' : ` ${show(criteria)}`\n return {\n some: `a message${shown} sent`,\n none: `no message${shown} sent`,\n }\n}\n\n/**\n * Asserts that a message matching the criteria was sent, at least once or\n * exactly `times` times. Returns the last matching message.\n *\n * ```ts\n * const outbox = createMemoryProvider()\n * const mailer = createMailer({ provider: outbox, from: 'noreply@example.com' })\n * await signUp(mailer, 'ada@example.com')\n * const mail = expectMailSent(outbox, { to: 'ada@example.com', template: welcomeMail })\n * expect(mail.html).toContain('https://example.com/confirm')\n * ```\n */\nexport const expectMailSent = <M extends SentMail>(\n outbox: MailOutbox<M>,\n criteria: MailCriteria = {},\n options: ExpectMailOptions = {}\n): M => {\n const label = 'expectMailSent()'\n const messages = outboxOf(label, outbox)\n const matching = predicateOf(label, criteria)\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (key !== 'times') {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n checkTimes(label, options.times)\n if (options.times === 0) {\n throw new TypeError(\n `${label}: times must be at least 1 (use expectNoMailSent()).`\n )\n }\n const found = messages.filter(matching)\n assertCount(\n describeCriteria(criteria),\n found.length,\n options.times,\n recordedOf(messages)\n )\n return found.at(-1) as M\n}\n\n/**\n * Asserts that no message (matching the criteria, if given) was sent.\n *\n * ```ts\n * expectNoMailSent(outbox, { to: 'ada@example.com' })\n * ```\n */\nexport const expectNoMailSent = (\n outbox: MailOutbox,\n criteria: MailCriteria = {}\n): void => {\n const label = 'expectNoMailSent()'\n const messages = outboxOf(label, outbox)\n const found = messages.filter(predicateOf(label, criteria))\n assertCount(describeCriteria(criteria), found.length, 0, recordedOf(messages))\n}\n"],"mappings":"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","names":["json","expect","fetch","expect","file","text","DEFAULT_BODY_LIMIT","DEFAULT_BODY_LIMIT"]}
|
|
1
|
+
{"version":3,"sources":["../src/assertions.ts","../src/lambda.ts","../src/memory.ts","../src/events.ts","../src/mail.ts"],"sourcesContent":["import { expect } from 'vitest'\n\n/** Error of a failed assertion, shown by the test runner. */\nexport class ExpectationError extends Error {\n override name = 'AssertionError'\n}\n\n/**\n * Whether a value matches an expected value: objects partially (like\n * `toMatchObject`), other values exactly (like `toEqual`); asymmetric\n * matchers (`expect.any()`, `expect.stringContaining()`…) are accepted.\n */\nexport const matches = (actual: unknown, expected: unknown): boolean => {\n try {\n if (\n typeof expected === 'object' &&\n expected !== null &&\n typeof actual === 'object' &&\n actual !== null\n ) {\n expect(actual).toMatchObject(expected)\n } else {\n expect(actual).toEqual(expected)\n }\n return true\n } catch {\n return false\n }\n}\n\n/** Short JSON of a value, for messages. */\nexport const show = (value: unknown): string => {\n let json: string | undefined\n try {\n // Asymmetric matchers are shown as in vitest: Any<String>.\n json = JSON.stringify(value, (_key, item: unknown) =>\n typeof item === 'object' && item !== null && 'asymmetricMatch' in item\n ? ((\n item as { toAsymmetricMatcher?: () => string }\n ).toAsymmetricMatcher?.() ?? String(item))\n : item\n )\n } catch {\n json = undefined\n }\n json ??= String(value)\n return json.length > 300 ? `${json.slice(0, 297)}...` : json\n}\n\n/** Checks the `times` option of an assertion. */\nexport const checkTimes = (label: string, times: unknown): void => {\n if (\n times !== undefined &&\n (!Number.isInteger(times) || (times as number) < 0)\n ) {\n throw new TypeError(`${label}: times must be a non-negative integer.`)\n }\n}\n\n/**\n * Throws unless `found` is `times`, or at least 1 without `times`.\n * `description` names what was expected, with the phrase for none;\n * `recorded` lists what was found.\n */\nexport const assertCount = (\n description: { readonly some: string; readonly none: string },\n found: number,\n times: number | undefined,\n recorded: string\n): void => {\n if (times === undefined ? found > 0 : found === times) return\n const expected =\n times === undefined\n ? description.some\n : times === 0\n ? description.none\n : `${description.some} ${times === 1 ? 'once' : `${times} times`}`\n throw new ExpectationError(\n `Expected ${expected}, found ${found}.\\n${recorded}`\n )\n}\n","import { closeOf, createFetchTarget, type TestTarget } from './target.js'\n\n/**\n * Lambda event formats: API Gateway REST API (payload v1), HTTP API and\n * Function URLs (payload v2), Application Load Balancer (with multi-value\n * headers, required for repeated query keys and several cookies).\n */\nexport type LambdaEventFormat = 'v1' | 'v2' | 'alb'\n\nexport interface LambdaTargetOptions {\n format: LambdaEventFormat\n /** Stops the application. Defaults to doing nothing. */\n close?: () => Promise<void>\n /** Client IP address of the events. Defaults to `127.0.0.1`. */\n sourceIp?: string\n}\n\n/** Lambda handler under test (`createLambdaHandler()` of an adapter). */\nexport type LambdaTestHandler = (\n event: never,\n context: never\n) => Promise<unknown>\n\ninterface LambdaResult {\n statusCode: number\n headers?: Record<string, string | string[]>\n multiValueHeaders?: Record<string, string[]>\n cookies?: string[]\n body?: string\n isBase64Encoded?: boolean\n}\n\nconst CHUNK = 0x8000\n\nconst encodeBase64 = (bytes: Uint8Array) => {\n let binary = ''\n for (let index = 0; index < bytes.length; index += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(index, index + CHUNK))\n }\n return btoa(binary)\n}\n\nconst decodeBase64 = (text: string) =>\n Uint8Array.from(atob(text), (char) => char.charCodeAt(0))\n\nconst TEXT_TYPES =\n /^\\s*(text\\/|application\\/(json|x-www-form-urlencoded)\\b|[^;]*\\+json\\b)/i\n\n/** Body of the event: text when possible, else base64, like API Gateway. */\nconst eventBody = (bytes: Uint8Array, contentType: string | null) => {\n if (bytes.byteLength === 0) return { body: null, isBase64Encoded: false }\n if (contentType && TEXT_TYPES.test(contentType)) {\n try {\n const body = new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n return { body, isBase64Encoded: false }\n } catch {\n // Invalid UTF-8: sent in base64.\n }\n }\n return { body: encodeBase64(bytes), isBase64Encoded: true }\n}\n\n/** Query parameters as sent (ALB does not decode them). */\nconst rawQuery = (search: string) => {\n const result: Record<string, string[]> = {}\n for (const pair of search.slice(1).split('&')) {\n if (!pair) continue\n const index = pair.indexOf('=')\n const key = index === -1 ? pair : pair.slice(0, index)\n const value = index === -1 ? '' : pair.slice(index + 1)\n ;(result[key] ??= []).push(value)\n }\n return result\n}\n\nconst lastValues = (values: Record<string, string[]>) =>\n Object.fromEntries(\n Object.entries(values).map(([key, list]) => [key, list.at(-1)!])\n )\n\nconst ACCOUNT = '123456789012'\n\n/** Creates the Lambda event of a request. */\nexport const toLambdaEvent = async (\n request: Request,\n format: LambdaEventFormat,\n sourceIp = '127.0.0.1'\n): Promise<Record<string, unknown>> => {\n const url = new URL(request.url)\n const headers: Record<string, string> = { host: url.host }\n for (const [name, value] of request.headers) headers[name] = value\n const { body, isBase64Encoded } = eventBody(\n new Uint8Array(await request.arrayBuffer()),\n request.headers.get('content-type')\n )\n const requestId = crypto.randomUUID()\n const userAgent = headers['user-agent'] ?? ''\n\n if (format === 'v2') {\n const { cookie, ...rest } = headers\n return {\n version: '2.0',\n routeKey: '$default',\n rawPath: url.pathname,\n rawQueryString: url.search.slice(1),\n ...(cookie ? { cookies: cookie.split(/;\\s*/) } : {}),\n headers: rest,\n requestContext: {\n accountId: ACCOUNT,\n apiId: 'api',\n domainName: url.host,\n domainPrefix: url.hostname.split('.')[0],\n http: {\n method: request.method,\n path: url.pathname,\n protocol: 'HTTP/1.1',\n sourceIp,\n userAgent,\n },\n requestId,\n routeKey: '$default',\n stage: '$default',\n time: new Date().toISOString(),\n timeEpoch: Date.now(),\n },\n ...(body === null ? {} : { body }),\n isBase64Encoded,\n }\n }\n\n const query: Record<string, string[]> = {}\n for (const [key, value] of url.searchParams) (query[key] ??= []).push(value)\n const multiValueHeaders = Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [name, [value]])\n )\n\n if (format === 'alb') {\n return {\n requestContext: {\n elb: {\n targetGroupArn: `arn:aws:elasticloadbalancing:us-east-1:${ACCOUNT}:targetgroup/dotpress/0123456789abcdef`,\n },\n },\n httpMethod: request.method,\n path: url.pathname,\n multiValueQueryStringParameters: rawQuery(url.search),\n // The load balancer appends the client address.\n multiValueHeaders: {\n ...multiValueHeaders,\n 'x-forwarded-for': [\n headers['x-forwarded-for']\n ? `${headers['x-forwarded-for']}, ${sourceIp}`\n : sourceIp,\n ],\n 'x-forwarded-proto': [url.protocol.slice(0, -1)],\n },\n body: body ?? '',\n isBase64Encoded,\n }\n }\n\n const hasQuery = Object.keys(query).length > 0\n return {\n resource: '/{proxy+}',\n path: url.pathname,\n httpMethod: request.method,\n headers,\n multiValueHeaders,\n queryStringParameters: hasQuery ? lastValues(query) : null,\n multiValueQueryStringParameters: hasQuery ? query : null,\n pathParameters: { proxy: url.pathname.slice(1) },\n stageVariables: null,\n requestContext: {\n accountId: ACCOUNT,\n apiId: 'api',\n domainName: url.host,\n httpMethod: request.method,\n identity: { sourceIp, userAgent },\n path: url.pathname,\n protocol: 'HTTP/1.1',\n requestId,\n requestTimeEpoch: Date.now(),\n resourcePath: '/{proxy+}',\n stage: '$default',\n },\n body,\n isBase64Encoded,\n }\n}\n\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304])\n\n/** Converts the result of a Lambda handler into a Web response. */\nexport const fromLambdaResult = (result: unknown): Response => {\n const { statusCode, headers, multiValueHeaders, cookies, body, ...rest } =\n result as LambdaResult\n\n const responseHeaders = new Headers()\n for (const [name, value] of Object.entries(headers ?? {})) {\n for (const item of Array.isArray(value) ? value : [value]) {\n responseHeaders.append(name, item)\n }\n }\n for (const [name, values] of Object.entries(multiValueHeaders ?? {})) {\n for (const value of values) responseHeaders.append(name, value)\n }\n for (const cookie of cookies ?? []) {\n responseHeaders.append('set-cookie', cookie)\n }\n\n const content =\n !body || NULL_BODY_STATUSES.has(statusCode)\n ? null\n : rest.isBase64Encoded\n ? decodeBase64(body)\n : body\n\n return new Response(content as BodyInit | null, {\n status: statusCode,\n headers: responseHeaders,\n })\n}\n\nconst lambdaContext = () => ({\n awsRequestId: crypto.randomUUID(),\n functionName: 'dotpress-test',\n functionVersion: '$LATEST',\n invokedFunctionArn: `arn:aws:lambda:us-east-1:${ACCOUNT}:function:dotpress-test`,\n memoryLimitInMB: '128',\n logGroupName: '/aws/lambda/dotpress-test',\n logStreamName: 'test',\n callbackWaitsForEmptyEventLoop: true,\n getRemainingTimeInMillis: () => 30_000,\n})\n\n/**\n * Target for AWS Lambda handlers: each request is sent as a simulated\n * event of the given format, and the result converted back. A failed\n * invocation gets a `502` response, like API Gateway.\n */\nexport const createLambdaTarget = (\n handler: LambdaTestHandler,\n options: LambdaTargetOptions\n): TestTarget => {\n const close = closeOf('createLambdaTarget()', options, [\n 'format',\n 'close',\n 'sourceIp',\n ] satisfies (keyof LambdaTargetOptions)[])\n const invoke = handler as unknown as (\n event: unknown,\n context: unknown\n ) => Promise<unknown>\n\n return createFetchTarget(\n async (request) => {\n const event = await toLambdaEvent(\n request,\n options.format,\n options.sourceIp\n )\n let result: unknown\n try {\n result = await invoke(event, lambdaContext())\n } catch {\n return Response.json(\n { message: 'Internal server error' },\n { status: 502 }\n )\n }\n return fromLambdaResult(result)\n },\n { close }\n )\n}\n","import {\n createApp,\n type App,\n type AppOptions,\n type HttpAdapter,\n type NormalizedResponse,\n} from '@dotpress/core'\nimport {\n DEFAULT_BODY_LIMIT,\n createRouteMatcher,\n fromWebRequest,\n readRequestBody,\n toWebResponse,\n type CompiledRoute,\n type DotpressRuntime,\n type RouteMatcher,\n} from '@dotpress/core/adapter'\nimport { createMemoryLogger, type MemoryLogger } from './logger.js'\nimport { toRequest } from './request.js'\n\nexport interface MemoryAdapterOptions {\n /** Maximum request body size, in bytes. Defaults to 1 MiB. */\n bodyLimit?: number\n /** Client IP address given to requests. Defaults to `127.0.0.1`. */\n ip?: string\n}\n\nexport interface MemoryAdapter extends HttpAdapter<{\n readonly routes: readonly CompiledRoute[]\n}> {\n /** Handles a Web `Request` in memory, without opening a port. */\n fetch(request: Request): Promise<Response>\n}\n\n/**\n * HTTP adapter running requests in memory, for tests. It follows the\n * adapter contract like any other adapter, and passes the conformance\n * suite.\n */\nexport const memoryAdapter = (\n options: MemoryAdapterOptions = {}\n): MemoryAdapter => {\n const bodyLimit = options.bodyLimit ?? DEFAULT_BODY_LIMIT\n let routes: readonly CompiledRoute[] = []\n let matcher: RouteMatcher | undefined\n let runtime: DotpressRuntime | undefined\n\n const handle = async (request: Request): Promise<NormalizedResponse> => {\n if (!matcher || !runtime) {\n throw new Error('The memory adapter is not mounted: call createApp().')\n }\n\n const normalized = fromWebRequest(request, {\n ip: options.ip ?? '127.0.0.1',\n })\n\n const match = matcher.match(request.method, normalized.path)\n if (!match) {\n await request.body?.cancel()\n return runtime.handleNotFound(normalized)\n }\n\n try {\n const { body, files, rawBody } = await readRequestBody(\n request,\n match.route.definition.bodyLimit ?? bodyLimit\n )\n normalized.body = body\n normalized.files = files\n normalized.rawBody = rawBody\n } catch (error) {\n return runtime.handleError(error, normalized)\n }\n\n normalized.params = match.params\n return match.route.execute(normalized)\n }\n\n return {\n name: 'memory',\n mount(compiledRoutes, dotpressRuntime) {\n if (runtime) throw new Error('The memory adapter is already mounted.')\n routes = compiledRoutes\n matcher = createRouteMatcher(compiledRoutes)\n runtime = dotpressRuntime\n },\n native: () => ({ routes }),\n async fetch(request) {\n return toWebResponse(await handle(request), request.method)\n },\n }\n}\n\nexport interface TestAppOptions extends Omit<AppOptions, 'adapter' | 'logger'> {\n /** Options of the memory adapter. */\n adapter?: MemoryAdapterOptions\n /** Defaults to a new memory logger. */\n logger?: MemoryLogger\n}\n\nexport interface TestApp {\n readonly app: App<{ readonly routes: readonly CompiledRoute[] }>\n readonly adapter: MemoryAdapter\n /** Logger of the app, recording the log entries. */\n readonly logger: MemoryLogger\n /**\n * Sends a request to the app, like `fetch()`. `path` is relative to\n * `http://localhost`.\n */\n fetch(path: string, init?: RequestInit): Promise<Response>\n close(): Promise<void>\n}\n\n/**\n * Creates an app on the memory adapter.\n *\n * @example\n * const { fetch } = await createTestApp({ routers: [router], plugins: [authPlugin(auth)] })\n * const res = await fetch('/auth/me', { headers: { authorization: `Bearer ${token}` } })\n * expect(res.status).toBe(200)\n */\nexport const createTestApp = async (\n options: TestAppOptions = {}\n): Promise<TestApp> => {\n const { adapter: adapterOptions, ...appOptions } = options\n const logger = appOptions.logger ?? createMemoryLogger()\n const adapter = memoryAdapter(adapterOptions)\n const app = await createApp({ ...appOptions, logger, adapter })\n\n return {\n app,\n adapter,\n logger,\n fetch: async (path, init) => adapter.fetch(await toRequest(path, init)),\n close: () => app.close(),\n }\n}\n","import { assertCount, checkTimes, matches, show } from './assertions.js'\n\n/** An emission, as recorded by `createFakeEventBus()` of `@dotpress/events`. */\nexport interface Emission {\n readonly emissionId: string\n readonly event: string\n readonly payload: unknown\n readonly delay: number | undefined\n readonly metadata: Readonly<Record<string, string>>\n readonly emittedAt: Date\n}\n\n/** The fields of an envelope read here (`Envelope` of `@dotpress/events`). */\n/** An envelope of `@dotpress/events`, as a transport sends it. */\nexport interface EnvelopeLike {\n readonly emissionId: string\n readonly event: string\n readonly payload: unknown\n readonly metadata: Readonly<Record<string, string>>\n readonly emittedAt: string\n}\n\n/**\n * Where emissions are read: a fake event bus (`createFakeEventBus()`), or a\n * memory transport (`createMemoryTransport()`), which only sees the events\n * that have listeners on it.\n */\nexport type EmissionSource<E extends Emission = Emission> =\n | { readonly emitted: readonly E[] }\n | { readonly sent: readonly EnvelopeLike[] }\n\n/** Options of `expectEventEmitted()`. */\nexport interface ExpectEventOptions {\n /** Exact number of matching emissions. Defaults to at least one. */\n times?: number | undefined\n /** Metadata of the emission, matched partially. */\n metadata?: Readonly<Record<string, unknown>> | undefined\n /** Delay of the emission, in seconds (fake event bus only). */\n delay?: number | undefined\n}\n\nconst OPTIONS = ['times', 'metadata', 'delay']\n\n/** Emissions of a source, oldest first. */\nconst emissionsOf = (label: string, source: unknown): Emission[] => {\n if (typeof source === 'object' && source !== null) {\n if ('emitted' in source && Array.isArray(source.emitted)) {\n return source.emitted as Emission[]\n }\n if ('sent' in source && Array.isArray(source.sent)) {\n // One envelope per listener: one emission per emissionId (the\n // envelopes of an emission share its fields).\n const emissions = new Map<string, Emission>()\n for (const envelope of source.sent as EnvelopeLike[]) {\n emissions.set(envelope.emissionId, {\n emissionId: envelope.emissionId,\n event: envelope.event,\n payload: envelope.payload,\n delay: undefined,\n metadata: envelope.metadata,\n emittedAt: new Date(envelope.emittedAt),\n })\n }\n return [...emissions.values()]\n }\n }\n throw new TypeError(\n `${label}: expected a fake event bus (createFakeEventBus()) or a memory transport (createMemoryTransport()).`\n )\n}\n\nconst nameOf = (label: string, event: unknown): string => {\n const name =\n typeof event === 'string'\n ? event\n : (event as { name?: unknown } | null)?.name\n if (typeof name !== 'string' || name === '') {\n throw new TypeError(\n `${label}: expected an event created by defineEvent(), or its name.`\n )\n }\n return name\n}\n\nconst find = (\n label: string,\n source: unknown,\n event: unknown,\n payload: unknown,\n options: unknown\n) => {\n const name = nameOf(label, event)\n if (\n options !== undefined &&\n (typeof options !== 'object' || options === null)\n ) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n const values = (options ?? {}) as ExpectEventOptions\n for (const key of Object.keys(values)) {\n if (!OPTIONS.includes(key)) {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n checkTimes(label, values.times)\n const emissions = emissionsOf(label, source)\n if ('delay' in values && !('emitted' in (source as object))) {\n throw new TypeError(\n `${label}: delay is only recorded by a fake event bus (createFakeEventBus()).`\n )\n }\n const found = emissions.filter(\n (emission) =>\n emission.event === name &&\n (payload === undefined || matches(emission.payload, payload)) &&\n (values.metadata === undefined ||\n matches(emission.metadata, values.metadata)) &&\n (!('delay' in values) || emission.delay === values.delay)\n )\n const details = [\n payload === undefined ? '' : ` with payload ${show(payload)}`,\n values.metadata === undefined\n ? ''\n : ` and metadata ${show(values.metadata)}`,\n 'delay' in values ? ` and delay ${String(values.delay)}` : '',\n ].join('')\n const recorded =\n emissions.length === 0\n ? 'No event was emitted.'\n : `Emitted events:\\n${emissions\n .map(\n (emission) =>\n ` - ${emission.event} ${show(emission.payload)}${emission.delay === undefined ? '' : ` (delay ${emission.delay})`}`\n )\n .join('\\n')}`\n return {\n found,\n description: {\n some: `event \"${name}\" emitted${details}`,\n none: `no event \"${name}\" emitted${details}`,\n },\n recorded,\n times: values.times,\n }\n}\n\n/**\n * Asserts that an event was emitted, at least once or exactly `times`\n * times, with a payload (and metadata) matching the expected values:\n * objects are matched partially, and asymmetric matchers are accepted.\n * Without payload (or with `undefined`), any payload matches.\n * Returns the last matching emission.\n *\n * ```ts\n * const events = createFakeEventBus()\n * await signUp(events, 'ada@example.com')\n * expectEventEmitted(events, userCreated, { email: 'ada@example.com' })\n * ```\n */\nexport function expectEventEmitted<E extends Emission>(\n source: EmissionSource<E>,\n event: { readonly name: string } | string,\n ...rest: [payload?: unknown, options?: ExpectEventOptions]\n): E {\n const label = 'expectEventEmitted()'\n const [payload, options] = rest\n const { found, description, recorded, times } = find(\n label,\n source,\n event,\n payload,\n options\n )\n if (times === 0) {\n throw new TypeError(\n `${label}: times must be at least 1 (use expectEventNotEmitted()).`\n )\n }\n assertCount(description, found.length, times, recorded)\n return found.at(-1) as E\n}\n\n/**\n * Asserts that an event was not emitted (with this payload, if given).\n *\n * ```ts\n * expectEventNotEmitted(events, userCreated)\n * ```\n */\nexport function expectEventNotEmitted(\n source: EmissionSource,\n event: { readonly name: string } | string,\n ...rest: [payload?: unknown]\n): void {\n const label = 'expectEventNotEmitted()'\n const { found, description, recorded } = find(\n label,\n source,\n event,\n rest[0],\n undefined\n )\n assertCount(description, found.length, 0, recorded)\n}\n","import { assertCount, checkTimes, matches, show } from './assertions.js'\n\n/** An address of a sent mail. */\nexport interface SentMailAddress {\n readonly name?: string\n readonly address: string\n}\n\n/** The fields of a message read here (`SentMail` of `@dotpress/mailer`). */\nexport interface SentMail {\n readonly from: SentMailAddress\n readonly to: readonly SentMailAddress[]\n readonly cc: readonly SentMailAddress[]\n readonly bcc: readonly SentMailAddress[]\n readonly replyTo: readonly SentMailAddress[]\n readonly subject: string\n readonly template: string | undefined\n}\n\n/** Where messages are read: `createMemoryProvider()` of `@dotpress/mailer`. */\nexport interface MailOutbox<M extends SentMail = SentMail> {\n readonly sent: readonly M[]\n}\n\n/** Recipients: every expected address must be among them (any case). */\n/** Expected addresses: one, or several. */\nexport type ExpectedAddresses = string | readonly string[]\n\n/**\n * Expected properties of a message. Recipients and `from` are compared by\n * address; the other properties are matched like `toMatchObject()`\n * (asymmetric matchers accepted: `expect.stringContaining()`…).\n */\nexport interface MailCriteria {\n to?: ExpectedAddresses | undefined\n cc?: ExpectedAddresses | undefined\n bcc?: ExpectedAddresses | undefined\n replyTo?: ExpectedAddresses | undefined\n from?: string | undefined\n /** The template, or its name. */\n template?: string | { readonly name: string } | undefined\n locale?: unknown\n subject?: unknown\n html?: unknown\n text?: unknown\n tags?: unknown\n metadata?: unknown\n headers?: unknown\n attachments?: unknown\n}\n\n/** Options of `expectMailSent()`. */\nexport interface ExpectMailOptions {\n /** Exact number of matching messages. Defaults to at least one. */\n times?: number | undefined\n}\n\nconst ADDRESS_KEYS = ['to', 'cc', 'bcc', 'replyTo'] as const\nconst KEYS = new Set([\n ...ADDRESS_KEYS,\n 'from',\n 'template',\n 'locale',\n 'subject',\n 'html',\n 'text',\n 'tags',\n 'metadata',\n 'headers',\n 'attachments',\n])\n\nconst lower = (address: string) => address.trim().toLowerCase()\n\nconst checkAddresses = (label: string, key: string, value: unknown) => {\n const list = typeof value === 'string' ? [value] : value\n if (\n !Array.isArray(list) ||\n list.length === 0 ||\n !list.every((item) => typeof item === 'string' && item.trim() !== '')\n ) {\n throw new TypeError(\n `${label}: ${key} must be an email address, or a non-empty array of them.`\n )\n }\n return (list as string[]).map(lower)\n}\n\n/** A predicate for the criteria; checks them once. */\nconst predicateOf = (label: string, criteria: unknown) => {\n if (typeof criteria !== 'object' || criteria === null) {\n throw new TypeError(`${label}: expected an object of criteria.`)\n }\n const values = criteria as Record<string, unknown>\n for (const key of Object.keys(values)) {\n if (!KEYS.has(key)) {\n throw new TypeError(`${label}: unknown criterion \"${key}\".`)\n }\n }\n const addresses = ADDRESS_KEYS.flatMap((key) =>\n values[key] === undefined\n ? []\n : [{ key, expected: checkAddresses(label, key, values[key]) }]\n )\n const { from, template } = values\n if (from !== undefined && (typeof from !== 'string' || from.trim() === '')) {\n throw new TypeError(`${label}: from must be an email address.`)\n }\n const templateName =\n template === undefined\n ? undefined\n : typeof template === 'string'\n ? template\n : (template as { name?: unknown } | null)?.name\n if (template !== undefined && typeof templateName !== 'string') {\n throw new TypeError(`${label}: template must be a template, or its name.`)\n }\n const others = Object.entries(values).filter(\n ([key, value]) =>\n value !== undefined &&\n !(ADDRESS_KEYS as readonly string[]).includes(key) &&\n key !== 'from' &&\n key !== 'template'\n )\n\n return (message: SentMail) =>\n addresses.every(({ key, expected }) => {\n const actual = (message[key] as readonly SentMailAddress[]).map(\n ({ address }) => lower(address)\n )\n return expected.every((address) => actual.includes(address))\n }) &&\n (from === undefined ||\n lower(message.from.address) === lower(from as string)) &&\n (templateName === undefined || message.template === templateName) &&\n others.every(([key, value]) =>\n matches((message as unknown as Record<string, unknown>)[key], value)\n )\n}\n\nconst outboxOf = (label: string, outbox: unknown): readonly SentMail[] => {\n const sent = (outbox as MailOutbox | null)?.sent\n if (!Array.isArray(sent)) {\n throw new TypeError(\n `${label}: expected the memory provider of the mailer (createMemoryProvider()).`\n )\n }\n return sent as readonly SentMail[]\n}\n\nconst recipients = (addresses: readonly SentMailAddress[]) =>\n addresses.map(({ address }) => address).join(', ')\n\nconst recordedOf = (messages: readonly SentMail[]) =>\n messages.length === 0\n ? 'No message was sent.'\n : `Sent messages:\\n${messages\n .map(\n (message) =>\n ` - to ${recipients(message.to)}: ${show(message.subject)}${message.template === undefined ? '' : ` (template ${message.template})`}`\n )\n .join('\\n')}`\n\nconst describeCriteria = (criteria: object) => {\n const shown = Object.keys(criteria).length === 0 ? '' : ` ${show(criteria)}`\n return {\n some: `a message${shown} sent`,\n none: `no message${shown} sent`,\n }\n}\n\n/**\n * Asserts that a message matching the criteria was sent, at least once or\n * exactly `times` times. Returns the last matching message.\n *\n * ```ts\n * const outbox = createMemoryProvider()\n * const mailer = createMailer({ provider: outbox, from: 'noreply@example.com' })\n * await signUp(mailer, 'ada@example.com')\n * const mail = expectMailSent(outbox, { to: 'ada@example.com', template: welcomeMail })\n * expect(mail.html).toContain('https://example.com/confirm')\n * ```\n */\nexport const expectMailSent = <M extends SentMail>(\n outbox: MailOutbox<M>,\n criteria: MailCriteria = {},\n options: ExpectMailOptions = {}\n): M => {\n const label = 'expectMailSent()'\n const messages = outboxOf(label, outbox)\n const matching = predicateOf(label, criteria)\n if (typeof options !== 'object' || options === null) {\n throw new TypeError(`${label}: expected an object of options.`)\n }\n for (const key of Object.keys(options)) {\n if (key !== 'times') {\n throw new TypeError(`${label}: unknown option \"${key}\".`)\n }\n }\n checkTimes(label, options.times)\n if (options.times === 0) {\n throw new TypeError(\n `${label}: times must be at least 1 (use expectNoMailSent()).`\n )\n }\n const found = messages.filter(matching)\n assertCount(\n describeCriteria(criteria),\n found.length,\n options.times,\n recordedOf(messages)\n )\n return found.at(-1) as M\n}\n\n/**\n * Asserts that no message (matching the criteria, if given) was sent.\n *\n * ```ts\n * expectNoMailSent(outbox, { to: 'ada@example.com' })\n * ```\n */\nexport const expectNoMailSent = (\n outbox: MailOutbox,\n criteria: MailCriteria = {}\n): void => {\n const label = 'expectNoMailSent()'\n const messages = outboxOf(label, outbox)\n const found = messages.filter(predicateOf(label, criteria))\n assertCount(describeCriteria(criteria), found.length, 0, recordedOf(messages))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running application under test.
|
|
3
|
+
*/
|
|
4
|
+
interface TestTarget {
|
|
5
|
+
/** Sends a request to the application. `path` includes the query. */
|
|
6
|
+
fetch(path: string, init?: RequestInit): Promise<Response>;
|
|
7
|
+
/** Stops the application. */
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
interface TargetOptions {
|
|
11
|
+
/** Stops the application. Defaults to doing nothing. */
|
|
12
|
+
close?: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Target for adapters listening on an HTTP port.
|
|
16
|
+
*/
|
|
17
|
+
declare const createHttpTarget: (baseUrl: string, options?: TargetOptions) => TestTarget;
|
|
18
|
+
/**
|
|
19
|
+
* Target for adapters handling Web requests in memory (`fetch()` of the
|
|
20
|
+
* adapter, serverless handlers). `path` is relative to `http://localhost`.
|
|
21
|
+
*/
|
|
22
|
+
declare const createFetchTarget: (handle: (request: Request) => Promise<Response>, options?: TargetOptions) => TestTarget;
|
|
23
|
+
|
|
24
|
+
export { createHttpTarget as b, createFetchTarget as c };
|
|
25
|
+
export type { TestTarget as T, TargetOptions as a };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotpress/testing",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.5",
|
|
4
4
|
"description": "Testing helpers for Dotpress: test app, conformance suite, event and mail assertions",
|
|
5
5
|
"author": "Renaud Guyon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,17 +24,21 @@
|
|
|
24
24
|
".": {
|
|
25
25
|
"types": "./dist/index.d.ts",
|
|
26
26
|
"default": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./conformance": {
|
|
29
|
+
"types": "./dist/conformance.d.ts",
|
|
30
|
+
"default": "./dist/conformance.js"
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"engines": {
|
|
30
34
|
"node": ">=22.12"
|
|
31
35
|
},
|
|
32
36
|
"peerDependencies": {
|
|
33
|
-
"@dotpress/core": "^2.0.0-alpha.
|
|
37
|
+
"@dotpress/core": "^2.0.0-alpha.5",
|
|
34
38
|
"vitest": "^5.0.0"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
|
-
"@dotpress/core": "2.0.0-alpha.
|
|
41
|
+
"@dotpress/core": "2.0.0-alpha.5",
|
|
38
42
|
"zod": "^4.6.7"
|
|
39
43
|
},
|
|
40
44
|
"keywords": [
|