@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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/conformance.ts"],"sourcesContent":["import {\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 { DEFAULT_BODY_LIMIT } from '@dotpress/core/adapter'\nimport { describe, expect, it, vi } from 'vitest'\nimport { createMemoryLogger } from './logger.js'\nimport type { TestTarget } from './target.js'\n\nexport {\n createFetchTarget,\n createHttpTarget,\n type TargetOptions,\n type TestTarget,\n} from './target.js'\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<TestTarget>\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\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: TestTarget) => 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: () =>\n notFoundError('User not found', { data: { 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', { status: 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"],"mappings":"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","names":["file"]}
package/dist/index.d.ts CHANGED
@@ -1,50 +1,12 @@
1
- import { AppOptions, Logger, HttpAdapter, CompiledRoute, App } from '@dotpress/core';
1
+ import { T as TestTarget } from './target-BZC-J2SE.js';
2
+ import { Logger, HttpAdapter, App, AppOptions } from '@dotpress/core';
3
+ import { CompiledRoute } from '@dotpress/core/adapter';
2
4
 
3
5
  /** Error of a failed assertion, shown by the test runner. */
4
6
  declare class ExpectationError extends Error {
5
7
  name: string;
6
8
  }
7
9
 
8
- /**
9
- * Running application under test.
10
- */
11
- interface ConformanceTarget {
12
- /** Sends a request to the application. `path` includes the query. */
13
- fetch(path: string, init?: RequestInit): Promise<Response>;
14
- /** Stops the application. */
15
- close(): Promise<void>;
16
- }
17
- type ConformanceAppOptions = Omit<AppOptions, 'adapter'>;
18
- interface ConformanceSuiteOptions {
19
- /** Adapter name, used in test titles. */
20
- name: string;
21
- /**
22
- * Creates an app with the adapter under test and the given options, and
23
- * starts it. Called once per test.
24
- */
25
- setup(options: ConformanceAppOptions): Promise<ConformanceTarget>;
26
- /**
27
- * The adapter sends responses once complete (AWS Lambda without response
28
- * streaming). The scenarios that need incremental streaming or client
29
- * disconnects are skipped.
30
- */
31
- bufferedResponses?: boolean;
32
- }
33
- /**
34
- * Target for adapters listening on an HTTP port.
35
- */
36
- declare const createHttpTarget: (baseUrl: string, close: () => Promise<void>) => ConformanceTarget;
37
- /**
38
- * Target for adapters handling Web requests in memory (`fetch()` of the
39
- * adapter, serverless handlers). `path` is relative to `http://localhost`.
40
- */
41
- declare const createFetchTarget: (handle: (request: Request) => Promise<Response>, close: () => Promise<void>) => ConformanceTarget;
42
- /**
43
- * Registers the adapter conformance suite (vitest). An adapter is ready
44
- * when it passes every test.
45
- */
46
- declare const runConformanceSuite: (options: ConformanceSuiteOptions) => void;
47
-
48
10
  /**
49
11
  * Lambda event formats: API Gateway REST API (payload v1), HTTP API and
50
12
  * Function URLs (payload v2), Application Load Balancer (with multi-value
@@ -53,8 +15,8 @@ declare const runConformanceSuite: (options: ConformanceSuiteOptions) => void;
53
15
  type LambdaEventFormat = 'v1' | 'v2' | 'alb';
54
16
  interface LambdaTargetOptions {
55
17
  format: LambdaEventFormat;
56
- /** Stops the application. */
57
- close: () => Promise<void>;
18
+ /** Stops the application. Defaults to doing nothing. */
19
+ close?: () => Promise<void>;
58
20
  /** Client IP address of the events. Defaults to `127.0.0.1`. */
59
21
  sourceIp?: string;
60
22
  }
@@ -69,7 +31,7 @@ declare const fromLambdaResult: (result: unknown) => Response;
69
31
  * event of the given format, and the result converted back. A failed
70
32
  * invocation gets a `502` response, like API Gateway.
71
33
  */
72
- declare const createLambdaTarget: (handler: LambdaTestHandler, options: LambdaTargetOptions) => ConformanceTarget;
34
+ declare const createLambdaTarget: (handler: LambdaTestHandler, options: LambdaTargetOptions) => TestTarget;
73
35
 
74
36
  interface LogEntry {
75
37
  readonly level: 'debug' | 'info' | 'warn' | 'error';
@@ -142,6 +104,7 @@ interface Emission {
142
104
  readonly emittedAt: Date;
143
105
  }
144
106
  /** The fields of an envelope read here (`Envelope` of `@dotpress/events`). */
107
+ /** An envelope of `@dotpress/events`, as a transport sends it. */
145
108
  interface EnvelopeLike {
146
109
  readonly emissionId: string;
147
110
  readonly event: string;
@@ -195,17 +158,18 @@ declare function expectEventNotEmitted(source: EmissionSource, event: {
195
158
  readonly name: string;
196
159
  } | string, ...rest: [payload?: unknown]): void;
197
160
 
198
- interface Address {
161
+ /** An address of a sent mail. */
162
+ interface SentMailAddress {
199
163
  readonly name?: string;
200
164
  readonly address: string;
201
165
  }
202
166
  /** The fields of a message read here (`SentMail` of `@dotpress/mailer`). */
203
167
  interface SentMail {
204
- readonly from: Address;
205
- readonly to: readonly Address[];
206
- readonly cc: readonly Address[];
207
- readonly bcc: readonly Address[];
208
- readonly replyTo: readonly Address[];
168
+ readonly from: SentMailAddress;
169
+ readonly to: readonly SentMailAddress[];
170
+ readonly cc: readonly SentMailAddress[];
171
+ readonly bcc: readonly SentMailAddress[];
172
+ readonly replyTo: readonly SentMailAddress[];
209
173
  readonly subject: string;
210
174
  readonly template: string | undefined;
211
175
  }
@@ -214,6 +178,7 @@ interface MailOutbox<M extends SentMail = SentMail> {
214
178
  readonly sent: readonly M[];
215
179
  }
216
180
  /** Recipients: every expected address must be among them (any case). */
181
+ /** Expected addresses: one, or several. */
217
182
  type ExpectedAddresses = string | readonly string[];
218
183
  /**
219
184
  * Expected properties of a message. Recipients and `from` are compared by
@@ -266,5 +231,5 @@ declare const expectMailSent: <M extends SentMail>(outbox: MailOutbox<M>, criter
266
231
  */
267
232
  declare const expectNoMailSent: (outbox: MailOutbox, criteria?: MailCriteria) => void;
268
233
 
269
- export { ExpectationError, createFetchTarget, createHttpTarget, createLambdaTarget, createMemoryLogger, createTestApp, expectEventEmitted, expectEventNotEmitted, expectMailSent, expectNoMailSent, fromLambdaResult, memoryAdapter, runConformanceSuite, toLambdaEvent };
270
- export type { ConformanceAppOptions, ConformanceSuiteOptions, ConformanceTarget, Emission, EmissionSource, ExpectEventOptions, ExpectMailOptions, LambdaEventFormat, LambdaTargetOptions, LambdaTestHandler, LogEntry, MailCriteria, MailOutbox, MemoryAdapter, MemoryAdapterOptions, MemoryLogger, SentMail, TestApp, TestAppOptions };
234
+ export { ExpectationError, TestTarget, createLambdaTarget, createMemoryLogger, createTestApp, expectEventEmitted, expectEventNotEmitted, expectMailSent, expectNoMailSent, fromLambdaResult, memoryAdapter, toLambdaEvent };
235
+ export type { Emission, EmissionSource, EnvelopeLike, ExpectEventOptions, ExpectMailOptions, ExpectedAddresses, LambdaEventFormat, LambdaTargetOptions, LambdaTestHandler, LogEntry, MailCriteria, MailOutbox, MemoryAdapter, MemoryAdapterOptions, MemoryLogger, SentMail, SentMailAddress, TestApp, TestAppOptions };