Skip to content

Commit 6e83413

Browse files
committed
test: split loading and invoking function
1 parent 70511b7 commit 6e83413

File tree

3 files changed

+70
-54
lines changed

3 files changed

+70
-54
lines changed

tests/utils/fixture.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { env } from 'node:process'
1414
import { fileURLToPath } from 'node:url'
1515
import { v4 } from 'uuid'
1616
import { LocalServer } from './local-server.js'
17-
import { loadAndInvokeFunctionImpl, type FunctionInvocationOptions } from './lambda-helpers.mjs'
17+
import { loadFunction, type FunctionInvocationOptions } from './lambda-helpers.mjs'
1818

1919
import { glob } from 'fast-glob'
2020
import {
@@ -345,7 +345,8 @@ export async function invokeFunction(
345345
.spyOn(process, 'cwd')
346346
.mockReturnValue(join(ctx.functionDist, SERVER_HANDLER_NAME))
347347
try {
348-
return await loadAndInvokeFunctionImpl(ctx, options)
348+
const invokeFunctionImpl = await loadFunction(ctx, options)
349+
return await invokeFunctionImpl(options)
349350
} finally {
350351
cwdMock.mockRestore()
351352
}

tests/utils/lambda-helpers.mjs

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const SERVER_HANDLER_NAME = '___netlify-server-handler'
2020
* @property {string} [url] TThe relative path that should be requested. Defaults to '/'
2121
* @property {Record<string, string>} [headers] The headers used for the invocation
2222
* @property {Record<string, unknown>} [flags] Feature flags that should be set during the invocation
23+
*
24+
* @typedef {Pick<FunctionInvocationOptions, 'env'>} LoadFunctionOptions
2325
*/
2426

2527
/**
@@ -109,73 +111,84 @@ const DEFAULT_FLAGS = {}
109111

110112
/**
111113
* @param {FixtureTestContext} ctx
112-
* @param {FunctionInvocationOptions} options
114+
* @param {LoadFunctionOptions} options
113115
*/
114-
export async function loadAndInvokeFunctionImpl(
115-
ctx,
116-
{ headers, httpMethod, flags, url, env } = {},
117-
) {
116+
export async function loadFunction(ctx, { env } = {}) {
118117
const restoreEnvironment = temporarilySetEnv(ctx, env)
119118

120119
const { handler } = await import(
121120
'file:///' + join(ctx.functionDist, SERVER_HANDLER_NAME, '___netlify-entry-point.mjs')
122121
)
123122

124-
let resolveInvocation, rejectInvocation
125-
const invocationPromise = new Promise((resolve, reject) => {
126-
resolveInvocation = resolve
127-
rejectInvocation = reject
128-
})
123+
/**
124+
* @param {FunctionInvocationOptions} options
125+
*/
126+
async function invokeFunction({ headers, httpMethod, flags, url, env: invokeEnv } = {}) {
127+
const restoreEnvironment = temporarilySetEnv(ctx, {
128+
...env,
129+
...invokeEnv,
130+
})
129131

130-
const response = await execute({
131-
event: {
132-
headers: headers || {},
133-
httpMethod: httpMethod || 'GET',
134-
rawUrl: new URL(url || '/', 'https://example.netlify').href,
135-
flags: flags ?? DEFAULT_FLAGS,
136-
},
137-
lambdaFunc: { handler },
138-
timeoutMs: 4_000,
139-
onInvocationEnd: (error) => {
140-
// lambda-local resolve promise return from execute when response is closed
141-
// but we should wait for tracked background work to finish
142-
// before resolving the promise to allow background work to finish
143-
if (error) {
144-
rejectInvocation(error)
145-
} else {
146-
resolveInvocation()
147-
}
148-
},
149-
})
132+
let resolveInvocation, rejectInvocation
133+
const invocationPromise = new Promise((resolve, reject) => {
134+
resolveInvocation = resolve
135+
rejectInvocation = reject
136+
})
137+
138+
const response = await execute({
139+
event: {
140+
headers: headers || {},
141+
httpMethod: httpMethod || 'GET',
142+
rawUrl: new URL(url || '/', 'https://example.netlify').href,
143+
flags: flags ?? DEFAULT_FLAGS,
144+
},
145+
lambdaFunc: { handler },
146+
timeoutMs: 4_000,
147+
onInvocationEnd: (error) => {
148+
// lambda-local resolve promise return from execute when response is closed
149+
// but we should wait for tracked background work to finish
150+
// before resolving the promise to allow background work to finish
151+
if (error) {
152+
rejectInvocation(error)
153+
} else {
154+
resolveInvocation()
155+
}
156+
},
157+
})
150158

151-
await invocationPromise
159+
await invocationPromise
152160

153-
if (!response) {
154-
throw new Error('No response from lambda-local')
155-
}
161+
if (!response) {
162+
throw new Error('No response from lambda-local')
163+
}
156164

157-
const responseHeaders = Object.entries(response.multiValueHeaders || {}).reduce(
158-
(prev, [key, value]) => ({
159-
...prev,
160-
[key]: value.length === 1 ? `${value}` : value.join(', '),
161-
}),
162-
response.headers || {},
163-
)
165+
const responseHeaders = Object.entries(response.multiValueHeaders || {}).reduce(
166+
(prev, [key, value]) => ({
167+
...prev,
168+
[key]: value.length === 1 ? `${value}` : value.join(', '),
169+
}),
170+
response.headers || {},
171+
)
164172

165-
const bodyBuffer = await streamToBuffer(response.body)
173+
const bodyBuffer = await streamToBuffer(response.body)
166174

167-
restoreEnvironment()
175+
restoreEnvironment()
168176

169-
return {
170-
statusCode: response.statusCode,
171-
bodyBuffer,
172-
body: bodyBuffer.toString('utf-8'),
173-
headers: responseHeaders,
174-
isBase64Encoded: response.isBase64Encoded,
177+
return {
178+
statusCode: response.statusCode,
179+
bodyBuffer,
180+
body: bodyBuffer.toString('utf-8'),
181+
headers: responseHeaders,
182+
isBase64Encoded: response.isBase64Encoded,
183+
}
175184
}
185+
186+
restoreEnvironment()
187+
188+
return invokeFunction
176189
}
177190

178191
/**
179-
* @typedef {typeof loadAndInvokeFunctionImpl} InvokeFunction
192+
* @typedef {Awaited<ReturnType<typeof loadFunction>>} InvokeFunction
180193
* @typedef {Promise<Awaited<ReturnType<InvokeFunction>>>} InvokeFunctionResult
181194
*/

tests/utils/sandbox-child.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @ts-check
22

33
import { getLogger } from 'lambda-local'
4-
import { loadAndInvokeFunctionImpl } from './lambda-helpers.mjs'
4+
import { loadFunction } from './lambda-helpers.mjs'
55

66
getLogger().level = 'alert'
77

@@ -17,7 +17,9 @@ process.on(
1717
try {
1818
const [ctx, options] = msg.args
1919

20-
const result = await loadAndInvokeFunctionImpl(ctx, options)
20+
const invokeFunctionImpl = await loadFunction(ctx, options)
21+
const result = await invokeFunctionImpl(options)
22+
2123
if (process.send) {
2224
process.send({
2325
action: 'invokeFunctionResult',

0 commit comments

Comments
 (0)