serverless-offline 9.2.0 → 9.2.3
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/events/http/Endpoint.js +2 -2
- package/src/events/http/authJWTSettingsExtractor.js +14 -10
- package/src/lambda/handler-runner/HandlerRunner.js +2 -13
- package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +1 -1
- package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +1 -1
- package/src/lambda/handler-runner/python-runner/PythonRunner.js +12 -10
- package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +1 -1
- 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 -46
- 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__/fixtures/package.json +0 -3
- 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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"dedicatedTo": "Blue, a great migrating bird.",
|
|
3
3
|
"name": "serverless-offline",
|
|
4
|
-
"version": "9.2.
|
|
4
|
+
"version": "9.2.3",
|
|
5
5
|
"description": "Emulate AWS λ and API Gateway locally when developing your Serverless project",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./src/index.js",
|
|
@@ -49,12 +49,6 @@
|
|
|
49
49
|
"schedule",
|
|
50
50
|
"websocket"
|
|
51
51
|
],
|
|
52
|
-
"files": [
|
|
53
|
-
"src/**",
|
|
54
|
-
"package.json",
|
|
55
|
-
"LICENSE",
|
|
56
|
-
"README.md"
|
|
57
|
-
],
|
|
58
52
|
"author": "David Hérault <dherault@gmail.com> (https://github.com/dherault)",
|
|
59
53
|
"maintainers": [
|
|
60
54
|
"Bilal Soylu (https://github.com/Bilal-S)",
|
|
@@ -195,7 +189,7 @@
|
|
|
195
189
|
"@hapi/h2o2": "^9.1.0",
|
|
196
190
|
"@hapi/hapi": "^20.2.2",
|
|
197
191
|
"@serverless/utils": "^6.7.0",
|
|
198
|
-
"aws-sdk": "^2.
|
|
192
|
+
"aws-sdk": "^2.1195.0",
|
|
199
193
|
"boxen": "^7.0.0",
|
|
200
194
|
"chalk": "^5.0.1",
|
|
201
195
|
"execa": "^6.1.0",
|
|
@@ -217,7 +211,7 @@
|
|
|
217
211
|
},
|
|
218
212
|
"devDependencies": {
|
|
219
213
|
"archiver": "^5.3.1",
|
|
220
|
-
"eslint": "^8.
|
|
214
|
+
"eslint": "^8.22.0",
|
|
221
215
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
222
216
|
"eslint-config-prettier": "^8.5.0",
|
|
223
217
|
"eslint-plugin-import": "^2.25.4",
|
|
@@ -227,7 +221,7 @@
|
|
|
227
221
|
"lint-staged": "^13.0.3",
|
|
228
222
|
"mocha": "^10.0.0",
|
|
229
223
|
"prettier": "^2.7.1",
|
|
230
|
-
"serverless": "^3.
|
|
224
|
+
"serverless": "^3.22.0",
|
|
231
225
|
"standard-version": "^9.5.0"
|
|
232
226
|
},
|
|
233
227
|
"peerDependencies": {
|
package/src/ServerlessOffline.js
CHANGED
|
@@ -245,9 +245,9 @@ export default class ServerlessOffline {
|
|
|
245
245
|
|
|
246
246
|
log.notice()
|
|
247
247
|
log.notice(
|
|
248
|
-
`Starting Offline at stage ${
|
|
249
|
-
|
|
250
|
-
)}`,
|
|
248
|
+
`Starting Offline at stage ${
|
|
249
|
+
this.#options.stage || provider.stage
|
|
250
|
+
} ${chalk.gray(`(${this.#options.region || provider.region})`)}`,
|
|
251
251
|
)
|
|
252
252
|
log.notice()
|
|
253
253
|
log.debug('options:', this.#options)
|
|
@@ -10,11 +10,11 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
10
10
|
|
|
11
11
|
// velocity template defaults
|
|
12
12
|
const defaultRequestTemplate = readFileSync(
|
|
13
|
-
resolve(__dirname, '
|
|
13
|
+
resolve(__dirname, 'templates/offline-default.req.vm'),
|
|
14
14
|
'utf8',
|
|
15
15
|
)
|
|
16
16
|
const defaultResponseTemplate = readFileSync(
|
|
17
|
-
resolve(__dirname, '
|
|
17
|
+
resolve(__dirname, 'templates/offline-default.res.vm'),
|
|
18
18
|
'utf8',
|
|
19
19
|
)
|
|
20
20
|
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { log } from '@serverless/utils/log.js'
|
|
2
2
|
|
|
3
|
+
function buildFailureResult(warningMessage) {
|
|
4
|
+
log.warning(warningMessage)
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
unsupportedAuth: true,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function buildSuccessResult(authorizerName) {
|
|
12
|
+
return {
|
|
13
|
+
authorizerName,
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
export default function authJWTSettingsExtractor(
|
|
4
18
|
endpoint,
|
|
5
19
|
provider,
|
|
6
20
|
ignoreJWTSignature,
|
|
7
21
|
) {
|
|
8
|
-
const buildFailureResult = (warningMessage) => {
|
|
9
|
-
log.warning(warningMessage)
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
unsupportedAuth: true,
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const buildSuccessResult = (authorizerName) => ({ authorizerName })
|
|
17
|
-
|
|
18
22
|
const { authorizer } = endpoint
|
|
19
23
|
|
|
20
24
|
if (!authorizer) {
|
|
@@ -24,9 +24,7 @@ export default class HandlerRunner {
|
|
|
24
24
|
|
|
25
25
|
async #loadRunner() {
|
|
26
26
|
const { useChildProcesses, useDocker, useInProcess } = this.#options
|
|
27
|
-
|
|
28
|
-
const { codeDir, functionKey, handler, runtime, servicePath, timeout } =
|
|
29
|
-
this.#funOptions
|
|
27
|
+
const { handler, runtime } = this.#funOptions
|
|
30
28
|
|
|
31
29
|
log.debug(`Loading handler... (${handler})`)
|
|
32
30
|
|
|
@@ -71,16 +69,7 @@ export default class HandlerRunner {
|
|
|
71
69
|
'./in-process-runner/index.js'
|
|
72
70
|
)
|
|
73
71
|
|
|
74
|
-
return new InProcessRunner(
|
|
75
|
-
{
|
|
76
|
-
codeDir,
|
|
77
|
-
functionKey,
|
|
78
|
-
handler,
|
|
79
|
-
servicePath,
|
|
80
|
-
timeout,
|
|
81
|
-
},
|
|
82
|
-
this.#env,
|
|
83
|
-
)
|
|
72
|
+
return new InProcessRunner(this.#funOptions, this.#env)
|
|
84
73
|
}
|
|
85
74
|
|
|
86
75
|
const { default: WorkerThreadRunner } = await import(
|
|
@@ -17,6 +17,8 @@ export default class PythonRunner {
|
|
|
17
17
|
|
|
18
18
|
#env = null
|
|
19
19
|
|
|
20
|
+
#handlerProcess = null
|
|
21
|
+
|
|
20
22
|
#runtime = null
|
|
21
23
|
|
|
22
24
|
constructor(funOptions, env) {
|
|
@@ -38,7 +40,7 @@ export default class PythonRunner {
|
|
|
38
40
|
|
|
39
41
|
const [pythonExecutable] = this.#runtime.split('.')
|
|
40
42
|
|
|
41
|
-
this
|
|
43
|
+
this.#handlerProcess = spawn(
|
|
42
44
|
pythonExecutable,
|
|
43
45
|
[
|
|
44
46
|
'-u',
|
|
@@ -52,14 +54,14 @@ export default class PythonRunner {
|
|
|
52
54
|
},
|
|
53
55
|
)
|
|
54
56
|
|
|
55
|
-
this
|
|
56
|
-
input: this
|
|
57
|
+
this.#handlerProcess.stdout.readline = createInterface({
|
|
58
|
+
input: this.#handlerProcess.stdout,
|
|
57
59
|
})
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
// () => void
|
|
61
63
|
cleanup() {
|
|
62
|
-
this
|
|
64
|
+
this.#handlerProcess.kill()
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
#parsePayload(value) {
|
|
@@ -113,8 +115,8 @@ export default class PythonRunner {
|
|
|
113
115
|
try {
|
|
114
116
|
const parsed = this.#parsePayload(line.toString())
|
|
115
117
|
if (parsed) {
|
|
116
|
-
this
|
|
117
|
-
this
|
|
118
|
+
this.#handlerProcess.stdout.readline.removeListener('line', onLine)
|
|
119
|
+
this.#handlerProcess.stderr.removeListener('data', onErr)
|
|
118
120
|
return accept(parsed)
|
|
119
121
|
}
|
|
120
122
|
return null
|
|
@@ -123,12 +125,12 @@ export default class PythonRunner {
|
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
126
|
-
this
|
|
127
|
-
this
|
|
128
|
+
this.#handlerProcess.stdout.readline.on('line', onLine)
|
|
129
|
+
this.#handlerProcess.stderr.on('data', onErr)
|
|
128
130
|
|
|
129
131
|
process.nextTick(() => {
|
|
130
|
-
this
|
|
131
|
-
this
|
|
132
|
+
this.#handlerProcess.stdin.write(input)
|
|
133
|
+
this.#handlerProcess.stdin.write('\n')
|
|
132
134
|
})
|
|
133
135
|
})
|
|
134
136
|
}
|
|
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
3
3
|
import { MessageChannel, Worker } from 'node:worker_threads'
|
|
4
4
|
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
const workerThreadHelperPath = resolve(__dirname, '
|
|
6
|
+
const workerThreadHelperPath = resolve(__dirname, 'workerThreadHelper.js')
|
|
7
7
|
|
|
8
8
|
export default class WorkerThreadRunner {
|
|
9
9
|
#workerThread = null
|
|
@@ -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,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 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: 'fixtures/lambdaFunction-fixture.asyncFunctionHandlerObject',
|
|
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
|
-
}
|
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
|
-
})
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import createUniqueId from '../createUniqueId.js'
|
|
3
|
-
|
|
4
|
-
describe('createUniqueId', () => {
|
|
5
|
-
it('should be unique', () => {
|
|
6
|
-
const items = 100000
|
|
7
|
-
const set = new Set(Array.from(Array(items)).map(createUniqueId))
|
|
8
|
-
|
|
9
|
-
assert.equal(set.size, items)
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
it('should be a 36 character string', () => {
|
|
13
|
-
const id = createUniqueId()
|
|
14
|
-
|
|
15
|
-
assert.equal(typeof id, 'string')
|
|
16
|
-
assert.equal(id.length, 36)
|
|
17
|
-
})
|
|
18
|
-
})
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import formatToClfTime from '../formatToClfTime.js'
|
|
3
|
-
|
|
4
|
-
const { now } = Date
|
|
5
|
-
|
|
6
|
-
describe('formatToClfTime', () => {
|
|
7
|
-
it('should return "common log format" formatted time', () => {
|
|
8
|
-
const millis = now()
|
|
9
|
-
const result = formatToClfTime(millis)
|
|
10
|
-
|
|
11
|
-
// expected: 17/Dec/1995:03:24:00 -0500 (with varying offset)
|
|
12
|
-
assert.match(result, /([\w:/]+\s[+-]\d{4})/)
|
|
13
|
-
})
|
|
14
|
-
})
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import generateHapiPath from '../generateHapiPath.js'
|
|
3
|
-
|
|
4
|
-
const serverless = {
|
|
5
|
-
service: {
|
|
6
|
-
provider: {
|
|
7
|
-
stage: 'dev',
|
|
8
|
-
},
|
|
9
|
-
},
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
describe('generateHapiPath', () => {
|
|
13
|
-
it('should generate url starting with a slash', () => {
|
|
14
|
-
const options = {}
|
|
15
|
-
const result = generateHapiPath('users', options, serverless)
|
|
16
|
-
assert.equal(result[0], '/')
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('should generate url with the stage prepended', () => {
|
|
20
|
-
const options = {}
|
|
21
|
-
const result = generateHapiPath('users', options, serverless)
|
|
22
|
-
assert.equal(result, '/dev/users')
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
describe('when a prefix option is set', () => {
|
|
26
|
-
it('the url should add the prefix', () => {
|
|
27
|
-
const options = { prefix: 'some-prefix' }
|
|
28
|
-
const result = generateHapiPath('users', options, serverless)
|
|
29
|
-
assert.equal(result, '/some-prefix/dev/users')
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
describe('when the noPrependStageInUrl option is set', () => {
|
|
34
|
-
it('the url should omit the stage', () => {
|
|
35
|
-
const options = { noPrependStageInUrl: true }
|
|
36
|
-
const result = generateHapiPath('users', options, serverless)
|
|
37
|
-
assert.equal(result, '/users')
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('the stage from options should override stage from serverless config', () => {
|
|
42
|
-
const options = { stage: 'prod' }
|
|
43
|
-
const result = generateHapiPath('users', options, serverless)
|
|
44
|
-
assert.equal(result, '/prod/users')
|
|
45
|
-
})
|
|
46
|
-
})
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import lowerCaseKeys from '../lowerCaseKeys.js'
|
|
3
|
-
|
|
4
|
-
describe('lowerCaseKeys', () => {
|
|
5
|
-
it('should handle empty object', () => {
|
|
6
|
-
const result = lowerCaseKeys({})
|
|
7
|
-
assert.deepEqual(result, {})
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('should handle object with one key', () => {
|
|
11
|
-
const result = lowerCaseKeys({ 'Some-Key': 'value' })
|
|
12
|
-
assert.deepEqual(result, { 'some-key': 'value' })
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should handle object with multiple keys', () => {
|
|
16
|
-
const result = lowerCaseKeys({
|
|
17
|
-
'already-lowercase': 'cool',
|
|
18
|
-
'Another-Key': 'anotherValue',
|
|
19
|
-
'lOts-OF-CAPitaLs': 'ButThisIsNotTouched',
|
|
20
|
-
'Some-Key': 'value',
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
assert.deepEqual(result, {
|
|
24
|
-
'already-lowercase': 'cool',
|
|
25
|
-
'another-key': 'anotherValue',
|
|
26
|
-
'lots-of-capitals': 'ButThisIsNotTouched',
|
|
27
|
-
'some-key': 'value',
|
|
28
|
-
})
|
|
29
|
-
})
|
|
30
|
-
})
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
// uses the same tests as parseMultiValueHeaders
|
|
3
|
-
import tests from './parseMultiValueHeaders.test.js'
|
|
4
|
-
import parseHeaders from '../parseHeaders.js'
|
|
5
|
-
|
|
6
|
-
describe('parseQueryStringParameters', () => {
|
|
7
|
-
tests.forEach(({ description, expected, param }) => {
|
|
8
|
-
it(`should return ${description}`, () => {
|
|
9
|
-
const result = parseHeaders(param)
|
|
10
|
-
assert.deepEqual(result, expected)
|
|
11
|
-
})
|
|
12
|
-
})
|
|
13
|
-
})
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import parseMultiValueHeaders from '../parseMultiValueHeaders.js'
|
|
3
|
-
|
|
4
|
-
// TODO need more tests
|
|
5
|
-
const tests = [
|
|
6
|
-
{
|
|
7
|
-
description: 'no parameter (empty array)',
|
|
8
|
-
expected: null,
|
|
9
|
-
expectedMulti: null,
|
|
10
|
-
param: [],
|
|
11
|
-
},
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
describe('parseMultiValueHeaders', () => {
|
|
15
|
-
tests.forEach(({ description, expectedMulti, param }) => {
|
|
16
|
-
it(`should return ${description}`, () => {
|
|
17
|
-
const resultMulti = parseMultiValueHeaders(param)
|
|
18
|
-
assert.deepEqual(resultMulti, expectedMulti)
|
|
19
|
-
})
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
// export tests for parseHeaders
|
|
24
|
-
export default tests
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import parseMultiValueQueryStringParameters from '../parseMultiValueQueryStringParameters.js'
|
|
3
|
-
|
|
4
|
-
const tests = [
|
|
5
|
-
{
|
|
6
|
-
description: 'no parameter (empty string)',
|
|
7
|
-
expected: null,
|
|
8
|
-
expectedMulti: null,
|
|
9
|
-
param: '',
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
description: 'string parameter',
|
|
14
|
-
expected: { foo: 'bar' },
|
|
15
|
-
expectedMulti: { foo: ['bar'] },
|
|
16
|
-
param: 'foo=bar',
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
description: 'number parameter (no type casting)',
|
|
21
|
-
expected: { foo: '1' },
|
|
22
|
-
expectedMulti: { foo: ['1'] },
|
|
23
|
-
param: 'foo=1',
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
description: 'boolean parameter (no type casting)',
|
|
28
|
-
expected: { foo: 'true' },
|
|
29
|
-
expectedMulti: { foo: ['true'] },
|
|
30
|
-
param: 'foo=true',
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
description: 'multiple parameters',
|
|
35
|
-
expected: { bar: 'test2', foo: 'test1' },
|
|
36
|
-
expectedMulti: { bar: ['test2'], foo: ['test1'] },
|
|
37
|
-
param: 'foo=test1&bar=test2',
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
description: 'multiple parameters, same keys',
|
|
42
|
-
expected: { foo: 'foobar' },
|
|
43
|
-
expectedMulti: { foo: ['test', 'foobar'] },
|
|
44
|
-
param: 'foo=test&foo=foobar',
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
description: 'multiple parameters, same keys, different casing',
|
|
49
|
-
expected: { foo: 'test', FOO: 'FOOBAR' },
|
|
50
|
-
expectedMulti: { foo: ['test'], FOO: ['FOOBAR'] },
|
|
51
|
-
param: 'foo=test&FOO=FOOBAR',
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
description: 'multiple parameters, same keys, same values',
|
|
56
|
-
expected: { foo: 'test' },
|
|
57
|
-
expectedMulti: { foo: ['test', 'test'] },
|
|
58
|
-
param: 'foo=test&foo=test',
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
{
|
|
62
|
-
description: 'no value',
|
|
63
|
-
expected: { foo: '' },
|
|
64
|
-
expectedMulti: { foo: [''] },
|
|
65
|
-
param: 'foo',
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
description: 'no value with =',
|
|
70
|
-
expected: { foo: '' },
|
|
71
|
-
expectedMulti: { foo: [''] },
|
|
72
|
-
param: 'foo=',
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
description: 'no value with &',
|
|
77
|
-
expected: { foo: '' },
|
|
78
|
-
expectedMulti: { foo: [''] },
|
|
79
|
-
param: 'foo&',
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
{
|
|
83
|
-
description: 'no value with = and &',
|
|
84
|
-
expected: { foo: '' },
|
|
85
|
-
expectedMulti: { foo: [''] },
|
|
86
|
-
param: 'foo=&',
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
{
|
|
90
|
-
description: 'value is whitespace',
|
|
91
|
-
expected: { foo: ' ' },
|
|
92
|
-
expectedMulti: { foo: [' '] },
|
|
93
|
-
param: 'foo=%20',
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
{
|
|
97
|
-
description: 'key and value have whitespace',
|
|
98
|
-
expected: { ' foo ': ' test ' },
|
|
99
|
-
expectedMulti: { ' foo ': [' test '] },
|
|
100
|
-
param: '%20foo%20=%20test%20',
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
description: 'unicode',
|
|
105
|
-
expected: { Σ: '😋' },
|
|
106
|
-
expectedMulti: { Σ: ['😋'] },
|
|
107
|
-
param: 'Σ=😋',
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
description: 'encoded', // encodeURIComponent
|
|
112
|
-
expected: { '?=/&:': '?=/&:' },
|
|
113
|
-
expectedMulti: { '?=/&:': ['?=/&:'] },
|
|
114
|
-
param: '%3F%3D%2F%26%3A=%3F%3D%2F%26%3A',
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
{
|
|
118
|
-
description: 'end of line',
|
|
119
|
-
expected: { '\n': '\n' },
|
|
120
|
-
expectedMulti: { '\n': ['\n'] },
|
|
121
|
-
param: '%0A=%0A',
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
// silly test section:
|
|
125
|
-
{
|
|
126
|
-
description: 'silly I.',
|
|
127
|
-
expected: { test: '?' },
|
|
128
|
-
expectedMulti: { test: ['?'] },
|
|
129
|
-
param: 'test=?',
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
{
|
|
133
|
-
description: 'silly II.',
|
|
134
|
-
expected: { test: '/' },
|
|
135
|
-
expectedMulti: { test: ['/'] },
|
|
136
|
-
param: 'test=/',
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
{
|
|
140
|
-
description: 'silly III.',
|
|
141
|
-
expected: { test: '=' },
|
|
142
|
-
expectedMulti: { test: ['='] },
|
|
143
|
-
param: 'test==',
|
|
144
|
-
},
|
|
145
|
-
]
|
|
146
|
-
|
|
147
|
-
describe('parseMultiValueQueryStringParameters', () => {
|
|
148
|
-
tests.forEach(({ description, expectedMulti, param }) => {
|
|
149
|
-
const url = `foo?${param}`
|
|
150
|
-
|
|
151
|
-
it(`should return ${description}`, () => {
|
|
152
|
-
const resultMulti = parseMultiValueQueryStringParameters(url)
|
|
153
|
-
assert.deepEqual(resultMulti, expectedMulti)
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
// export tests for parseQueryStringParameters
|
|
159
|
-
export default tests
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
// uses the same tests as parseMultiValueQueryStringParameters
|
|
3
|
-
import tests from './parseMultiValueQueryStringParameters.test.js'
|
|
4
|
-
import parseQueryStringParameters from '../parseQueryStringParameters.js'
|
|
5
|
-
|
|
6
|
-
describe('parseQueryStringParameters', () => {
|
|
7
|
-
tests.forEach(({ description, expected, param }) => {
|
|
8
|
-
const url = `/foo?${param}`
|
|
9
|
-
|
|
10
|
-
it(`should return ${description}`, () => {
|
|
11
|
-
const result = parseQueryStringParameters(url)
|
|
12
|
-
assert.deepEqual(result, expected)
|
|
13
|
-
})
|
|
14
|
-
})
|
|
15
|
-
})
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import splitHandlerPathAndName from '../splitHandlerPathAndName.js'
|
|
3
|
-
|
|
4
|
-
const tests = [
|
|
5
|
-
{
|
|
6
|
-
description: 'ruby handler with namespace resolution operator ::',
|
|
7
|
-
expected: ['./src/somefolder/source', 'LambdaFunctions::Handler.process'],
|
|
8
|
-
handler: './src/somefolder/source.LambdaFunctions::Handler.process',
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
description: 'ruby handler with multiple namespace resolution operators ::',
|
|
12
|
-
expected: [
|
|
13
|
-
'./src/somefolder/source',
|
|
14
|
-
'Functions::LambdaFunctions::Handler.process',
|
|
15
|
-
],
|
|
16
|
-
handler:
|
|
17
|
-
'./src/somefolder/source.Functions::LambdaFunctions::Handler.process',
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
description: 'ruby handler with namespace resolution operator ::, unnested',
|
|
21
|
-
expected: ['source', 'LambdaFunctions::Handler.process'],
|
|
22
|
-
handler: 'source.LambdaFunctions::Handler.process',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
description:
|
|
26
|
-
'ruby handler with multiple namespace resolution operators ::, unnested',
|
|
27
|
-
expected: ['./source', 'Functions::LambdaFunctions::Handler.process'],
|
|
28
|
-
handler: './source.Functions::LambdaFunctions::Handler.process',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
description: 'ruby handler from kernel',
|
|
32
|
-
expected: ['./src/somefolder/function', 'handler'],
|
|
33
|
-
handler: './src/somefolder/function.handler',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
description: 'generic handler',
|
|
37
|
-
expected: ['./src/somefolder/.handlers/handler', 'run'],
|
|
38
|
-
handler: './src/somefolder/.handlers/handler.run',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
description: 'generic handler, unnested',
|
|
42
|
-
expected: ['handler', 'run'],
|
|
43
|
-
handler: 'handler.run',
|
|
44
|
-
},
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
describe('splitHandlerPathAndName', () => {
|
|
48
|
-
tests.forEach(({ description, expected, handler }) => {
|
|
49
|
-
it(`should split ${description}`, () => {
|
|
50
|
-
const result = splitHandlerPathAndName(handler)
|
|
51
|
-
assert.deepEqual(result, expected)
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
})
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import unflatten from '../unflatten.js'
|
|
3
|
-
|
|
4
|
-
describe('unflatten', () => {
|
|
5
|
-
it('should work with empty array parameter', () => {
|
|
6
|
-
const value = []
|
|
7
|
-
const out = unflatten(value, 2)
|
|
8
|
-
const expected = []
|
|
9
|
-
|
|
10
|
-
assert.deepEqual(out, expected)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('should work with single pair parameter', () => {
|
|
14
|
-
const value = ['a', 1]
|
|
15
|
-
const out = unflatten(value, 2)
|
|
16
|
-
const expected = [['a', 1]]
|
|
17
|
-
|
|
18
|
-
assert.deepEqual(out, expected)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should work with multiple pair parameters', () => {
|
|
22
|
-
const value = ['a', 1, 'b', 2, 'c', 3]
|
|
23
|
-
const out = unflatten(value, 2)
|
|
24
|
-
const expected = [
|
|
25
|
-
['a', 1],
|
|
26
|
-
['b', 2],
|
|
27
|
-
['c', 3],
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
assert.deepEqual(out, expected)
|
|
31
|
-
})
|
|
32
|
-
})
|