@creator.co/wapi 1.7.1-alpha3 → 1.7.1

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 (114) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +60 -0
  3. package/.github/workflows/npmpublish.yml +11 -0
  4. package/.github/workflows/prs.yml +13 -0
  5. package/dist/package-lock.json +2 -2
  6. package/dist/package.json +1 -1
  7. package/jest.config.ts +33 -0
  8. package/jest.smoke.config.ts +35 -0
  9. package/package.json +1 -1
  10. package/tests/API/Request.test.ts +273 -0
  11. package/tests/API/Response.test.ts +367 -0
  12. package/tests/API/Utils.test.ts +167 -0
  13. package/tests/BaseEvent/EventProcessor.test.ts +261 -0
  14. package/tests/BaseEvent/Process.test.ts +49 -0
  15. package/tests/BaseEvent/Transaction.test.ts +408 -0
  16. package/tests/Cache/Redis-client.test.ts +90 -0
  17. package/tests/Cache/Redis-cluster.test.ts +100 -0
  18. package/tests/Config/Config.test.ts +205 -0
  19. package/tests/Config/EnvironmentVar.test.ts +250 -0
  20. package/tests/Crypto/Crypto.test.ts +88 -0
  21. package/tests/Crypto/JWT.test.ts +92 -0
  22. package/tests/Database/DatabaseManager.test.ts +71 -0
  23. package/tests/Database/integrations/knex/KnexDatabase.test.ts +76 -0
  24. package/tests/Database/integrations/knex/KnexTransaction.test.ts +149 -0
  25. package/tests/Database/integrations/kysely/KyselyDatabase.test.ts +113 -0
  26. package/tests/Database/integrations/kysely/KyselyTransaction.test.ts +119 -0
  27. package/tests/Database/integrations/pg/PostgresDatabase.test.ts +76 -0
  28. package/tests/Database/integrations/pg/PostgresTransaction.test.ts +118 -0
  29. package/tests/Logger/Logger.test.ts +219 -0
  30. package/tests/Mailer/Mailer.test.ts +59 -0
  31. package/tests/Publisher/Publisher.test.ts +94 -0
  32. package/tests/Server/RouteResolver.test.ts +102 -0
  33. package/tests/Server/Router.test.ts +39 -0
  34. package/tests/Server/lib/ContainerServer.test.ts +531 -0
  35. package/tests/Server/lib/Server.test.ts +12 -0
  36. package/tests/Server/lib/container/GenericHandler.test.ts +131 -0
  37. package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
  38. package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
  39. package/tests/Server/lib/container/Proxy.test.ts +268 -0
  40. package/tests/Server/lib/container/Utils.test.ts +47 -0
  41. package/tests/Test.utils.ts +74 -0
  42. package/tests/Validation/Validator.test.ts +76 -0
  43. package/tsconfig.json +26 -0
  44. package/tsconfig.smoke.json +26 -0
  45. package/coverage/clover.xml +0 -1088
  46. package/coverage/coverage/coverage.txt +0 -40
  47. package/coverage/coverage-final.json +0 -37
  48. package/coverage/coverage-summary.json +0 -38
  49. package/coverage/coverage.txt +0 -59
  50. package/coverage/lcov-report/base.css +0 -224
  51. package/coverage/lcov-report/block-navigation.js +0 -87
  52. package/coverage/lcov-report/favicon.png +0 -0
  53. package/coverage/lcov-report/index.html +0 -371
  54. package/coverage/lcov-report/prettify.css +0 -1
  55. package/coverage/lcov-report/prettify.js +0 -2
  56. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  57. package/coverage/lcov-report/sorter.js +0 -196
  58. package/coverage/lcov-report/src/API/Request.ts.html +0 -727
  59. package/coverage/lcov-report/src/API/Response.ts.html +0 -1189
  60. package/coverage/lcov-report/src/API/Utils.ts.html +0 -313
  61. package/coverage/lcov-report/src/API/index.html +0 -131
  62. package/coverage/lcov-report/src/BaseEvent/EventProcessor.ts.html +0 -496
  63. package/coverage/lcov-report/src/BaseEvent/Process.ts.html +0 -346
  64. package/coverage/lcov-report/src/BaseEvent/Transaction.ts.html +0 -1015
  65. package/coverage/lcov-report/src/BaseEvent/index.html +0 -146
  66. package/coverage/lcov-report/src/Cache/Redis.ts.html +0 -367
  67. package/coverage/lcov-report/src/Cache/index.html +0 -116
  68. package/coverage/lcov-report/src/Config/Configuration.ts.html +0 -700
  69. package/coverage/lcov-report/src/Config/EnvironmentVar.ts.html +0 -526
  70. package/coverage/lcov-report/src/Config/index.html +0 -131
  71. package/coverage/lcov-report/src/Crypto/Crypto.ts.html +0 -352
  72. package/coverage/lcov-report/src/Crypto/JWT.ts.html +0 -337
  73. package/coverage/lcov-report/src/Crypto/index.html +0 -131
  74. package/coverage/lcov-report/src/Database/Database.ts.html +0 -151
  75. package/coverage/lcov-report/src/Database/DatabaseManager.ts.html +0 -289
  76. package/coverage/lcov-report/src/Database/DatabaseTransaction.ts.html +0 -595
  77. package/coverage/lcov-report/src/Database/index.html +0 -161
  78. package/coverage/lcov-report/src/Database/index.ts.html +0 -169
  79. package/coverage/lcov-report/src/Database/integrations/knex/KnexDatabase.ts.html +0 -283
  80. package/coverage/lcov-report/src/Database/integrations/knex/KnexTransaction.ts.html +0 -337
  81. package/coverage/lcov-report/src/Database/integrations/knex/index.html +0 -131
  82. package/coverage/lcov-report/src/Database/integrations/kysely/KyselyDatabase.ts.html +0 -376
  83. package/coverage/lcov-report/src/Database/integrations/kysely/KyselyTransaction.ts.html +0 -601
  84. package/coverage/lcov-report/src/Database/integrations/kysely/index.html +0 -131
  85. package/coverage/lcov-report/src/Database/integrations/pgsql/PostgresDatabase.ts.html +0 -250
  86. package/coverage/lcov-report/src/Database/integrations/pgsql/PostgresTransaction.ts.html +0 -346
  87. package/coverage/lcov-report/src/Database/integrations/pgsql/index.html +0 -131
  88. package/coverage/lcov-report/src/Database/types.d.ts.html +0 -232
  89. package/coverage/lcov-report/src/Globals.ts.html +0 -394
  90. package/coverage/lcov-report/src/Logger/Logger.ts.html +0 -1138
  91. package/coverage/lcov-report/src/Logger/index.html +0 -116
  92. package/coverage/lcov-report/src/Mailer/Mailer.ts.html +0 -754
  93. package/coverage/lcov-report/src/Mailer/index.html +0 -116
  94. package/coverage/lcov-report/src/Publisher/Publisher.ts.html +0 -460
  95. package/coverage/lcov-report/src/Publisher/index.html +0 -116
  96. package/coverage/lcov-report/src/Server/RouteResolver.ts.html +0 -442
  97. package/coverage/lcov-report/src/Server/Router.ts.html +0 -616
  98. package/coverage/lcov-report/src/Server/index.html +0 -131
  99. package/coverage/lcov-report/src/Server/lib/ContainerServer.ts.html +0 -280
  100. package/coverage/lcov-report/src/Server/lib/Server.ts.html +0 -403
  101. package/coverage/lcov-report/src/Server/lib/container/GenericHandler.ts.html +0 -331
  102. package/coverage/lcov-report/src/Server/lib/container/GenericHandlerEvent.ts.html +0 -547
  103. package/coverage/lcov-report/src/Server/lib/container/HealthHandler.ts.html +0 -118
  104. package/coverage/lcov-report/src/Server/lib/container/Proxy.ts.html +0 -619
  105. package/coverage/lcov-report/src/Server/lib/container/Utils.ts.html +0 -184
  106. package/coverage/lcov-report/src/Server/lib/container/index.html +0 -176
  107. package/coverage/lcov-report/src/Server/lib/index.html +0 -131
  108. package/coverage/lcov-report/src/Util/AsyncSingleton.ts.html +0 -343
  109. package/coverage/lcov-report/src/Util/Utils.ts.html +0 -313
  110. package/coverage/lcov-report/src/Util/index.html +0 -131
  111. package/coverage/lcov-report/src/Validation/Validator.ts.html +0 -217
  112. package/coverage/lcov-report/src/Validation/index.html +0 -116
  113. package/coverage/lcov-report/src/index.html +0 -116
  114. package/coverage/lcov.info +0 -2326
