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.
Files changed (27) hide show
  1. package/package.json +4 -10
  2. package/src/ServerlessOffline.js +3 -3
  3. package/src/events/http/Endpoint.js +2 -2
  4. package/src/events/http/authJWTSettingsExtractor.js +14 -10
  5. package/src/lambda/handler-runner/HandlerRunner.js +2 -13
  6. package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +1 -1
  7. package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +1 -1
  8. package/src/lambda/handler-runner/python-runner/PythonRunner.js +12 -10
  9. package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +1 -1
  10. package/src/lambda/__tests__/LambdaContext.test.js +0 -30
  11. package/src/lambda/__tests__/LambdaFunction.test.js +0 -196
  12. package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsJSONObject-fixture.js +0 -46
  13. package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString-fixture.js +0 -46
  14. package/src/lambda/__tests__/fixtures/Lambda/package.json +0 -3
  15. package/src/lambda/__tests__/fixtures/lambdaFunction-fixture.js +0 -145
  16. package/src/lambda/__tests__/fixtures/package.json +0 -3
  17. package/src/lambda/__tests__/routes/invocations/InvocationsController.test.js +0 -42
  18. package/src/utils/__tests__/createUniqueId.test.js +0 -18
  19. package/src/utils/__tests__/formatToClfTime.test.js +0 -14
  20. package/src/utils/__tests__/generateHapiPath.test.js +0 -46
  21. package/src/utils/__tests__/lowerCaseKeys.test.js +0 -30
  22. package/src/utils/__tests__/parseHeaders.test.js +0 -13
  23. package/src/utils/__tests__/parseMultiValueHeaders.test.js +0 -24
  24. package/src/utils/__tests__/parseMultiValueQueryStringParameters.test.js +0 -159
  25. package/src/utils/__tests__/parseQueryStringParameters.test.js +0 -15
  26. package/src/utils/__tests__/splitHandlerPathAndName.test.js +0 -54
  27. 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.0",
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.1187.0",
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.21.0",
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.21.0",
224
+ "serverless": "^3.22.0",
231
225
  "standard-version": "^9.5.0"
232
226
  },
233
227
  "peerDependencies": {
@@ -245,9 +245,9 @@ export default class ServerlessOffline {
245
245
 
246
246
  log.notice()
247
247
  log.notice(
248
- `Starting Offline at stage ${provider.stage} ${chalk.gray(
249
- `(${provider.region})`,
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, './templates/offline-default.req.vm'),
13
+ resolve(__dirname, 'templates/offline-default.req.vm'),
14
14
  'utf8',
15
15
  )
16
16
  const defaultResponseTemplate = readFileSync(
17
- resolve(__dirname, './templates/offline-default.res.vm'),
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(
@@ -59,7 +59,7 @@ export default class ChildProcessRunner {
59
59
  rej(data.error)
60
60
  return
61
61
  }
62
- res(data)
62
+ res(data.result)
63
63
  })
64
64
  })
65
65
  } catch (err) {
@@ -41,5 +41,5 @@ process.on('message', async (messageData) => {
41
41
  const result = await inProcessRunner.run(event, context)
42
42
 
43
43
  // TODO check serializeability (contains function, symbol etc)
44
- process.send(result)
44
+ process.send({ result })
45
45
  })
@@ -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.handlerProcess = spawn(
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.handlerProcess.stdout.readline = createInterface({
56
- input: this.handlerProcess.stdout,
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.handlerProcess.kill()
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.handlerProcess.stdout.readline.removeListener('line', onLine)
117
- this.handlerProcess.stderr.removeListener('data', onErr)
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.handlerProcess.stdout.readline.on('line', onLine)
127
- this.handlerProcess.stderr.on('data', onErr)
128
+ this.#handlerProcess.stdout.readline.on('line', onLine)
129
+ this.#handlerProcess.stderr.on('data', onErr)
128
130
 
129
131
  process.nextTick(() => {
130
- this.handlerProcess.stdin.write(input)
131
- this.handlerProcess.stdin.write('\n')
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, './workerThreadHelper.js')
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
- }
@@ -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,3 +0,0 @@
1
- {
2
- "type": "module"
3
- }
@@ -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,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }
@@ -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
- })