feat: add API Gateway route testing and discovery

This commit is contained in:
Zakaria
2026-09-24 08:56:22 -04:00
parent 7e49be27f1
commit 9bbe80b5f2
10 changed files with 802 additions and 6 deletions
@@ -6,7 +6,9 @@ import {
DeleteRestApiCommand,
GetRestApiCommand,
GetRestApisCommand,
GetMethodCommand,
GetResourcesCommand,
GetStagesCommand,
PutIntegrationCommand,
PutMethodCommand,
type APIGatewayClient,
@@ -176,6 +178,86 @@ describe('AwsApiGatewayAdapter', () => {
})
})
test('tests a deployed route and preserves its HTTP response', async () => {
const originalFetch = globalThis.fetch
const requests: Request[] = []
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
requests.push(new Request(input, init))
return new Response(JSON.stringify({error: 'invalid request'}), {
status: 400,
headers: {'content-type': 'application/json', 'x-request-id': 'request-123'},
})
}) as typeof fetch
try {
const adapter = new AwsApiGatewayAdapter(fakeClient(() => ({})))
const result = await adapter.testApiGatewayRoute('a1b2c3', {
path: '/hello', method: 'POST', stage: 'dev', body: '{"name":"Ada"}',
headers: {'x-client': 'console'}, query: 'verbose=true',
})
expect(requests).toHaveLength(1)
expect(requests[0]?.url).toBe('http://localhost:4566/execute-api/a1b2c3/dev/hello?verbose=true')
expect(requests[0]?.method).toBe('POST')
expect(requests[0]?.headers.get('content-type')).toBe('application/json')
expect(requests[0]?.headers.get('x-client')).toBe('console')
expect(await requests[0]?.text()).toBe('{"name":"Ada"}')
expect(result).toMatchObject({
statusCode: 400,
headers: {'content-type': 'application/json', 'x-request-id': 'request-123'},
body: '{"error":"invalid request"}',
})
} finally {
globalThis.fetch = originalFetch
}
})
test('omits a body when testing a GET route', async () => {
const originalFetch = globalThis.fetch
const requests: Request[] = []
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
requests.push(new Request(input, init))
return new Response('{}')
}) as typeof fetch
try {
const adapter = new AwsApiGatewayAdapter(fakeClient(() => ({})))
await adapter.testApiGatewayRoute('a1b2c3', {
path: '/hello', method: 'GET', stage: 'dev', body: '{}', headers: {}, query: '',
})
expect(await requests[0]?.text()).toBe('')
} finally {
globalThis.fetch = originalFetch
}
})
test('lists deployed methods, Lambda integrations, and stages', async () => {
const adapter = new AwsApiGatewayAdapter(fakeClient((command) => {
if (command instanceof GetResourcesCommand) {
return {items: [
{id: 'root123', path: '/', resourceMethods: {}},
{id: 'resource456', path: '/hello', resourceMethods: {GET: true, POST: true}},
]}
}
if (command instanceof GetMethodCommand) {
return {methodIntegration: {
uri: 'arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:hello/invocations',
}}
}
if (command instanceof GetStagesCommand) return {item: [{stageName: 'prod'}, {stageName: 'dev'}]}
throw new Error('Unexpected command')
}))
await expect(adapter.listApiGatewayRoutes('a1b2c3')).resolves.toEqual({
routes: [
{resourceId: 'resource456', path: '/hello', method: 'GET', functionName: 'hello'},
{resourceId: 'resource456', path: '/hello', method: 'POST', functionName: 'hello'},
],
stages: ['dev', 'prod'],
})
})
test('returns the AWS API Gateway schema', () => {
const adapter = new AwsApiGatewayAdapter(fakeClient(() => ({})))