@@ -0,0 +1,102 @@
1
+ import { HttpMethod } from '../../src/API/Request'
2
+ import { Route } from '../../src/Server/Router'
3
+ import RouteResolver from '../../src/Server/RouteResolver'
4
+
5
+ const mockRoute = (method: HttpMethod, path: string) =>
6
+ ({
7
+ method: method,
8
+ path: path,
9
+ }) as any as Route
10
+
11
+ describe('RouteResolver', () => {
12
+ test('no routes configured', () => {
13
+ parameterizedTest({}, () => [
14
+ [HttpMethod.GET, '/', undefined],
15
+ [HttpMethod.GET, '', undefined],
16
+ [HttpMethod.GET, 'hjdah', undefined],
17
+ ])
18
+ })
19
+
20
+ test('one route', () => {
21
+ parameterizedTest(
22
+ {
23
+ route: mockRoute(HttpMethod.GET, '/'),
24
+ },
25
+ routes => [
26
+ [HttpMethod.GET, '/', routes.route],
27
+ [HttpMethod.GET, '', routes.route],
28
+ [HttpMethod.GET, 'hjdah', undefined],
29
+ [HttpMethod.POST, '/', undefined],
30
+ [HttpMethod.DELETE, '/', undefined],
31
+ ]
32
+ )
33
+ })
34
+
35
+ test('basic matching', () => {
36
+ parameterizedTest(
37
+ {
38
+ getBase: mockRoute(HttpMethod.GET, '/'),
39
+ getA: mockRoute(HttpMethod.GET, '/a'),
40
+ getB: mockRoute(HttpMethod.GET, '/b'),
41
+ postBase: mockRoute(HttpMethod.POST, '/'),
42
+ postA: mockRoute(HttpMethod.POST, '/a'),
43
+ variable: mockRoute(HttpMethod.GET, '/:a'),
44
+ getAb: mockRoute(HttpMethod.GET, '/a/b'),
45
+ },
46
+ routes => [
47
+ [HttpMethod.GET, '/', routes.getBase],
48
+ [HttpMethod.GET, '/a', routes.getA],
49
+ [HttpMethod.GET, '/b', routes.getB],
50
+ [HttpMethod.POST, '/', routes.postBase],
51
+ [HttpMethod.POST, '/a', routes.postA],
52
+ [HttpMethod.GET, '/c', routes.variable],
53
+ [HttpMethod.GET, '/a/b', routes.getAb],
54
+ ]
55
+ )
56
+ })
57
+
58
+ test('path variables', () => {
59
+ parameterizedTest(
60
+ {
61
+ path_vars: mockRoute(HttpMethod.GET, '/base/:a/:b/:c'),
62
+ abc: mockRoute(HttpMethod.GET, '/base/a/b/c'),
63
+ abcd: mockRoute(HttpMethod.GET, '/base/a/b/c/d'),
64
+ abc_path: mockRoute(HttpMethod.GET, '/base/a/b/c/:d'),
65
+ },
66
+ routes => [
67
+ [HttpMethod.GET, '/base/1/2/3', routes.path_vars],
68
+ [HttpMethod.GET, '/base/a/b/c', routes.abc],
69
+ [HttpMethod.GET, '/base/a/b/c/d', routes.abcd],
70
+ [HttpMethod.GET, '/base/a/b/c/u', routes.abc_path],
71
+ [HttpMethod.GET, '/base/a/b', undefined],
72
+ [HttpMethod.GET, '/base/a/b/c/d/e', undefined],
73
+ [HttpMethod.GET, '/base/a/b/c/d/e/f', undefined],
74
+ ]
75
+ )
76
+ })
77
+
78
+ test('fails to construct with duplicate routes', () => {
79
+ expect(
80
+ () =>
81
+ new RouteResolver({
82
+ routes: [
83
+ mockRoute(HttpMethod.GET, '/a/b/c/:d/:e/b'),
84
+ mockRoute(HttpMethod.GET, '/a/b/c/:jshj/:e/b'),
85
+ ],
86
+ })
87
+ ).toThrowError('Duplicate route: GET: /a/b/c/:jshj/:e/b')
88
+ })
89
+ })
90
+
91
+ const parameterizedTest = <T extends { [k: string]: Route }>(
92
+ routes: T,
93
+ tests: (routes: T) => [HttpMethod, string, Route?][]
94
+ ) => {
95
+ const underTest = new RouteResolver({
96
+ routes: Object.values(routes),
97
+ })
98
+
99
+ tests(routes).forEach(([method, path, expected]) =>
100
+ expect(underTest.resolveRoute(method, path)).toBe(expected)
101
+ )
102
+ }
@@ -0,0 +1,39 @@
1
+ import { jest } from '@jest/globals'
2
+ import { expect } from 'chai'
3
+
4
+ import ContainerServer from '../../src/Server/lib/ContainerServer'
5
+ import Server from '../../src/Server/lib/Server'
6
+ import Router from '../../src/Server/Router'
7
+
8
+ describe('Router basics', () => {
9
+ // @ts-ignore
10
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
11
+ beforeAll(() => {
12
+ // @ts-ignore
13
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
14
+ })
15
+ afterAll(() => {
16
+ mockExit.mockRestore()
17
+ })
18
+ beforeEach(() => {
19
+ mockExit.mockReset()
20
+ })
21
+
22
+ test('Serverless server', async () => {
23
+ process.env['HYBRIDLESS_RUNTIME'] = undefined
24
+ const router = new Router({ routes: [] })
25
+ expect(router.getExport()).to.not.be.undefined
26
+ expect(router['server']).to.be.an.instanceof(Server)
27
+ expect(router['server']).to.not.be.an.instanceof(ContainerServer)
28
+ })
29
+ test('Container server', async () => {
30
+ process.env['HYBRIDLESS_RUNTIME'] = 'true'
31
+ const router = new Router({ routes: [] })
32
+ expect(router.getExport()).to.not.be.undefined
33
+ expect(router['server']).to.be.an.instanceof(Server)
34
+ expect(router['server']).to.be.an.instanceof(ContainerServer)
35
+ await router['server']['stop']()
36
+ })
37
+ })
38
+
39
+ export {}
@@ -0,0 +1,531 @@
1
+ import { jest } from '@jest/globals'
2
+ import { expect as c_expect } from 'chai'
3
+ import request from 'supertest'
4
+ import { z } from 'zod'
5
+
6
+ import { HttpMethod } from '../../../src/API/Request'
7
+ import Response from '../../../src/API/Response'
8
+ import Globals from '../../../src/Globals'
9
+ import ContainerServer from '../../../src/Server/lib/ContainerServer'
10
+ import { defaultUrl } from '../../Test.utils'
11
+
12
+ export const ViewSchema = z.object({
13
+ id: z.string(),
14
+ content: z.string(),
15
+ createdAt: z.string(),
16
+ updatedAt: z.string(),
17
+ })
18
+
19
+ export const QuerySchema = z.object({
20
+ id: z.string(),
21
+ order: z.string().refine(
22
+ v => {
23
+ const n = Number(v)
24
+ return !isNaN(n) && v?.length > 0
25
+ },
26
+ { message: 'Invalid number' }
27
+ ),
28
+ })
29
+
30
+ describe('Container server routing', () => {
31
+ // @ts-ignore
32
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
33
+ beforeAll(() => {
34
+ // @ts-ignore
35
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
36
+ })
37
+ afterAll(() => {
38
+ mockExit.mockRestore()
39
+ })
40
+ beforeEach(() => {
41
+ mockExit.mockReset()
42
+ })
43
+
44
+ test('Health & 404 handler', async () => {
45
+ const server = new ContainerServer({ routes: [] })
46
+ await server.start()
47
+ // Health check
48
+ const res = await request(defaultUrl)
49
+ .get(Globals.Listener_HTTP_DefaultHealthCheckRoute)
50
+ .expect('Content-Type', 'text/html; charset=utf-8')
51
+ .expect(200)
52
+ c_expect(res.text).to.be.equals('Healthy!')
53
+ // Not handled
54
+ await request(defaultUrl)
55
+ .get(`/abc`)
56
+ .expect('Content-Type', 'application/json; charset=utf-8')
57
+ .expect(404)
58
+
59
+ // Unload
60
+ await server.stop('Error')
61
+ expect(mockExit).toHaveBeenCalledTimes(1)
62
+ expect(mockExit).toBeCalledWith(1)
63
+ })
64
+
65
+ test('Simple route', async () => {
66
+ const server = new ContainerServer({
67
+ routes: [
68
+ {
69
+ path: '/abc',
70
+ method: HttpMethod.GET,
71
+ handler: async () => {
72
+ return Response.SimpleResponse({ name: 'abc' })
73
+ },
74
+ } as any,
75
+ ],
76
+ })
77
+ await server.start()
78
+ // Health check
79
+ const res = await request(defaultUrl)
80
+ .get(Globals.Listener_HTTP_DefaultHealthCheckRoute)
81
+ .expect('Content-Type', 'text/html; charset=utf-8')
82
+ .expect(200)
83
+ c_expect(res.text).to.be.equals('Healthy!')
84
+ // Found route
85
+ const resG = await request(defaultUrl)
86
+ .get(`/abc`)
87
+ .expect('Content-Type', 'application/json; charset=utf-8')
88
+ .expect(200)
89
+ c_expect(resG.body['name']).to.be.deep.equals('abc')
90
+ c_expect(resG.body['transactionID']).to.not.be.null
91
+ // Found route, but not method
92
+ await request(defaultUrl)
93
+ .post(`/abc`)
94
+ .expect('Content-Type', 'application/json; charset=utf-8')
95
+ .expect(404)
96
+ // Not found route
97
+ await request(defaultUrl)
98
+ .get(`/abc/123`)
99
+ .expect('Content-Type', 'application/json; charset=utf-8')
100
+ .expect(404)
101
+ // Unload
102
+ await server.stop('Error')
103
+ expect(mockExit).toHaveBeenCalledTimes(1)
104
+ expect(mockExit).toBeCalledWith(1)
105
+ })
106
+ })
107
+
108
+ describe('Container server basics', () => {
109
+ // @ts-ignore
110
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
111
+ beforeAll(() => {
112
+ // @ts-ignore
113
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
114
+ })
115
+ afterAll(() => {
116
+ mockExit.mockRestore()
117
+ })
118
+ beforeEach(() => {
119
+ mockExit.mockReset()
120
+ })
121
+
122
+ test('Starts with exports', async () => {
123
+ const server = new ContainerServer({ routes: [] })
124
+ server.getExport()
125
+ return new Promise(resolve => {
126
+ setTimeout(async () => {
127
+ // Health check
128
+ const res = await request(defaultUrl)
129
+ .get(Globals.Listener_HTTP_DefaultHealthCheckRoute)
130
+ .expect('Content-Type', 'text/html; charset=utf-8')
131
+ .expect(200)
132
+ c_expect(res.text).to.be.equals('Healthy!')
133
+ // Unload
134
+ await server.stop()
135
+ expect(mockExit).toHaveBeenCalledTimes(1)
136
+ expect(mockExit).toBeCalledWith(0)
137
+ resolve(null)
138
+ }, 2000)
139
+ })
140
+ })
141
+
142
+ test('Stops if process sends message SIGINT', async () => {
143
+ const server = new ContainerServer({ routes: [] })
144
+ await server.start()
145
+ process.emit('SIGINT')
146
+ return new Promise(resolve => {
147
+ setTimeout(async () => {
148
+ expect(mockExit).toHaveBeenCalledTimes(1)
149
+ expect(mockExit).toBeCalledWith(0)
150
+ resolve(null)
151
+ }, 2000)
152
+ })
153
+ }, 10000)
154
+
155
+ test('Stops if process sends message unhandledRejection', async () => {
156
+ const server = new ContainerServer({ routes: [] })
157
+ await server.start()
158
+ // @ts-ignore
159
+ process.emit('unhandledRejection')
160
+ return new Promise(resolve => {
161
+ setTimeout(async () => {
162
+ expect(mockExit).toHaveBeenCalledTimes(1)
163
+ expect(mockExit).toBeCalledWith(0)
164
+ resolve(null)
165
+ }, 2000)
166
+ })
167
+ }, 10000)
168
+ })
169
+
170
+ describe('Container server validation (body)', () => {
171
+ // @ts-ignore
172
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
173
+ beforeAll(() => {
174
+ // @ts-ignore
175
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
176
+ })
177
+ afterAll(() => {
178
+ mockExit.mockRestore()
179
+ })
180
+ beforeEach(() => {
181
+ mockExit.mockReset()
182
+ })
183
+
184
+ function validateValidationFailure(res: any, failureCount?: number) {
185
+ c_expect(res.body['err']).to.be.equals(Globals.ErrorResponseValidationFail)
186
+ c_expect(res.body['errCode']).to.be.equals(Globals.ErrorCode_InvalidInput)
187
+ c_expect(res.body['transactionID']).to.not.be.null
188
+ c_expect(res.body['transactionID']).to.be.an('string')
189
+ c_expect(res.body['validationFailure']?.length).to.be.equals(failureCount || 4)
190
+ }
191
+
192
+ test('Validates empty body', async () => {
193
+ const server = new ContainerServer({
194
+ routes: [
195
+ {
196
+ path: '/abc',
197
+ method: HttpMethod.POST,
198
+ inputSchema: ViewSchema,
199
+ handler: async () => {
200
+ return Response.SimpleResponse({ name: 'abc' })
201
+ },
202
+ } as any,
203
+ ],
204
+ })
205
+ await server.start()
206
+ // Validation fails, empty body
207
+ const resG = await request(defaultUrl)
208
+ .post(`/abc`)
209
+ .expect('Content-Type', 'application/json; charset=utf-8')
210
+ .expect(400)
211
+ validateValidationFailure(resG)
212
+ await server.stop()
213
+ })
214
+
215
+ test('Validates empty string body', async () => {
216
+ const server = new ContainerServer({
217
+ routes: [
218
+ {
219
+ path: '/abc',
220
+ method: HttpMethod.POST,
221
+ inputSchema: ViewSchema,
222
+ handler: async () => {
223
+ return Response.SimpleResponse({ name: 'abc' })
224
+ },
225
+ } as any,
226
+ ],
227
+ })
228
+ await server.start()
229
+ // Validation fails, empty body
230
+ const resG = await request(defaultUrl)
231
+ .post(`/abc`)
232
+ .send('')
233
+ .expect('Content-Type', 'application/json; charset=utf-8')
234
+ .expect(400)
235
+ validateValidationFailure(resG)
236
+ await server.stop()
237
+ })
238
+
239
+ test('Validates empty object body', async () => {
240
+ const server = new ContainerServer({
241
+ routes: [
242
+ {
243
+ path: '/abc',
244
+ method: HttpMethod.POST,
245
+ inputSchema: ViewSchema,
246
+ handler: async () => {
247
+ return Response.SimpleResponse({ name: 'abc' })
248
+ },
249
+ } as any,
250
+ ],
251
+ })
252
+ await server.start()
253
+ // Validation fails, empty body
254
+ const resG = await request(defaultUrl)
255
+ .post(`/abc`)
256
+ .send({})
257
+ .expect('Content-Type', 'application/json; charset=utf-8')
258
+ .expect(400)
259
+ validateValidationFailure(resG)
260
+ await server.stop()
261
+ })
262
+
263
+ test('Validates missing props body', async () => {
264
+ const server = new ContainerServer({
265
+ routes: [
266
+ {
267
+ path: '/abc',
268
+ method: HttpMethod.POST,
269
+ inputSchema: ViewSchema,
270
+ handler: async () => {
271
+ return Response.SimpleResponse({ name: 'abc' })
272
+ },
273
+ } as any,
274
+ ],
275
+ })
276
+ await server.start()
277
+ // Validation fails, empty body
278
+ const resG = await request(defaultUrl)
279
+ .post(`/abc`)
280
+ .send({ id: '123' })
281
+ .expect('Content-Type', 'application/json; charset=utf-8')
282
+ .expect(400)
283
+ validateValidationFailure(resG, 3)
284
+ await server.stop()
285
+ })
286
+
287
+ test('Validates wrong type props body', async () => {
288
+ const server = new ContainerServer({
289
+ routes: [
290
+ {
291
+ path: '/abc',
292
+ method: HttpMethod.POST,
293
+ inputSchema: ViewSchema,
294
+ handler: async () => {
295
+ return Response.SimpleResponse({ name: 'abc' })
296
+ },
297
+ } as any,
298
+ ],
299
+ })
300
+ await server.start()
301
+ // Validation fails, empty body
302
+ const resG = await request(defaultUrl)
303
+ .post(`/abc`)
304
+ .send({ id: 123 })
305
+ .expect('Content-Type', 'application/json; charset=utf-8')
306
+ .expect(400)
307
+ validateValidationFailure(resG)
308
+ await server.stop()
309
+ })
310
+
311
+ test('Validates successfully', async () => {
312
+ const server = new ContainerServer({
313
+ routes: [
314
+ {
315
+ path: '/abc',
316
+ method: HttpMethod.POST,
317
+ inputSchema: ViewSchema,
318
+ handler: async () => {
319
+ return Response.SimpleResponse({ name: 'abc' })
320
+ },
321
+ } as any,
322
+ ],
323
+ })
324
+ await server.start()
325
+ // Validation fails, empty body
326
+ await request(defaultUrl)
327
+ .post(`/abc`)
328
+ .send({
329
+ id: '123',
330
+ content: '123',
331
+ createdAt: new Date(),
332
+ updatedAt: new Date(),
333
+ })
334
+ .expect('Content-Type', 'application/json; charset=utf-8')
335
+ .expect(200)
336
+ await server.stop()
337
+ })
338
+ })
339
+
340
+ describe('Container server validation (query)', () => {
341
+ let server: ContainerServer
342
+ // @ts-ignore
343
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
344
+ beforeAll(() => {
345
+ // @ts-ignore
346
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
347
+ })
348
+ afterAll(() => {
349
+ mockExit.mockRestore()
350
+ })
351
+ beforeEach(async () => {
352
+ mockExit.mockReset()
353
+ server = new ContainerServer({
354
+ routes: [
355
+ {
356
+ path: '/abc',
357
+ method: HttpMethod.POST,
358
+ querySchema: QuerySchema,
359
+ handler: async () => {
360
+ return Response.SimpleResponse({ name: 'abc' })
361
+ },
362
+ } as any,
363
+ ],
364
+ })
365
+ await server.start()
366
+ })
367
+
368
+ afterEach(async () => {
369
+ await server.stop()
370
+ })
371
+
372
+ function validateValidationFailure(res: any, failureCount?: number) {
373
+ c_expect(res.body['err']).to.be.equals(Globals.ErrorResponseValidationFail)
374
+ c_expect(res.body['errCode']).to.be.equals(Globals.ErrorCode_InvalidInput)
375
+ c_expect(res.body['transactionID']).to.not.be.null
376
+ c_expect(res.body['transactionID']).to.be.an('string')
377
+ c_expect(res.body['validationFailure']?.length).to.be.equals(failureCount || 2)
378
+ }
379
+
380
+ test('Validates empty query', async () => {
381
+ // Validation fails, empty body
382
+ const resG = await request(defaultUrl)
383
+ .post(`/abc`)
384
+ .expect('Content-Type', 'application/json; charset=utf-8')
385
+ .expect(400)
386
+ validateValidationFailure(resG, 2)
387
+ })
388
+
389
+ test('Validates empty query differently', async () => {
390
+ // Validation fails, empty body
391
+ const resG = await request(defaultUrl)
392
+ .post(`/abc?`)
393
+ .expect('Content-Type', 'application/json; charset=utf-8')
394
+ .expect(400)
395
+ validateValidationFailure(resG, 2)
396
+ })
397
+
398
+ test('Validates missing props body', async () => {
399
+ // Validation fails, empty body
400
+ const resG = await request(defaultUrl)
401
+ .post(`/abc?id=myname`)
402
+ .expect('Content-Type', 'application/json; charset=utf-8')
403
+ .expect(400)
404
+ validateValidationFailure(resG, 1)
405
+ })
406
+
407
+ test('Validates wrong type props body', async () => {
408
+ // Validation fails, empty body
409
+ const resG = await request(defaultUrl)
410
+ .post(`/abc?id=name&order=myname`)
411
+ .expect('Content-Type', 'application/json; charset=utf-8')
412
+ .expect(400)
413
+ validateValidationFailure(resG, 1)
414
+ })
415
+
416
+ test('Validates successfully2', async () => {
417
+ // Validation fails, empty body
418
+ await request(defaultUrl)
419
+ .post(`/abc`)
420
+ .query({ id: 'name', order: 123 })
421
+ .expect('Content-Type', 'application/json; charset=utf-8')
422
+ .expect(200)
423
+ })
424
+ })
425
+
426
+ describe('Container server validation (path)', () => {
427
+ let server: ContainerServer
428
+ // @ts-ignore
429
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
430
+ beforeAll(() => {
431
+ // @ts-ignore
432
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
433
+ })
434
+ afterAll(() => {
435
+ mockExit.mockRestore()
436
+ })
437
+ beforeEach(async () => {
438
+ mockExit.mockReset()
439
+ server = new ContainerServer({
440
+ routes: [
441
+ {
442
+ path: '/abc/:id/:order',
443
+ method: HttpMethod.POST,
444
+ pathSchema: QuerySchema,
445
+ handler: async () => {
446
+ return Response.SimpleResponse({ name: 'abc' })
447
+ },
448
+ } as any,
449
+ ],
450
+ })
451
+ await server.start()
452
+ })
453
+
454
+ afterEach(async () => {
455
+ await server.stop()
456
+ })
457
+
458
+ function validateValidationFailure(res: any, failureCount?: number) {
459
+ c_expect(res.body['err']).to.be.equals(Globals.ErrorResponseValidationFail)
460
+ c_expect(res.body['errCode']).to.be.equals(Globals.ErrorCode_InvalidInput)
461
+ c_expect(res.body['transactionID']).to.not.be.null
462
+ c_expect(res.body['transactionID']).to.be.an('string')
463
+ c_expect(res.body['validationFailure']?.length).to.be.equals(failureCount || 2)
464
+ }
465
+
466
+ test('Validates wrong type props body', async () => {
467
+ // Validation fails, empty body
468
+ const resG = await request(defaultUrl)
469
+ .post(`/abc/name/myname`)
470
+ .expect('Content-Type', 'application/json; charset=utf-8')
471
+ .expect(400)
472
+ validateValidationFailure(resG, 1)
473
+ })
474
+
475
+ test('Validates successfully4', async () => {
476
+ // Validation fails, empty body
477
+ await request(defaultUrl)
478
+ .post(`/abc/name/123`)
479
+ .expect('Content-Type', 'application/json; charset=utf-8')
480
+ .expect(200)
481
+ })
482
+ })
483
+
484
+ describe('Container server raw body', () => {
485
+ // @ts-ignore
486
+ let mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
487
+ beforeAll(() => {
488
+ // @ts-ignore
489
+ mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
490
+ process.env['HYBRIDLESS_RUNTIME'] = 'true'
491
+ })
492
+ afterAll(() => {
493
+ mockExit.mockRestore()
494
+ process.env['HYBRIDLESS_RUNTIME'] = undefined
495
+ })
496
+ beforeEach(() => {
497
+ mockExit.mockReset()
498
+ })
499
+
500
+ test('Raw body is available', async () => {
501
+ let raw = null
502
+ const server = new ContainerServer({
503
+ routes: [
504
+ {
505
+ path: '/abc',
506
+ method: HttpMethod.POST,
507
+ handler: async t => {
508
+ raw = t.request.getBody(true)
509
+ return Response.SimpleResponse(null)
510
+ },
511
+ } as any,
512
+ ],
513
+ })
514
+ await server.start()
515
+
516
+ // it's formatted funky, because we're testing for exact match
517
+ const data = ' { "name" : "abc" }'
518
+ await request(defaultUrl)
519
+ .post(`/abc`)
520
+ .send(data)
521
+ .set('Content-Type', 'application/json')
522
+ .expect(200)
523
+
524
+ const s = new TextDecoder('utf-8').decode(raw!)
525
+ c_expect(s).to.be.equals(data)
526
+
527
+ await server.stop()
528
+ })
529
+ })
530
+
531
+ export {}
@@ -0,0 +1,12 @@
1
+ import { expect } from 'chai'
2
+
3
+ import Server from '../../../src/Server/lib/Server'
4
+
5
+ describe('Server basics', () => {
6
+ test('Works', async () => {
7
+ const server = new Server({ routes: [] })
8
+ expect(server.getExport()).to.not.be.undefined
9
+ })
10
+ })
11
+
12
+ export {}