@yopdev/dev-server 3.0.2-RC → 3.0.2-RC2
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/.github/workflows/npm-publish.yml +34 -0
- package/__tests__/bootstrap.test.ts +89 -0
- package/__tests__/deferred.test.ts +86 -0
- package/__tests__/event-proxy.test.ts +42 -0
- package/__tests__/lambda-http-proxy.test.ts +179 -0
- package/dist/__tests__/bootstrap.test.d.ts +1 -0
- package/dist/__tests__/bootstrap.test.js +90 -0
- package/dist/__tests__/deferred.test.d.ts +1 -0
- package/dist/__tests__/deferred.test.js +76 -0
- package/dist/__tests__/event-proxy.test.d.ts +1 -0
- package/dist/__tests__/event-proxy.test.js +37 -0
- package/dist/__tests__/lambda-http-proxy.test.d.ts +1 -0
- package/dist/__tests__/lambda-http-proxy.test.js +135 -0
- package/dist/src/assert.d.ts +1 -0
- package/dist/src/assert.js +9 -0
- package/dist/src/cloudformation-dynamodb-table.d.ts +13 -0
- package/dist/src/cloudformation-dynamodb-table.js +45 -0
- package/dist/src/cloudformation-event-proxy.d.ts +13 -0
- package/dist/src/cloudformation-event-proxy.js +25 -0
- package/dist/src/cloudformation-lambda-http-proxy.d.ts +14 -0
- package/dist/src/cloudformation-lambda-http-proxy.js +62 -0
- package/dist/src/cloudformation.d.ts +28 -0
- package/dist/src/cloudformation.js +50 -0
- package/dist/src/config.d.ts +31 -0
- package/dist/src/config.js +2 -0
- package/dist/src/container.d.ts +18 -0
- package/dist/src/container.js +33 -0
- package/dist/src/deferred.d.ts +4 -0
- package/dist/src/deferred.js +45 -0
- package/dist/src/dev-server.d.ts +19 -0
- package/dist/src/dev-server.js +63 -0
- package/dist/src/dynamodb.d.ts +16 -0
- package/dist/src/dynamodb.js +48 -0
- package/dist/src/event-proxy.d.ts +13 -0
- package/dist/src/event-proxy.js +68 -0
- package/dist/src/factories.d.ts +3 -0
- package/dist/src/factories.js +16 -0
- package/dist/src/http-server.d.ts +25 -0
- package/dist/src/http-server.js +37 -0
- package/dist/src/index.d.ts +24 -0
- package/dist/src/index.js +46 -0
- package/dist/src/internal-queue.d.ts +11 -0
- package/dist/src/internal-queue.js +53 -0
- package/dist/src/lambda-http-proxy.d.ts +28 -0
- package/dist/src/lambda-http-proxy.js +50 -0
- package/dist/src/localstack.d.ts +11 -0
- package/dist/src/localstack.js +62 -0
- package/dist/src/mappers.d.ts +25 -0
- package/dist/src/mappers.js +176 -0
- package/dist/src/pre-traffic-hooks.d.ts +2 -0
- package/dist/src/pre-traffic-hooks.js +19 -0
- package/dist/src/responses.d.ts +5 -0
- package/dist/src/responses.js +25 -0
- package/dist/src/s3.d.ts +7 -0
- package/dist/src/s3.js +20 -0
- package/dist/src/scheduled-tasks.d.ts +6 -0
- package/dist/src/scheduled-tasks.js +20 -0
- package/dist/src/services.d.ts +22 -0
- package/dist/src/services.js +26 -0
- package/dist/src/sns-http-proxy.d.ts +28 -0
- package/dist/src/sns-http-proxy.js +66 -0
- package/dist/src/sns.d.ts +15 -0
- package/dist/src/sns.js +35 -0
- package/dist/src/sqs.d.ts +13 -0
- package/dist/src/sqs.js +33 -0
- package/dist/src/stoppable.d.ts +2 -0
- package/dist/src/stoppable.js +15 -0
- package/dist/src/tunnel.d.ts +10 -0
- package/dist/src/tunnel.js +52 -0
- package/jest.config.js +7 -0
- package/package.json +2 -5
- package/src/assert.ts +4 -0
- package/src/cloudformation-dynamodb-table.ts +97 -0
- package/src/cloudformation-event-proxy.ts +61 -0
- package/src/cloudformation-lambda-http-proxy.ts +125 -0
- package/src/cloudformation.ts +95 -0
- package/src/config.ts +34 -0
- package/src/container.ts +82 -0
- package/src/deferred.ts +60 -0
- package/src/dev-server.ts +78 -0
- package/src/dynamodb.ts +62 -0
- package/src/event-proxy.ts +101 -0
- package/src/factories.ts +19 -0
- package/src/http-server.ts +59 -0
- package/src/index.ts +32 -0
- package/src/internal-queue.ts +89 -0
- package/src/lambda-http-proxy.ts +111 -0
- package/src/localstack.ts +74 -0
- package/src/mappers.ts +231 -0
- package/src/pre-traffic-hooks.ts +24 -0
- package/src/responses.ts +28 -0
- package/src/s3.ts +24 -0
- package/src/scheduled-tasks.ts +31 -0
- package/src/services.ts +46 -0
- package/src/sns-http-proxy.ts +109 -0
- package/src/sns.ts +49 -0
- package/src/sqs.ts +46 -0
- package/src/stoppable.ts +10 -0
- package/src/tunnel.ts +32 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm test
|
|
20
|
+
|
|
21
|
+
publish-npm:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 20
|
|
29
|
+
registry-url: https://registry.npmjs.org/
|
|
30
|
+
- run: npm ci
|
|
31
|
+
- run: npm run compile
|
|
32
|
+
- run: npm publish
|
|
33
|
+
env:
|
|
34
|
+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { DevServer, allOf } from "../src";
|
|
3
|
+
import { Service } from "../src/services";
|
|
4
|
+
import { fail } from "assert";
|
|
5
|
+
|
|
6
|
+
describe("dev server", () => {
|
|
7
|
+
it("bootstraps with defaults and an empty service", async () => {
|
|
8
|
+
const name = 'bootstrap';
|
|
9
|
+
const tested = await new DevServer(name, new Service({
|
|
10
|
+
name: 'service',
|
|
11
|
+
start: async () => Promise.resolve(),
|
|
12
|
+
stop: async () => Promise.resolve(),
|
|
13
|
+
})).start()
|
|
14
|
+
return expect(tested.name).toEqual(name)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it("calls service cleanup when init fails", async () => {
|
|
18
|
+
let called = false;
|
|
19
|
+
try {
|
|
20
|
+
await new DevServer('bootstrap', new Service({
|
|
21
|
+
name: 'failing-root',
|
|
22
|
+
start: async () => Promise.reject(new Error('fail!')),
|
|
23
|
+
stop: async () => Promise.resolve(called = true).then(),
|
|
24
|
+
})).start()
|
|
25
|
+
fail('should have failed')
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return expect(called)
|
|
28
|
+
.toStrictEqual(true)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("calls service cleanups on all components", async () => {
|
|
33
|
+
let calledOnParent = false;
|
|
34
|
+
let calledOnSibling = false;
|
|
35
|
+
let calledOnFailing = false;
|
|
36
|
+
try {
|
|
37
|
+
await new DevServer('bootstrap', allOf([
|
|
38
|
+
new Service({
|
|
39
|
+
name: 'parent',
|
|
40
|
+
start: async () => Promise.resolve(),
|
|
41
|
+
stop: async () => Promise.resolve(calledOnParent = true).then(),
|
|
42
|
+
}), allOf([
|
|
43
|
+
new Service({
|
|
44
|
+
name: 'failing-nested',
|
|
45
|
+
start: async () => Promise.reject(new Error('fail!')),
|
|
46
|
+
stop: async () => Promise.resolve(calledOnFailing = true).then(),
|
|
47
|
+
}),
|
|
48
|
+
new Service({
|
|
49
|
+
name: 'sibling',
|
|
50
|
+
start: async () => Promise.resolve(),
|
|
51
|
+
stop: async () => Promise.resolve(calledOnSibling = true).then(),
|
|
52
|
+
}),
|
|
53
|
+
])
|
|
54
|
+
])).start()
|
|
55
|
+
fail('should have failed')
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return expect([calledOnParent, calledOnFailing, calledOnSibling])
|
|
58
|
+
.toStrictEqual([true, true, true])
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it("propagates the exception from the failing service", async () => {
|
|
63
|
+
const error = new Error('fail!')
|
|
64
|
+
try {
|
|
65
|
+
await new DevServer('bootstrap', allOf([
|
|
66
|
+
new Service({
|
|
67
|
+
name: 'parent',
|
|
68
|
+
start: async () => Promise.resolve(),
|
|
69
|
+
stop: async () => Promise.resolve(),
|
|
70
|
+
}), allOf([
|
|
71
|
+
new Service({
|
|
72
|
+
name: 'failing-nested',
|
|
73
|
+
start: async () => Promise.reject(error),
|
|
74
|
+
stop: async () => Promise.resolve(),
|
|
75
|
+
}),
|
|
76
|
+
new Service({
|
|
77
|
+
name: 'sibling',
|
|
78
|
+
start: async () => Promise.resolve(),
|
|
79
|
+
stop: async () => Promise.resolve(),
|
|
80
|
+
}),
|
|
81
|
+
])
|
|
82
|
+
])).start()
|
|
83
|
+
fail('should have failed')
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return expect(e)
|
|
86
|
+
.toStrictEqual(error)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, test, expect } from "@jest/globals";
|
|
2
|
+
import { DevServer, DevServerConfig, Service, promised } from "../src";
|
|
3
|
+
import { StartedNetwork } from "testcontainers";
|
|
4
|
+
import { Sqs } from "../src/sqs";
|
|
5
|
+
import { Sns } from "../src/sns";
|
|
6
|
+
import { S3 } from "../src/s3";
|
|
7
|
+
import { DynamoDb } from "../src/dynamodb";
|
|
8
|
+
import { fail } from "assert";
|
|
9
|
+
|
|
10
|
+
describe("a promised service", () => {
|
|
11
|
+
test("calls start with devserver config", () => {
|
|
12
|
+
const aws = {
|
|
13
|
+
region: 'region',
|
|
14
|
+
endpoint: 'http://localhost',
|
|
15
|
+
credentials: {
|
|
16
|
+
accessKeyId: 'aki',
|
|
17
|
+
secretAccessKey: 'sak',
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
const c: DevServerConfig = {
|
|
21
|
+
raw: aws,
|
|
22
|
+
network: new StartedNetwork(undefined, 'network', undefined),
|
|
23
|
+
sqs: new Sqs(aws),
|
|
24
|
+
sns: new Sns(aws),
|
|
25
|
+
s3: new S3(aws),
|
|
26
|
+
dynamo: new DynamoDb(aws),
|
|
27
|
+
eventsProxy: {
|
|
28
|
+
topic: {
|
|
29
|
+
arn: 'arn:...',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
return expect(
|
|
34
|
+
promised(async (cfg: DevServerConfig) => Promise.resolve(new Service(
|
|
35
|
+
{
|
|
36
|
+
name: 'resolved',
|
|
37
|
+
start: (config) => Promise.resolve(cfg === config),
|
|
38
|
+
stop: () => Promise.resolve(),
|
|
39
|
+
}
|
|
40
|
+
))
|
|
41
|
+
).start(c)
|
|
42
|
+
)
|
|
43
|
+
.resolves.toStrictEqual(true)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test("calls the stop routine on failure starting promised service", async () => {
|
|
47
|
+
const error = new Error('failed starting service')
|
|
48
|
+
let stopped = false;
|
|
49
|
+
try {
|
|
50
|
+
await new DevServer('fail-on-start', promised(
|
|
51
|
+
() => Promise.resolve(
|
|
52
|
+
new Service({
|
|
53
|
+
name: 'fail-on-start',
|
|
54
|
+
start: async () => Promise.reject(error),
|
|
55
|
+
stop: async () => { stopped = true },
|
|
56
|
+
}))
|
|
57
|
+
)).start()
|
|
58
|
+
fail('should have thrown exception')
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return expect(stopped)
|
|
61
|
+
.toStrictEqual(true)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test("calls the cleanup routine on failure constructing promised service", async () => {
|
|
66
|
+
const error = {
|
|
67
|
+
cleanup: async () => stopped = true
|
|
68
|
+
}
|
|
69
|
+
let stopped = false;
|
|
70
|
+
try {
|
|
71
|
+
await new DevServer('fail-on-construct', promised(
|
|
72
|
+
() => Promise.resolve(
|
|
73
|
+
new Service({
|
|
74
|
+
name: 'fail-on-construct',
|
|
75
|
+
start: async () => Promise.resolve(),
|
|
76
|
+
stop: async () => Promise.resolve(),
|
|
77
|
+
}))
|
|
78
|
+
.then(() => Promise.reject(error)),
|
|
79
|
+
)).start()
|
|
80
|
+
fail('should have thrown exception')
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return expect(stopped)
|
|
83
|
+
.toStrictEqual(true)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, test, expect } from "@jest/globals";
|
|
2
|
+
import { allOf, DevServer, eventsProxy, Service } from "../src";
|
|
3
|
+
import { EventHandler } from "../src/event-proxy";
|
|
4
|
+
import { Sns } from "../src/sns";
|
|
5
|
+
|
|
6
|
+
describe("the event proxy", () => {
|
|
7
|
+
test("handles an implementation error gracefully", async () => {
|
|
8
|
+
let sns: Sns;
|
|
9
|
+
let topic: { arn: string; };
|
|
10
|
+
let invoked = false;
|
|
11
|
+
const handler: EventHandler = {
|
|
12
|
+
name: 'fail',
|
|
13
|
+
handler: async () => {
|
|
14
|
+
invoked = true;
|
|
15
|
+
return Promise.reject();
|
|
16
|
+
},
|
|
17
|
+
matcher: () => true,
|
|
18
|
+
}
|
|
19
|
+
const devServer = await new DevServer(
|
|
20
|
+
'events-proxy',
|
|
21
|
+
allOf([
|
|
22
|
+
new Service({
|
|
23
|
+
name: 'config-capture',
|
|
24
|
+
start: (config) => {
|
|
25
|
+
sns = config.sns;
|
|
26
|
+
topic = config.eventsProxy.topic;
|
|
27
|
+
return Promise.resolve()
|
|
28
|
+
},
|
|
29
|
+
stop: () => Promise.resolve(),
|
|
30
|
+
}),
|
|
31
|
+
eventsProxy('test', {
|
|
32
|
+
handlers: [handler]
|
|
33
|
+
}),
|
|
34
|
+
])
|
|
35
|
+
).start();
|
|
36
|
+
return expect(sns.publish(topic, '{}')
|
|
37
|
+
.then(() => new Promise(resolve => setTimeout(resolve, 200)))
|
|
38
|
+
.then(() => invoked)
|
|
39
|
+
.finally(() => devServer.stop())
|
|
40
|
+
).resolves.toStrictEqual(true)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { DevServer } from "../src";
|
|
3
|
+
import { UNAUTHORIZED, newLambdaHttpProxy } from "../src/lambda-http-proxy";
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
import { v2 } from "../src/mappers";
|
|
6
|
+
|
|
7
|
+
describe("the lambda http proxy", () => {
|
|
8
|
+
const payloadVersionV2Mapper = v2().mapper;
|
|
9
|
+
|
|
10
|
+
it("invokes the handler with the heaviest route", async () => {
|
|
11
|
+
let endpoint: string
|
|
12
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
13
|
+
'service',
|
|
14
|
+
{
|
|
15
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
16
|
+
routes: [
|
|
17
|
+
{ method: /GET/, path: /\/a\/.*\/b\/.*\/c/, weight: 3, handler: () => Promise.resolve({ statusCode: 200, body: '' }) },
|
|
18
|
+
{ method: /GET/, path: /\/a\/.*\/b\/.*/, weight: 2, handler: () => Promise.resolve({ statusCode: 400, body: '' }) },
|
|
19
|
+
{ method: /GET/, path: /\/a\/.*/, weight: 2, handler: () => Promise.resolve({ statusCode: 400, body: '' }) },
|
|
20
|
+
],
|
|
21
|
+
mapper: payloadVersionV2Mapper,
|
|
22
|
+
},
|
|
23
|
+
async (url) => { endpoint = url; }
|
|
24
|
+
)).start()
|
|
25
|
+
return expect(axios.get(`${endpoint}a/1/b/2/c`)
|
|
26
|
+
.then(r => r.status)
|
|
27
|
+
.finally(async () => tested.stop())
|
|
28
|
+
)
|
|
29
|
+
.resolves
|
|
30
|
+
.toEqual(200)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("supports a single 'catch all' route", async () => {
|
|
34
|
+
let endpoint: string
|
|
35
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
36
|
+
'service',
|
|
37
|
+
{
|
|
38
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
39
|
+
routes: [
|
|
40
|
+
{ method: /.*/, path: /.*/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }) },
|
|
41
|
+
],
|
|
42
|
+
mapper: payloadVersionV2Mapper,
|
|
43
|
+
},
|
|
44
|
+
async (url) => { endpoint = url; }
|
|
45
|
+
)).start()
|
|
46
|
+
return expect(axios.post(`${endpoint}find-me`)
|
|
47
|
+
.then(r => ({ status: r.status, body: r.data }))
|
|
48
|
+
.finally(async () => tested.stop())
|
|
49
|
+
)
|
|
50
|
+
.resolves
|
|
51
|
+
.toStrictEqual({ status: 200, body: 'handled!' })
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("returns unauthorized when the authorizer rejects", async () => {
|
|
55
|
+
let endpoint: string
|
|
56
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
57
|
+
'service',
|
|
58
|
+
{
|
|
59
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
60
|
+
routes: [
|
|
61
|
+
{ method: /.*/, path: /.*/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }), authorizer: () => Promise.reject(UNAUTHORIZED) },
|
|
62
|
+
],
|
|
63
|
+
mapper: payloadVersionV2Mapper,
|
|
64
|
+
},
|
|
65
|
+
async (url) => { endpoint = url; }
|
|
66
|
+
)).start()
|
|
67
|
+
return expect(axios.get(endpoint, { validateStatus: () => true })
|
|
68
|
+
.then(r => r.status)
|
|
69
|
+
.finally(async () => tested.stop())
|
|
70
|
+
)
|
|
71
|
+
.resolves
|
|
72
|
+
.toEqual(401)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("triggers the fallback handler when no route matches a request", async () => {
|
|
76
|
+
let endpoint: string
|
|
77
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
78
|
+
'service',
|
|
79
|
+
{
|
|
80
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
81
|
+
routes: [
|
|
82
|
+
{ method: /.*/, path: /\a/, weight: 0, handler: () => Promise.resolve({ statusCode: 200, body: 'handled!' }) },
|
|
83
|
+
],
|
|
84
|
+
mapper: payloadVersionV2Mapper,
|
|
85
|
+
},
|
|
86
|
+
async (url) => { endpoint = url; }
|
|
87
|
+
)).start()
|
|
88
|
+
return expect(axios.get(`${endpoint}b`, { validateStatus: () => true })
|
|
89
|
+
.then(r => ({ status: r.status, body: r.data }))
|
|
90
|
+
.finally(async () => tested.stop())
|
|
91
|
+
)
|
|
92
|
+
.resolves
|
|
93
|
+
.toStrictEqual({ status: 404, body: 'no route found to handle GET to /b' })
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it("handler has access to the context", async () => {
|
|
97
|
+
const expectedContext = 'hello world!'
|
|
98
|
+
let endpoint: string
|
|
99
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
100
|
+
'service',
|
|
101
|
+
{
|
|
102
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
103
|
+
routes: [
|
|
104
|
+
{
|
|
105
|
+
method: /.*/,
|
|
106
|
+
path: /\a/,
|
|
107
|
+
weight: 0,
|
|
108
|
+
handler: (_: unknown, context: string) => Promise.resolve({ statusCode: 200, body: context })
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
context: () => expectedContext,
|
|
112
|
+
mapper: payloadVersionV2Mapper,
|
|
113
|
+
},
|
|
114
|
+
async (url) => { endpoint = url; }
|
|
115
|
+
)).start()
|
|
116
|
+
return expect(axios.get(`${endpoint}a`)
|
|
117
|
+
.then(r => r.data)
|
|
118
|
+
.finally(async () => tested.stop())
|
|
119
|
+
)
|
|
120
|
+
.resolves
|
|
121
|
+
.toStrictEqual(expectedContext)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it("can set cookies", async () => {
|
|
125
|
+
let endpoint: string
|
|
126
|
+
|
|
127
|
+
const handler = async (event: object, context: unknown) => {
|
|
128
|
+
return {
|
|
129
|
+
statusCode: 200,
|
|
130
|
+
cookies: ['galletita=criollita'],
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
135
|
+
'service',
|
|
136
|
+
{
|
|
137
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
138
|
+
routes: [
|
|
139
|
+
{ method: /GET/, path: /\/a/, weight: 0, handler: handler },
|
|
140
|
+
],
|
|
141
|
+
mapper: payloadVersionV2Mapper,
|
|
142
|
+
},
|
|
143
|
+
async (url) => { endpoint = url; }
|
|
144
|
+
)).start();
|
|
145
|
+
|
|
146
|
+
return expect(axios.get(`${endpoint}a`)
|
|
147
|
+
.then(r => r.headers["set-cookie"])
|
|
148
|
+
.finally(async () => tested.stop())
|
|
149
|
+
).resolves.toContain('galletita=criollita')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it("can read cookies", async () => {
|
|
153
|
+
let endpoint: string
|
|
154
|
+
|
|
155
|
+
const handler = async (event: object, context: unknown) => {
|
|
156
|
+
return {
|
|
157
|
+
statusCode: 200,
|
|
158
|
+
body: event["headers"].cookie,
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const tested = await new DevServer('lambda-http-proxy', newLambdaHttpProxy(
|
|
163
|
+
'service',
|
|
164
|
+
{
|
|
165
|
+
settings: { protocol: 'http:', host: '127.0.0.1', port: undefined },
|
|
166
|
+
routes: [
|
|
167
|
+
{ method: /GET/, path: /\/a/, weight: 0, handler: handler },
|
|
168
|
+
],
|
|
169
|
+
mapper: payloadVersionV2Mapper,
|
|
170
|
+
},
|
|
171
|
+
async (url) => { endpoint = url; }
|
|
172
|
+
)).start();
|
|
173
|
+
|
|
174
|
+
return expect(axios.get(`${endpoint}a`, { headers: { cookie: ['galletita=criollita'] } })
|
|
175
|
+
.then(r => r.data)
|
|
176
|
+
.finally(async () => tested.stop())
|
|
177
|
+
).resolves.toContain('galletita=criollita')
|
|
178
|
+
})
|
|
179
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const src_1 = require("../src");
|
|
5
|
+
const services_1 = require("../src/services");
|
|
6
|
+
const assert_1 = require("assert");
|
|
7
|
+
(0, globals_1.describe)("dev server", () => {
|
|
8
|
+
(0, globals_1.it)("bootstraps with defaults and an empty service", async () => {
|
|
9
|
+
const name = 'bootstrap';
|
|
10
|
+
const tested = await new src_1.DevServer(name, new services_1.Service({
|
|
11
|
+
name: 'service',
|
|
12
|
+
start: async () => Promise.resolve(),
|
|
13
|
+
stop: async () => Promise.resolve(),
|
|
14
|
+
})).start();
|
|
15
|
+
return (0, globals_1.expect)(tested.name).toEqual(name);
|
|
16
|
+
});
|
|
17
|
+
(0, globals_1.it)("calls service cleanup when init fails", async () => {
|
|
18
|
+
let called = false;
|
|
19
|
+
try {
|
|
20
|
+
await new src_1.DevServer('bootstrap', new services_1.Service({
|
|
21
|
+
name: 'failing-root',
|
|
22
|
+
start: async () => Promise.reject(new Error('fail!')),
|
|
23
|
+
stop: async () => Promise.resolve(called = true).then(),
|
|
24
|
+
})).start();
|
|
25
|
+
(0, assert_1.fail)('should have failed');
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
return (0, globals_1.expect)(called)
|
|
29
|
+
.toStrictEqual(true);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
(0, globals_1.it)("calls service cleanups on all components", async () => {
|
|
33
|
+
let calledOnParent = false;
|
|
34
|
+
let calledOnSibling = false;
|
|
35
|
+
let calledOnFailing = false;
|
|
36
|
+
try {
|
|
37
|
+
await new src_1.DevServer('bootstrap', (0, src_1.allOf)([
|
|
38
|
+
new services_1.Service({
|
|
39
|
+
name: 'parent',
|
|
40
|
+
start: async () => Promise.resolve(),
|
|
41
|
+
stop: async () => Promise.resolve(calledOnParent = true).then(),
|
|
42
|
+
}), (0, src_1.allOf)([
|
|
43
|
+
new services_1.Service({
|
|
44
|
+
name: 'failing-nested',
|
|
45
|
+
start: async () => Promise.reject(new Error('fail!')),
|
|
46
|
+
stop: async () => Promise.resolve(calledOnFailing = true).then(),
|
|
47
|
+
}),
|
|
48
|
+
new services_1.Service({
|
|
49
|
+
name: 'sibling',
|
|
50
|
+
start: async () => Promise.resolve(),
|
|
51
|
+
stop: async () => Promise.resolve(calledOnSibling = true).then(),
|
|
52
|
+
}),
|
|
53
|
+
])
|
|
54
|
+
])).start();
|
|
55
|
+
(0, assert_1.fail)('should have failed');
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return (0, globals_1.expect)([calledOnParent, calledOnFailing, calledOnSibling])
|
|
59
|
+
.toStrictEqual([true, true, true]);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
(0, globals_1.it)("propagates the exception from the failing service", async () => {
|
|
63
|
+
const error = new Error('fail!');
|
|
64
|
+
try {
|
|
65
|
+
await new src_1.DevServer('bootstrap', (0, src_1.allOf)([
|
|
66
|
+
new services_1.Service({
|
|
67
|
+
name: 'parent',
|
|
68
|
+
start: async () => Promise.resolve(),
|
|
69
|
+
stop: async () => Promise.resolve(),
|
|
70
|
+
}), (0, src_1.allOf)([
|
|
71
|
+
new services_1.Service({
|
|
72
|
+
name: 'failing-nested',
|
|
73
|
+
start: async () => Promise.reject(error),
|
|
74
|
+
stop: async () => Promise.resolve(),
|
|
75
|
+
}),
|
|
76
|
+
new services_1.Service({
|
|
77
|
+
name: 'sibling',
|
|
78
|
+
start: async () => Promise.resolve(),
|
|
79
|
+
stop: async () => Promise.resolve(),
|
|
80
|
+
}),
|
|
81
|
+
])
|
|
82
|
+
])).start();
|
|
83
|
+
(0, assert_1.fail)('should have failed');
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
return (0, globals_1.expect)(e)
|
|
87
|
+
.toStrictEqual(error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const src_1 = require("../src");
|
|
5
|
+
const testcontainers_1 = require("testcontainers");
|
|
6
|
+
const sqs_1 = require("../src/sqs");
|
|
7
|
+
const sns_1 = require("../src/sns");
|
|
8
|
+
const s3_1 = require("../src/s3");
|
|
9
|
+
const dynamodb_1 = require("../src/dynamodb");
|
|
10
|
+
const assert_1 = require("assert");
|
|
11
|
+
(0, globals_1.describe)("a promised service", () => {
|
|
12
|
+
(0, globals_1.test)("calls start with devserver config", () => {
|
|
13
|
+
const aws = {
|
|
14
|
+
region: 'region',
|
|
15
|
+
endpoint: 'http://localhost',
|
|
16
|
+
credentials: {
|
|
17
|
+
accessKeyId: 'aki',
|
|
18
|
+
secretAccessKey: 'sak',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const c = {
|
|
22
|
+
raw: aws,
|
|
23
|
+
network: new testcontainers_1.StartedNetwork(undefined, 'network', undefined),
|
|
24
|
+
sqs: new sqs_1.Sqs(aws),
|
|
25
|
+
sns: new sns_1.Sns(aws),
|
|
26
|
+
s3: new s3_1.S3(aws),
|
|
27
|
+
dynamo: new dynamodb_1.DynamoDb(aws),
|
|
28
|
+
eventsProxy: {
|
|
29
|
+
topic: {
|
|
30
|
+
arn: 'arn:...',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
return (0, globals_1.expect)((0, src_1.promised)(async (cfg) => Promise.resolve(new src_1.Service({
|
|
35
|
+
name: 'resolved',
|
|
36
|
+
start: (config) => Promise.resolve(cfg === config),
|
|
37
|
+
stop: () => Promise.resolve(),
|
|
38
|
+
}))).start(c))
|
|
39
|
+
.resolves.toStrictEqual(true);
|
|
40
|
+
});
|
|
41
|
+
(0, globals_1.test)("calls the stop routine on failure starting promised service", async () => {
|
|
42
|
+
const error = new Error('failed starting service');
|
|
43
|
+
let stopped = false;
|
|
44
|
+
try {
|
|
45
|
+
await new src_1.DevServer('fail-on-start', (0, src_1.promised)(() => Promise.resolve(new src_1.Service({
|
|
46
|
+
name: 'fail-on-start',
|
|
47
|
+
start: async () => Promise.reject(error),
|
|
48
|
+
stop: async () => { stopped = true; },
|
|
49
|
+
})))).start();
|
|
50
|
+
(0, assert_1.fail)('should have thrown exception');
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return (0, globals_1.expect)(stopped)
|
|
54
|
+
.toStrictEqual(true);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
(0, globals_1.test)("calls the cleanup routine on failure constructing promised service", async () => {
|
|
58
|
+
const error = {
|
|
59
|
+
cleanup: async () => stopped = true
|
|
60
|
+
};
|
|
61
|
+
let stopped = false;
|
|
62
|
+
try {
|
|
63
|
+
await new src_1.DevServer('fail-on-construct', (0, src_1.promised)(() => Promise.resolve(new src_1.Service({
|
|
64
|
+
name: 'fail-on-construct',
|
|
65
|
+
start: async () => Promise.resolve(),
|
|
66
|
+
stop: async () => Promise.resolve(),
|
|
67
|
+
}))
|
|
68
|
+
.then(() => Promise.reject(error)))).start();
|
|
69
|
+
(0, assert_1.fail)('should have thrown exception');
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
return (0, globals_1.expect)(stopped)
|
|
73
|
+
.toStrictEqual(true);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const src_1 = require("../src");
|
|
5
|
+
(0, globals_1.describe)("the event proxy", () => {
|
|
6
|
+
(0, globals_1.test)("handles an implementation error gracefully", async () => {
|
|
7
|
+
let sns;
|
|
8
|
+
let topic;
|
|
9
|
+
let invoked = false;
|
|
10
|
+
const handler = {
|
|
11
|
+
name: 'fail',
|
|
12
|
+
handler: async () => {
|
|
13
|
+
invoked = true;
|
|
14
|
+
return Promise.reject();
|
|
15
|
+
},
|
|
16
|
+
matcher: () => true,
|
|
17
|
+
};
|
|
18
|
+
const devServer = await new src_1.DevServer('events-proxy', (0, src_1.allOf)([
|
|
19
|
+
new src_1.Service({
|
|
20
|
+
name: 'config-capture',
|
|
21
|
+
start: (config) => {
|
|
22
|
+
sns = config.sns;
|
|
23
|
+
topic = config.eventsProxy.topic;
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
},
|
|
26
|
+
stop: () => Promise.resolve(),
|
|
27
|
+
}),
|
|
28
|
+
(0, src_1.eventsProxy)('test', {
|
|
29
|
+
handlers: [handler]
|
|
30
|
+
}),
|
|
31
|
+
])).start();
|
|
32
|
+
return (0, globals_1.expect)(sns.publish(topic, '{}')
|
|
33
|
+
.then(() => new Promise(resolve => setTimeout(resolve, 200)))
|
|
34
|
+
.then(() => invoked)
|
|
35
|
+
.finally(() => devServer.stop())).resolves.toStrictEqual(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|