serverless-offline 9.1.7 → 9.2.2
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/package.json +4 -10
- package/src/ServerlessOffline.js +3 -3
- package/src/lambda/LambdaFunction.js +1 -4
- package/src/lambda/handler-runner/HandlerRunner.js +3 -11
- package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +9 -6
- package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +8 -5
- package/src/lambda/handler-runner/go-runner/GoRunner.js +3 -1
- package/src/lambda/handler-runner/in-process-runner/InProcessRunner.js +16 -34
- package/src/lambda/handler-runner/in-process-runner/aws-lambda-ric/UserFunction.js +359 -0
- package/src/lambda/{__tests__/fixtures → handler-runner/in-process-runner/aws-lambda-ric}/package.json +0 -0
- package/src/lambda/handler-runner/python-runner/PythonRunner.js +17 -19
- package/src/lambda/handler-runner/ruby-runner/RubyRunner.js +4 -1
- package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +5 -6
- package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +8 -5
- package/src/lambda/__tests__/LambdaContext.test.js +0 -30
- package/src/lambda/__tests__/LambdaFunction.test.js +0 -196
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsJSONObject.fixture.js +0 -47
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js +0 -46
- package/src/lambda/__tests__/fixtures/Lambda/package.json +0 -3
- package/src/lambda/__tests__/fixtures/lambdaFunction.fixture.js +0 -145
- package/src/lambda/__tests__/routes/invocations/InvocationsController.test.js +0 -42
- package/src/utils/__tests__/createUniqueId.test.js +0 -18
- package/src/utils/__tests__/formatToClfTime.test.js +0 -14
- package/src/utils/__tests__/generateHapiPath.test.js +0 -46
- package/src/utils/__tests__/lowerCaseKeys.test.js +0 -30
- package/src/utils/__tests__/parseHeaders.test.js +0 -13
- package/src/utils/__tests__/parseMultiValueHeaders.test.js +0 -24
- package/src/utils/__tests__/parseMultiValueQueryStringParameters.test.js +0 -159
- package/src/utils/__tests__/parseQueryStringParameters.test.js +0 -15
- package/src/utils/__tests__/splitHandlerPathAndName.test.js +0 -54
- package/src/utils/__tests__/unflatten.test.js +0 -32
|
@@ -2,9 +2,10 @@ import { spawn } from 'node:child_process'
|
|
|
2
2
|
import { EOL, platform } from 'node:os'
|
|
3
3
|
import { delimiter, dirname, join, relative, resolve } from 'node:path'
|
|
4
4
|
import process, { cwd } from 'node:process'
|
|
5
|
-
import
|
|
5
|
+
import { createInterface } from 'node:readline'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
7
|
import { log } from '@serverless/utils/log.js'
|
|
8
|
+
import { splitHandlerPathAndName } from '../../../utils/index.js'
|
|
8
9
|
|
|
9
10
|
const { parse, stringify } = JSON
|
|
10
11
|
const { assign, hasOwn } = Object
|
|
@@ -16,18 +17,15 @@ export default class PythonRunner {
|
|
|
16
17
|
|
|
17
18
|
#env = null
|
|
18
19
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
#handlerPath = null
|
|
20
|
+
#handlerProcess = null
|
|
22
21
|
|
|
23
22
|
#runtime = null
|
|
24
23
|
|
|
25
24
|
constructor(funOptions, env) {
|
|
26
|
-
const {
|
|
25
|
+
const { handler, runtime } = funOptions
|
|
26
|
+
const [handlerPath, handlerName] = splitHandlerPathAndName(handler)
|
|
27
27
|
|
|
28
28
|
this.#env = env
|
|
29
|
-
this.#handlerName = handlerName
|
|
30
|
-
this.#handlerPath = handlerPath
|
|
31
29
|
this.#runtime = platform() === 'win32' ? 'python.exe' : runtime
|
|
32
30
|
|
|
33
31
|
if (process.env.VIRTUAL_ENV) {
|
|
@@ -42,13 +40,13 @@ export default class PythonRunner {
|
|
|
42
40
|
|
|
43
41
|
const [pythonExecutable] = this.#runtime.split('.')
|
|
44
42
|
|
|
45
|
-
this
|
|
43
|
+
this.#handlerProcess = spawn(
|
|
46
44
|
pythonExecutable,
|
|
47
45
|
[
|
|
48
46
|
'-u',
|
|
49
47
|
resolve(__dirname, 'invoke.py'),
|
|
50
|
-
relative(cwd(),
|
|
51
|
-
|
|
48
|
+
relative(cwd(), handlerPath),
|
|
49
|
+
handlerName,
|
|
52
50
|
],
|
|
53
51
|
{
|
|
54
52
|
env: assign(process.env, this.#env),
|
|
@@ -56,14 +54,14 @@ export default class PythonRunner {
|
|
|
56
54
|
},
|
|
57
55
|
)
|
|
58
56
|
|
|
59
|
-
this
|
|
60
|
-
input: this
|
|
57
|
+
this.#handlerProcess.stdout.readline = createInterface({
|
|
58
|
+
input: this.#handlerProcess.stdout,
|
|
61
59
|
})
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
// () => void
|
|
65
63
|
cleanup() {
|
|
66
|
-
this
|
|
64
|
+
this.#handlerProcess.kill()
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
#parsePayload(value) {
|
|
@@ -117,8 +115,8 @@ export default class PythonRunner {
|
|
|
117
115
|
try {
|
|
118
116
|
const parsed = this.#parsePayload(line.toString())
|
|
119
117
|
if (parsed) {
|
|
120
|
-
this
|
|
121
|
-
this
|
|
118
|
+
this.#handlerProcess.stdout.readline.removeListener('line', onLine)
|
|
119
|
+
this.#handlerProcess.stderr.removeListener('data', onErr)
|
|
122
120
|
return accept(parsed)
|
|
123
121
|
}
|
|
124
122
|
return null
|
|
@@ -127,12 +125,12 @@ export default class PythonRunner {
|
|
|
127
125
|
}
|
|
128
126
|
}
|
|
129
127
|
|
|
130
|
-
this
|
|
131
|
-
this
|
|
128
|
+
this.#handlerProcess.stdout.readline.on('line', onLine)
|
|
129
|
+
this.#handlerProcess.stderr.on('data', onErr)
|
|
132
130
|
|
|
133
131
|
process.nextTick(() => {
|
|
134
|
-
this
|
|
135
|
-
this
|
|
132
|
+
this.#handlerProcess.stdin.write(input)
|
|
133
|
+
this.#handlerProcess.stdin.write('\n')
|
|
136
134
|
})
|
|
137
135
|
})
|
|
138
136
|
}
|
|
@@ -4,6 +4,7 @@ import { cwd } from 'node:process'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { log } from '@serverless/utils/log.js'
|
|
6
6
|
import { execa } from 'execa'
|
|
7
|
+
import { splitHandlerPathAndName } from '../../../utils/index.js'
|
|
7
8
|
|
|
8
9
|
const { parse, stringify } = JSON
|
|
9
10
|
const { hasOwn } = Object
|
|
@@ -20,7 +21,9 @@ export default class RubyRunner {
|
|
|
20
21
|
#handlerPath = null
|
|
21
22
|
|
|
22
23
|
constructor(funOptions, env) {
|
|
23
|
-
const
|
|
24
|
+
const [handlerPath, handlerName] = splitHandlerPathAndName(
|
|
25
|
+
funOptions.handler,
|
|
26
|
+
)
|
|
24
27
|
|
|
25
28
|
this.#env = env
|
|
26
29
|
this.#handlerName = handlerName
|
|
@@ -8,18 +8,17 @@ const workerThreadHelperPath = resolve(__dirname, './workerThreadHelper.js')
|
|
|
8
8
|
export default class WorkerThreadRunner {
|
|
9
9
|
#workerThread = null
|
|
10
10
|
|
|
11
|
-
constructor(funOptions
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const { functionKey, handlerName, handlerPath, timeout } = funOptions
|
|
11
|
+
constructor(funOptions, env) {
|
|
12
|
+
const { codeDir, functionKey, handler, servicePath, timeout } = funOptions
|
|
15
13
|
|
|
16
14
|
this.#workerThread = new Worker(workerThreadHelperPath, {
|
|
17
15
|
// don't pass process.env from the main process!
|
|
18
16
|
env,
|
|
19
17
|
workerData: {
|
|
18
|
+
codeDir,
|
|
20
19
|
functionKey,
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
handler,
|
|
21
|
+
servicePath,
|
|
23
22
|
timeout,
|
|
24
23
|
},
|
|
25
24
|
})
|
|
@@ -2,18 +2,21 @@ import { env } from 'node:process'
|
|
|
2
2
|
import { parentPort, workerData } from 'node:worker_threads'
|
|
3
3
|
import InProcessRunner from '../in-process-runner/index.js'
|
|
4
4
|
|
|
5
|
-
const { functionKey,
|
|
5
|
+
const { functionKey, handler, servicePath, timeout, codeDir } = workerData
|
|
6
6
|
|
|
7
7
|
parentPort.on('message', async (messageData) => {
|
|
8
8
|
const { context, event, port } = messageData
|
|
9
9
|
|
|
10
10
|
// TODO we could probably cache this in the module scope?
|
|
11
11
|
const inProcessRunner = new InProcessRunner(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
{
|
|
13
|
+
codeDir,
|
|
14
|
+
functionKey,
|
|
15
|
+
handler,
|
|
16
|
+
servicePath,
|
|
17
|
+
timeout,
|
|
18
|
+
},
|
|
15
19
|
env,
|
|
16
|
-
timeout,
|
|
17
20
|
)
|
|
18
21
|
|
|
19
22
|
let result
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import LambdaContext from '../LambdaContext.js'
|
|
3
|
-
|
|
4
|
-
describe('LambdaContext', () => {
|
|
5
|
-
it('should create LambdaContext with correct values', () => {
|
|
6
|
-
const functionName = 'foo'
|
|
7
|
-
const memorySize = 512
|
|
8
|
-
const requestId = 'abc123'
|
|
9
|
-
|
|
10
|
-
const lambdaContext = new LambdaContext(functionName, memorySize)
|
|
11
|
-
lambdaContext.setRequestId(requestId)
|
|
12
|
-
const context = lambdaContext.create()
|
|
13
|
-
|
|
14
|
-
const expected = {
|
|
15
|
-
awsRequestId: 'abc123',
|
|
16
|
-
callbackWaitsForEmptyEventLoop: true,
|
|
17
|
-
clientContext: undefined,
|
|
18
|
-
functionName: 'foo',
|
|
19
|
-
functionVersion: '$LATEST',
|
|
20
|
-
identity: undefined,
|
|
21
|
-
invokedFunctionArn: 'offline_invokedFunctionArn_for_foo',
|
|
22
|
-
logGroupName: 'offline_logGroupName_for_foo',
|
|
23
|
-
logStreamName: 'offline_logStreamName_for_foo',
|
|
24
|
-
memoryLimitInMB: '512',
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// expect(context).toEqual(expected)
|
|
28
|
-
assert.deepEqual(context, expected)
|
|
29
|
-
})
|
|
30
|
-
})
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { dirname, resolve } from 'node:path'
|
|
3
|
-
// import { performance } from 'node:perf_hooks'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
|
-
import LambdaFunction from '../LambdaFunction.js'
|
|
6
|
-
import { DEFAULT_LAMBDA_TIMEOUT } from '../../config/index.js'
|
|
7
|
-
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
|
-
|
|
10
|
-
describe('LambdaFunction', () => {
|
|
11
|
-
const functionName = 'foo'
|
|
12
|
-
|
|
13
|
-
const serverless = {
|
|
14
|
-
config: {
|
|
15
|
-
serverlessPath: '',
|
|
16
|
-
servicePath: resolve(__dirname),
|
|
17
|
-
},
|
|
18
|
-
service: {
|
|
19
|
-
provider: {
|
|
20
|
-
runtime: 'nodejs12.x',
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('Handler tests', () => {
|
|
26
|
-
;[
|
|
27
|
-
{
|
|
28
|
-
description: 'should return result when handler is context.done',
|
|
29
|
-
expected: 'foo',
|
|
30
|
-
handler: 'fixtures/lambdaFunction.fixture.contextDoneHandler',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
description:
|
|
34
|
-
'should return result when handler is context.done which is deferred',
|
|
35
|
-
expected: 'foo',
|
|
36
|
-
handler: 'fixtures/lambdaFunction.fixture.contextDoneHandlerDeferred',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
description: 'should return result when handler is context.succeed',
|
|
40
|
-
expected: 'foo',
|
|
41
|
-
handler: 'fixtures/lambdaFunction.fixture.contextSucceedHandler',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
description:
|
|
45
|
-
'should return result when handler is context.succeed which is deferred',
|
|
46
|
-
expected: 'foo',
|
|
47
|
-
handler:
|
|
48
|
-
'fixtures/lambdaFunction.fixture.contextSucceedHandlerDeferred',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
description: 'should return result when handler is a callback',
|
|
52
|
-
expected: 'foo',
|
|
53
|
-
handler: 'fixtures/lambdaFunction.fixture.callbackHandler',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
description:
|
|
57
|
-
'should return result when handler is a callback which is deferred',
|
|
58
|
-
expected: 'foo',
|
|
59
|
-
handler: 'fixtures/lambdaFunction.fixture.callbackHandlerDeferred',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
description: 'should return result when handler returns a promise',
|
|
63
|
-
expected: 'foo',
|
|
64
|
-
handler: 'fixtures/lambdaFunction.fixture.promiseHandler',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
description:
|
|
68
|
-
'should return result when handler returns a promise which is deferred',
|
|
69
|
-
expected: 'foo',
|
|
70
|
-
handler: 'fixtures/lambdaFunction.fixture.promiseHandlerDeferred',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
description: 'should return result when handler is an async function',
|
|
74
|
-
expected: 'foo',
|
|
75
|
-
handler: 'fixtures/lambdaFunction.fixture.asyncFunctionHandler',
|
|
76
|
-
},
|
|
77
|
-
// NOTE: mix and matching of callbacks and promises is not recommended,
|
|
78
|
-
// nonetheless, we test some of the behaviour to match AWS execution precedence
|
|
79
|
-
{
|
|
80
|
-
description:
|
|
81
|
-
'should return result when handler returns a callback but defines a callback parameter',
|
|
82
|
-
expected: 'Hello Promise!',
|
|
83
|
-
handler:
|
|
84
|
-
'fixtures/lambdaFunction.fixture.promiseWithDefinedCallbackHandler',
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
description:
|
|
88
|
-
'should return result when handler calls context.succeed and context.done',
|
|
89
|
-
expected: 'Hello Context.succeed!',
|
|
90
|
-
handler:
|
|
91
|
-
'fixtures/lambdaFunction.fixture.contextSucceedWithContextDoneHandler',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
description:
|
|
95
|
-
'should return result when handler calls callback and context.done',
|
|
96
|
-
expected: 'Hello Callback!',
|
|
97
|
-
handler:
|
|
98
|
-
'fixtures/lambdaFunction.fixture.callbackWithContextDoneHandler',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
description:
|
|
102
|
-
'should return result when handler calls callback and returns Promise',
|
|
103
|
-
expected: 'Hello Callback!',
|
|
104
|
-
handler: 'fixtures/lambdaFunction.fixture.callbackWithPromiseHandler',
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
description:
|
|
108
|
-
'should return result when handler calls callback inside returned Promise',
|
|
109
|
-
expected: 'Hello Callback!',
|
|
110
|
-
handler: 'fixtures/lambdaFunction.fixture.callbackInsidePromiseHandler',
|
|
111
|
-
},
|
|
112
|
-
].forEach(({ description, expected, handler }) => {
|
|
113
|
-
it(description, async () => {
|
|
114
|
-
const functionDefinition = {
|
|
115
|
-
handler,
|
|
116
|
-
}
|
|
117
|
-
const options = {}
|
|
118
|
-
const lambdaFunction = new LambdaFunction(
|
|
119
|
-
functionName,
|
|
120
|
-
functionDefinition,
|
|
121
|
-
serverless,
|
|
122
|
-
options,
|
|
123
|
-
)
|
|
124
|
-
const result = await lambdaFunction.runHandler()
|
|
125
|
-
|
|
126
|
-
await lambdaFunction.cleanup()
|
|
127
|
-
|
|
128
|
-
assert.equal(result, expected)
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('should pass remaining time to LambdaContext', async () => {
|
|
134
|
-
const functionDefinition = {
|
|
135
|
-
handler: 'fixtures/lambdaFunction.fixture.remainingExecutionTimeHandler',
|
|
136
|
-
}
|
|
137
|
-
const options = {}
|
|
138
|
-
const lambdaFunction = new LambdaFunction(
|
|
139
|
-
functionName,
|
|
140
|
-
functionDefinition,
|
|
141
|
-
serverless,
|
|
142
|
-
options,
|
|
143
|
-
)
|
|
144
|
-
const [first, second, third] = await lambdaFunction.runHandler()
|
|
145
|
-
|
|
146
|
-
await lambdaFunction.cleanup()
|
|
147
|
-
|
|
148
|
-
// handler "pauses" for 100 ms
|
|
149
|
-
assert.ok(first > second - 100)
|
|
150
|
-
assert.ok(second > third - 200)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it.skip('should use default lambda timeout when timeout is not provided', async () => {
|
|
154
|
-
const functionDefinition = {
|
|
155
|
-
handler: 'fixtures/lambdaFunction.fixture.defaultTimeoutHandler',
|
|
156
|
-
}
|
|
157
|
-
const options = {}
|
|
158
|
-
const lambdaFunction = new LambdaFunction(
|
|
159
|
-
functionName,
|
|
160
|
-
functionDefinition,
|
|
161
|
-
serverless,
|
|
162
|
-
options,
|
|
163
|
-
)
|
|
164
|
-
const remainingTime = await lambdaFunction.runHandler()
|
|
165
|
-
|
|
166
|
-
await lambdaFunction.cleanup()
|
|
167
|
-
|
|
168
|
-
assert.ok(remainingTime < DEFAULT_LAMBDA_TIMEOUT * 1000)
|
|
169
|
-
|
|
170
|
-
// result might be flaky/unreliable:
|
|
171
|
-
// (assmuning handler runs no longer than 1 s)
|
|
172
|
-
assert.ok(remainingTime + 1000 > DEFAULT_LAMBDA_TIMEOUT * 1000)
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
// // might run flaky (unreliable)
|
|
176
|
-
// test('executionTimeInMillis should return execution time', async () => {
|
|
177
|
-
// const functionDefinition = {
|
|
178
|
-
// handler: 'fixtures/lambdaFunction.fixture.executionTimeInMillisHandler',
|
|
179
|
-
// }
|
|
180
|
-
// const options = {}
|
|
181
|
-
// const lambdaFunction = new LambdaFunction(
|
|
182
|
-
// functionName,
|
|
183
|
-
// functionDefinition,
|
|
184
|
-
// provider,
|
|
185
|
-
// config,
|
|
186
|
-
// options,
|
|
187
|
-
// )
|
|
188
|
-
// const timerStart = performance.now()
|
|
189
|
-
// await lambdaFunction.runHandler()
|
|
190
|
-
// const timerEnd = performance.now()
|
|
191
|
-
//
|
|
192
|
-
// expect(lambdaFunction.executionTimeInMillis).toBeLessThanOrEqual(
|
|
193
|
-
// timerEnd - timerStart + 10,
|
|
194
|
-
// )
|
|
195
|
-
// })
|
|
196
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { dirname, resolve } from 'node:path'
|
|
2
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import LambdaFunction from '../../../LambdaFunction.js'
|
|
4
|
-
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
|
|
7
|
-
export default class LambdaFunctionThatReturnsJSONObject {
|
|
8
|
-
#lambdaFunction
|
|
9
|
-
|
|
10
|
-
options = {}
|
|
11
|
-
|
|
12
|
-
serverless = {
|
|
13
|
-
config: {
|
|
14
|
-
serverlessPath: '',
|
|
15
|
-
servicePath: resolve(__dirname),
|
|
16
|
-
},
|
|
17
|
-
service: {
|
|
18
|
-
provider: {
|
|
19
|
-
runtime: 'nodejs12.x',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
listFunctionNames() {
|
|
25
|
-
return ['foo']
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getByFunctionName(functionName) {
|
|
29
|
-
const functionDefinition = {
|
|
30
|
-
handler:
|
|
31
|
-
'../../fixtures/lambdaFunction.fixture.asyncFunctionHandlerObject',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
this.#lambdaFunction = new LambdaFunction(
|
|
35
|
-
functionName,
|
|
36
|
-
functionDefinition,
|
|
37
|
-
this.serverless,
|
|
38
|
-
this.options,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
return this.#lambdaFunction
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async cleanup() {
|
|
45
|
-
await this.#lambdaFunction.cleanup()
|
|
46
|
-
}
|
|
47
|
-
}
|
package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { dirname, resolve } from 'node:path'
|
|
2
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import LambdaFunction from '../../../LambdaFunction.js'
|
|
4
|
-
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
|
|
7
|
-
export default class LambdaFunctionThatReturnsNativeString {
|
|
8
|
-
#lambdaFunction
|
|
9
|
-
|
|
10
|
-
options = {}
|
|
11
|
-
|
|
12
|
-
serverless = {
|
|
13
|
-
config: {
|
|
14
|
-
serverlessPath: '',
|
|
15
|
-
servicePath: resolve(__dirname),
|
|
16
|
-
},
|
|
17
|
-
service: {
|
|
18
|
-
provider: {
|
|
19
|
-
runtime: 'nodejs12.x',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
listFunctionNames() {
|
|
25
|
-
return ['foo']
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getByFunctionName(functionName) {
|
|
29
|
-
const functionDefinition = {
|
|
30
|
-
handler: '../../fixtures/lambdaFunction.fixture.asyncFunctionHandler',
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
this.#lambdaFunction = new LambdaFunction(
|
|
34
|
-
functionName,
|
|
35
|
-
functionDefinition,
|
|
36
|
-
this.serverless,
|
|
37
|
-
this.options,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
return this.#lambdaFunction
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async cleanup() {
|
|
44
|
-
await this.#lambdaFunction.cleanup()
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
exports.contextDoneHandler = function contextDoneHandler(event, context) {
|
|
4
|
-
context.done(null, 'foo')
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
exports.contextDoneHandlerDeferred = function contextDoneHandlerDeferred(
|
|
8
|
-
event,
|
|
9
|
-
context,
|
|
10
|
-
) {
|
|
11
|
-
setTimeout(() => context.done(null, 'foo'), 100)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
exports.contextSucceedHandler = function contextSucceedHandler(event, context) {
|
|
15
|
-
context.succeed('foo')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
exports.contextSucceedHandlerDeferred = function contextSucceedHandlerDeferred(
|
|
19
|
-
event,
|
|
20
|
-
context,
|
|
21
|
-
) {
|
|
22
|
-
setTimeout(() => context.succeed('foo'), 100)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
exports.callbackHandler = function callbackHandler(event, context, callback) {
|
|
26
|
-
callback(null, 'foo')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
exports.callbackHandlerDeferred = function callbackHandlerDeferred(
|
|
30
|
-
event,
|
|
31
|
-
context,
|
|
32
|
-
callback,
|
|
33
|
-
) {
|
|
34
|
-
setTimeout(() => callback(null, 'foo'), 100)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
exports.promiseHandler = function promiseHandler() {
|
|
38
|
-
return Promise.resolve('foo')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
exports.promiseHandlerDeferred = function promiseDeferred() {
|
|
42
|
-
return new Promise((resolve) => {
|
|
43
|
-
setTimeout(() => resolve('foo'), 100)
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
exports.asyncFunctionHandler = async function asyncFunctionHandler() {
|
|
48
|
-
return 'foo'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
exports.asyncFunctionHandlerObject = async function asyncFunctionHandler() {
|
|
52
|
-
return {
|
|
53
|
-
foo: 'bar',
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// we deliberately test the case where a 'callback' is defined
|
|
58
|
-
// in the handler, but a promise is being returned to protect from a
|
|
59
|
-
// potential naive implementation, e.g.
|
|
60
|
-
//
|
|
61
|
-
// const { promisify } = 'utils'
|
|
62
|
-
// const promisifiedHandler = handler.length === 3 ? promisify(handler) : handler
|
|
63
|
-
//
|
|
64
|
-
// if someone would return a promise, but also defines callback, without using it
|
|
65
|
-
// the handler would not be returning anything
|
|
66
|
-
exports.promiseWithDefinedCallbackHandler =
|
|
67
|
-
function promiseWithDefinedCallbackHandler(
|
|
68
|
-
event, // eslint-disable-line no-unused-vars
|
|
69
|
-
context, // eslint-disable-line no-unused-vars
|
|
70
|
-
callback, // eslint-disable-line no-unused-vars
|
|
71
|
-
) {
|
|
72
|
-
return Promise.resolve('Hello Promise!')
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
exports.contextSucceedWithContextDoneHandler =
|
|
76
|
-
function contextSucceedWithContextDoneHandler(event, context) {
|
|
77
|
-
context.succeed('Hello Context.succeed!')
|
|
78
|
-
|
|
79
|
-
context.done(null, 'Hello Context.done!')
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
exports.callbackWithContextDoneHandler =
|
|
83
|
-
function callbackWithContextDoneHandler(event, context, callback) {
|
|
84
|
-
callback(null, 'Hello Callback!')
|
|
85
|
-
|
|
86
|
-
context.done(null, 'Hello Context.done!')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
exports.callbackWithPromiseHandler = function callbackWithPromiseHandler(
|
|
90
|
-
event,
|
|
91
|
-
context,
|
|
92
|
-
callback,
|
|
93
|
-
) {
|
|
94
|
-
callback(null, 'Hello Callback!')
|
|
95
|
-
|
|
96
|
-
return Promise.resolve('Hello Promise!')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
exports.callbackInsidePromiseHandler = function callbackInsidePromiseHandler(
|
|
100
|
-
event,
|
|
101
|
-
context,
|
|
102
|
-
callback,
|
|
103
|
-
) {
|
|
104
|
-
return new Promise((resolve) => {
|
|
105
|
-
callback(null, 'Hello Callback!')
|
|
106
|
-
|
|
107
|
-
resolve('Hello Promise!')
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
exports.requestIdHandler = async function requestIdHandler(event, context) {
|
|
112
|
-
return context.awsRequestId
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
exports.remainingExecutionTimeHandler =
|
|
116
|
-
async function remainingExecutionTimeHandler(event, context) {
|
|
117
|
-
const first = context.getRemainingTimeInMillis()
|
|
118
|
-
|
|
119
|
-
await new Promise((resolve) => {
|
|
120
|
-
setTimeout(resolve, 100)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
const second = context.getRemainingTimeInMillis()
|
|
124
|
-
|
|
125
|
-
await new Promise((resolve) => {
|
|
126
|
-
setTimeout(resolve, 200)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
const third = context.getRemainingTimeInMillis()
|
|
130
|
-
|
|
131
|
-
return [first, second, third]
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
exports.defaultTimeoutHandler = async function defaultTimeoutHandler(
|
|
135
|
-
event,
|
|
136
|
-
context,
|
|
137
|
-
) {
|
|
138
|
-
return context.getRemainingTimeInMillis()
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
exports.executionTimeInMillisHandler = function executionTimeInMillisHandler() {
|
|
142
|
-
return new Promise((resolve) => {
|
|
143
|
-
setTimeout(resolve, 100)
|
|
144
|
-
})
|
|
145
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import InvocationsController from '../../../routes/invocations/InvocationsController.js'
|
|
3
|
-
import LambdaFunctionThatReturnsJSONObject from '../../fixtures/Lambda/LambdaFunctionThatReturnsJSONObject.fixture.js'
|
|
4
|
-
import LambdaFunctionThatReturnsNativeString from '../../fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js'
|
|
5
|
-
|
|
6
|
-
describe('InvocationController', () => {
|
|
7
|
-
const functionName = 'foo'
|
|
8
|
-
|
|
9
|
-
describe('when event type is "RequestResponse"', () => {
|
|
10
|
-
const eventType = 'RequestResponse'
|
|
11
|
-
|
|
12
|
-
it('should return json object if lambda response is json', async () => {
|
|
13
|
-
const expected = {
|
|
14
|
-
Payload: {
|
|
15
|
-
foo: 'bar',
|
|
16
|
-
},
|
|
17
|
-
StatusCode: 200,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const lambdaFunction = new LambdaFunctionThatReturnsJSONObject()
|
|
21
|
-
const invocationController = new InvocationsController(lambdaFunction)
|
|
22
|
-
const result = await invocationController.invoke(functionName, eventType)
|
|
23
|
-
await lambdaFunction.cleanup()
|
|
24
|
-
|
|
25
|
-
assert.deepEqual(result, expected)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('should wrap native string responses with ""', async () => {
|
|
29
|
-
const expected = {
|
|
30
|
-
Payload: '"foo"',
|
|
31
|
-
StatusCode: 200,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const lambdaFunction = new LambdaFunctionThatReturnsNativeString()
|
|
35
|
-
const invocationController = new InvocationsController(lambdaFunction)
|
|
36
|
-
const result = await invocationController.invoke(functionName, eventType)
|
|
37
|
-
await lambdaFunction.cleanup()
|
|
38
|
-
|
|
39
|
-
assert.deepEqual(result, expected)
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
})
|