@creator.co/wapi 1.7.1-alpha4 → 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.
- package/.eslintignore +3 -0
- package/.eslintrc.cjs +60 -0
- package/.github/workflows/npmpublish.yml +11 -0
- package/.github/workflows/prs.yml +13 -0
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/jest.config.ts +33 -0
- package/jest.smoke.config.ts +35 -0
- package/package.json +1 -1
- package/tests/API/Request.test.ts +273 -0
- package/tests/API/Response.test.ts +367 -0
- package/tests/API/Utils.test.ts +167 -0
- package/tests/BaseEvent/EventProcessor.test.ts +261 -0
- package/tests/BaseEvent/Process.test.ts +49 -0
- package/tests/BaseEvent/Transaction.test.ts +408 -0
- package/tests/Cache/Redis-client.test.ts +90 -0
- package/tests/Cache/Redis-cluster.test.ts +100 -0
- package/tests/Config/Config.test.ts +205 -0
- package/tests/Config/EnvironmentVar.test.ts +250 -0
- package/tests/Crypto/Crypto.test.ts +88 -0
- package/tests/Crypto/JWT.test.ts +92 -0
- package/tests/Database/DatabaseManager.test.ts +71 -0
- package/tests/Database/integrations/knex/KnexDatabase.test.ts +76 -0
- package/tests/Database/integrations/knex/KnexTransaction.test.ts +149 -0
- package/tests/Database/integrations/kysely/KyselyDatabase.test.ts +113 -0
- package/tests/Database/integrations/kysely/KyselyTransaction.test.ts +119 -0
- package/tests/Database/integrations/pg/PostgresDatabase.test.ts +76 -0
- package/tests/Database/integrations/pg/PostgresTransaction.test.ts +118 -0
- package/tests/Logger/Logger.test.ts +219 -0
- package/tests/Mailer/Mailer.test.ts +59 -0
- package/tests/Publisher/Publisher.test.ts +94 -0
- package/tests/Server/RouteResolver.test.ts +102 -0
- package/tests/Server/Router.test.ts +39 -0
- package/tests/Server/lib/ContainerServer.test.ts +531 -0
- package/tests/Server/lib/Server.test.ts +12 -0
- package/tests/Server/lib/container/GenericHandler.test.ts +131 -0
- package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
- package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
- package/tests/Server/lib/container/Proxy.test.ts +268 -0
- package/tests/Server/lib/container/Utils.test.ts +47 -0
- package/tests/Test.utils.ts +74 -0
- package/tests/Validation/Validator.test.ts +76 -0
- package/tsconfig.json +26 -0
- package/tsconfig.smoke.json +26 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { expect as c_expect } from 'chai'
|
|
2
|
+
|
|
3
|
+
import Response from '../../src/API/Response'
|
|
4
|
+
import EventProcessor from '../../src/BaseEvent/EventProcessor'
|
|
5
|
+
import Globals from '../../src/Globals'
|
|
6
|
+
import { emptyQueueEvent, observableContext } from '../Test.utils'
|
|
7
|
+
|
|
8
|
+
describe('EventProcessor success invocation path', () => {
|
|
9
|
+
test('Simple success', async () => {
|
|
10
|
+
const b = { name: '123' }
|
|
11
|
+
const context = observableContext()
|
|
12
|
+
const transaction = new EventProcessor(
|
|
13
|
+
emptyQueueEvent({
|
|
14
|
+
Records: [{ body: JSON.stringify(b) }, { body: JSON.stringify(b) }],
|
|
15
|
+
}),
|
|
16
|
+
context,
|
|
17
|
+
{}
|
|
18
|
+
)
|
|
19
|
+
let count = 0
|
|
20
|
+
const handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
21
|
+
c_expect(eventRecord).to.be.deep.equal(b)
|
|
22
|
+
count++
|
|
23
|
+
return Response.SuccessResponse(null)
|
|
24
|
+
})
|
|
25
|
+
// check resp
|
|
26
|
+
c_expect(count).to.be.equals(2)
|
|
27
|
+
c_expect(handlerResp).to.be.an.instanceof(Response)
|
|
28
|
+
if (handlerResp instanceof Response) {
|
|
29
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equal({
|
|
30
|
+
transactionID: 'unknown',
|
|
31
|
+
})
|
|
32
|
+
c_expect(handlerResp.getCode()).to.be.equal(200)
|
|
33
|
+
}
|
|
34
|
+
// ctx
|
|
35
|
+
expect(context.fail).not.toBeCalled()
|
|
36
|
+
expect(context.done).not.toBeCalled()
|
|
37
|
+
expect(context.succeed).not.toBeCalled()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('Simple success - do not decode', async () => {
|
|
41
|
+
const b = { name: '123' }
|
|
42
|
+
const context = observableContext()
|
|
43
|
+
const transaction = new EventProcessor(
|
|
44
|
+
emptyQueueEvent({
|
|
45
|
+
Records: [{ body: JSON.stringify(b) }, { body: JSON.stringify(b) }],
|
|
46
|
+
}),
|
|
47
|
+
context,
|
|
48
|
+
{}
|
|
49
|
+
)
|
|
50
|
+
let count = 0
|
|
51
|
+
const handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
52
|
+
c_expect(eventRecord).to.be.deep.equal(JSON.stringify(b))
|
|
53
|
+
count++
|
|
54
|
+
return Response.SuccessResponse(null)
|
|
55
|
+
}, true)
|
|
56
|
+
// check resp
|
|
57
|
+
c_expect(count).to.be.equals(2)
|
|
58
|
+
c_expect(handlerResp).to.be.an.instanceof(Response)
|
|
59
|
+
if (handlerResp instanceof Response) {
|
|
60
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equal({
|
|
61
|
+
transactionID: 'unknown',
|
|
62
|
+
})
|
|
63
|
+
c_expect(handlerResp.getCode()).to.be.equal(200)
|
|
64
|
+
}
|
|
65
|
+
// ctx
|
|
66
|
+
expect(context.fail).not.toBeCalled()
|
|
67
|
+
expect(context.done).not.toBeCalled()
|
|
68
|
+
expect(context.succeed).not.toBeCalled()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('EventProcessor failure invocation path', () => {
|
|
73
|
+
test('Simple failure', async () => {
|
|
74
|
+
const b = { name: '123' }
|
|
75
|
+
const context = observableContext()
|
|
76
|
+
const transaction = new EventProcessor(
|
|
77
|
+
emptyQueueEvent({
|
|
78
|
+
Records: [
|
|
79
|
+
{ body: JSON.stringify(b) },
|
|
80
|
+
{ messageId: '123', body: JSON.stringify(b) },
|
|
81
|
+
{ body: JSON.stringify(b) },
|
|
82
|
+
],
|
|
83
|
+
}),
|
|
84
|
+
context,
|
|
85
|
+
{}
|
|
86
|
+
)
|
|
87
|
+
let count = 0
|
|
88
|
+
let handlerResp: any = null,
|
|
89
|
+
err: any = null
|
|
90
|
+
try {
|
|
91
|
+
handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
92
|
+
c_expect(eventRecord).to.be.deep.equal(b)
|
|
93
|
+
count++
|
|
94
|
+
return count == 1 ? Response.SuccessResponse(null) : Response.BadRequestResponse('Failed!')
|
|
95
|
+
})
|
|
96
|
+
} catch (e) {
|
|
97
|
+
err = e
|
|
98
|
+
}
|
|
99
|
+
// check resp
|
|
100
|
+
c_expect(count).to.be.equals(2)
|
|
101
|
+
c_expect(handlerResp).to.be.null
|
|
102
|
+
c_expect(err).to.be.deep.equal(
|
|
103
|
+
new Error(
|
|
104
|
+
JSON.stringify({
|
|
105
|
+
err: 'Failed!',
|
|
106
|
+
transactionID: 'unknown',
|
|
107
|
+
})
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
// ctx
|
|
111
|
+
expect(context.fail).not.toBeCalled()
|
|
112
|
+
expect(context.done).not.toBeCalled()
|
|
113
|
+
expect(context.succeed).not.toBeCalled()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('Simple failure w/ null', async () => {
|
|
117
|
+
const b = { name: '123' }
|
|
118
|
+
const context = observableContext()
|
|
119
|
+
const transaction = new EventProcessor(
|
|
120
|
+
emptyQueueEvent({
|
|
121
|
+
Records: [
|
|
122
|
+
{ body: JSON.stringify(b) },
|
|
123
|
+
{ messageId: '123', body: JSON.stringify(b) },
|
|
124
|
+
{ body: JSON.stringify(b) },
|
|
125
|
+
],
|
|
126
|
+
}),
|
|
127
|
+
context,
|
|
128
|
+
{}
|
|
129
|
+
)
|
|
130
|
+
let count = 0
|
|
131
|
+
let handlerResp: any = null,
|
|
132
|
+
err: any = null
|
|
133
|
+
try {
|
|
134
|
+
handlerResp = await transaction.processEvent(
|
|
135
|
+
// @ts-ignore
|
|
136
|
+
async (transaction, eventRecord) => {
|
|
137
|
+
c_expect(eventRecord).to.be.deep.equal(b)
|
|
138
|
+
count++
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
} catch (e) {
|
|
143
|
+
err = e
|
|
144
|
+
}
|
|
145
|
+
// check resp
|
|
146
|
+
c_expect(count).to.be.equals(1)
|
|
147
|
+
c_expect(handlerResp).to.be.null
|
|
148
|
+
c_expect(err).to.be.deep.equal(
|
|
149
|
+
new Error(
|
|
150
|
+
JSON.stringify({
|
|
151
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
152
|
+
rollback: true,
|
|
153
|
+
errCode: Globals.ErrorCode_APIError,
|
|
154
|
+
transactionID: 'unknown',
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
// ctx
|
|
159
|
+
expect(context.fail).not.toBeCalled()
|
|
160
|
+
expect(context.done).not.toBeCalled()
|
|
161
|
+
expect(context.succeed).not.toBeCalled()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('Simple failure - allow failures', async () => {
|
|
165
|
+
const b = { name: '123' }
|
|
166
|
+
const context = observableContext()
|
|
167
|
+
const transaction = new EventProcessor(
|
|
168
|
+
emptyQueueEvent({
|
|
169
|
+
Records: [{ body: JSON.stringify(b) }, { messageId: '123', body: JSON.stringify(b) }],
|
|
170
|
+
}),
|
|
171
|
+
context,
|
|
172
|
+
{},
|
|
173
|
+
true
|
|
174
|
+
)
|
|
175
|
+
let count = 0
|
|
176
|
+
const handlerResp = await transaction.processEvent(async (transaction, eventRecord) => {
|
|
177
|
+
c_expect(eventRecord).to.be.deep.equal(b)
|
|
178
|
+
count++
|
|
179
|
+
return count == 1 ? Response.SuccessResponse(null) : Response.BadRequestResponse('Failed!')
|
|
180
|
+
})
|
|
181
|
+
// check resp
|
|
182
|
+
c_expect(count).to.be.equals(2)
|
|
183
|
+
c_expect(handlerResp).to.be.deep.equal({
|
|
184
|
+
batchItemFailures: [{ itemIdentifier: '123' }],
|
|
185
|
+
})
|
|
186
|
+
// ctx
|
|
187
|
+
expect(context.fail).not.toBeCalled()
|
|
188
|
+
expect(context.done).not.toBeCalled()
|
|
189
|
+
expect(context.succeed).not.toBeCalled()
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('Simple failure w/ null response - allow failures', async () => {
|
|
193
|
+
const b = { name: '123' }
|
|
194
|
+
const context = observableContext()
|
|
195
|
+
const transaction = new EventProcessor(
|
|
196
|
+
emptyQueueEvent({
|
|
197
|
+
Records: [
|
|
198
|
+
{ messageId: '456', body: JSON.stringify(b) },
|
|
199
|
+
{ messageId: '123', body: JSON.stringify(b) },
|
|
200
|
+
],
|
|
201
|
+
}),
|
|
202
|
+
context,
|
|
203
|
+
{},
|
|
204
|
+
true
|
|
205
|
+
)
|
|
206
|
+
let count = 0
|
|
207
|
+
const handlerResp = await transaction.processEvent(
|
|
208
|
+
// @ts-ignore
|
|
209
|
+
async (transaction, eventRecord) => {
|
|
210
|
+
c_expect(eventRecord).to.be.deep.equal(b)
|
|
211
|
+
count++
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
// check resp
|
|
216
|
+
c_expect(count).to.be.equals(2)
|
|
217
|
+
c_expect(handlerResp).to.be.deep.equal({
|
|
218
|
+
batchItemFailures: [{ itemIdentifier: '456' }, { itemIdentifier: '123' }],
|
|
219
|
+
})
|
|
220
|
+
// ctx
|
|
221
|
+
expect(context.fail).not.toBeCalled()
|
|
222
|
+
expect(context.done).not.toBeCalled()
|
|
223
|
+
expect(context.succeed).not.toBeCalled()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('Simple failure no records', async () => {
|
|
227
|
+
const context = observableContext()
|
|
228
|
+
const transaction = new EventProcessor(emptyQueueEvent(), context, {})
|
|
229
|
+
let count = 0
|
|
230
|
+
let handlerResp: any = null,
|
|
231
|
+
err: any = null
|
|
232
|
+
try {
|
|
233
|
+
handlerResp = await transaction.processEvent(
|
|
234
|
+
// @ts-ignore
|
|
235
|
+
async () => {
|
|
236
|
+
count++
|
|
237
|
+
return null
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
} catch (e) {
|
|
241
|
+
err = e
|
|
242
|
+
}
|
|
243
|
+
// check resp
|
|
244
|
+
c_expect(count).to.be.equals(0)
|
|
245
|
+
c_expect(handlerResp).to.be.null
|
|
246
|
+
c_expect(err).to.be.deep.equal(
|
|
247
|
+
new Error(
|
|
248
|
+
JSON.stringify({
|
|
249
|
+
err: Globals.ErrorResponseNoRecords,
|
|
250
|
+
errCode: Globals.ErrorCode_NoRecords,
|
|
251
|
+
})
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
// ctx
|
|
255
|
+
expect(context.fail).not.toBeCalled()
|
|
256
|
+
expect(context.done).not.toBeCalled()
|
|
257
|
+
expect(context.succeed).not.toBeCalled()
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
export {}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { expect } from 'chai'
|
|
2
|
+
|
|
3
|
+
import Response from '../../src/API/Response'
|
|
4
|
+
import Process from '../../src/BaseEvent/Process'
|
|
5
|
+
|
|
6
|
+
describe('Process success invocation path', () => {
|
|
7
|
+
test('Simple success', async () => {
|
|
8
|
+
const b = { name: '123' }
|
|
9
|
+
const proc = new Process({}, 100)
|
|
10
|
+
let count = 0
|
|
11
|
+
const handlerResp = await proc.execute(async () => {
|
|
12
|
+
count++
|
|
13
|
+
return Response.SuccessResponse(b)
|
|
14
|
+
})
|
|
15
|
+
// check resp
|
|
16
|
+
expect(handlerResp).to.be.undefined
|
|
17
|
+
// check iterations
|
|
18
|
+
return new Promise(resolve => {
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
expect(count).to.be.equals(2)
|
|
21
|
+
clearInterval(proc.timeout)
|
|
22
|
+
resolve(null)
|
|
23
|
+
}, 250)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('Process failure invocation path', () => {
|
|
29
|
+
test('Simple failure', async () => {
|
|
30
|
+
const proc = new Process({}, 100)
|
|
31
|
+
let count = 0
|
|
32
|
+
const handlerResp = await proc.execute(async () => {
|
|
33
|
+
count++
|
|
34
|
+
throw new Error('Failed!')
|
|
35
|
+
})
|
|
36
|
+
// check resp
|
|
37
|
+
expect(handlerResp).to.be.undefined
|
|
38
|
+
// check iterations
|
|
39
|
+
return new Promise(resolve => {
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
expect(count).to.be.equals(2)
|
|
42
|
+
clearInterval(proc.timeout)
|
|
43
|
+
resolve(null)
|
|
44
|
+
}, 250)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export {}
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { expect as c_expect } from 'chai'
|
|
3
|
+
|
|
4
|
+
import Response from '../../src/API/Response'
|
|
5
|
+
import Transaction from '../../src/BaseEvent/Transaction'
|
|
6
|
+
import type { DbConfig } from '../../src/Database/types'
|
|
7
|
+
import Globals from '../../src/Globals'
|
|
8
|
+
import { defaultHeaders, emptyEvent, observableContext } from '../Test.utils'
|
|
9
|
+
|
|
10
|
+
describe('Transaction success invocation path', () => {
|
|
11
|
+
test('Simple success', async () => {
|
|
12
|
+
const b = { name: '123' }
|
|
13
|
+
const context = observableContext()
|
|
14
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
15
|
+
const handlerResp = await transaction.execute(async () => {
|
|
16
|
+
return Response.SuccessResponse(b)
|
|
17
|
+
})
|
|
18
|
+
// check resp
|
|
19
|
+
c_expect(handlerResp).to.be.null
|
|
20
|
+
// ctx
|
|
21
|
+
expect(context.fail).not.toBeCalled()
|
|
22
|
+
expect(context.done).not.toBeCalled()
|
|
23
|
+
expect(context.succeed).toBeCalledWith({
|
|
24
|
+
statusCode: 200,
|
|
25
|
+
body: JSON.stringify(b),
|
|
26
|
+
headers: defaultHeaders,
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('Simple success - sync return', async () => {
|
|
31
|
+
const b = { name: '123' }
|
|
32
|
+
const context = observableContext()
|
|
33
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context, {
|
|
34
|
+
syncReturn: true,
|
|
35
|
+
})
|
|
36
|
+
const handlerResp = await transaction.execute(async () => {
|
|
37
|
+
return Response.SuccessResponse(b)
|
|
38
|
+
})
|
|
39
|
+
// check resp
|
|
40
|
+
c_expect(handlerResp).to.be.an.instanceof(Response)
|
|
41
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equals(b)
|
|
42
|
+
c_expect(handlerResp?.getCode()).to.be.equals(200)
|
|
43
|
+
// ctx
|
|
44
|
+
expect(context.fail).not.toBeCalled()
|
|
45
|
+
expect(context.done).not.toBeCalled()
|
|
46
|
+
expect(context.succeed).not.toBeCalled()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('Transaction error invocation path without response', () => {
|
|
51
|
+
test('Not valid response returned success', async () => {
|
|
52
|
+
const b = {
|
|
53
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
54
|
+
rollback: true,
|
|
55
|
+
errCode: Globals.ErrorCode_APIError,
|
|
56
|
+
transactionID: '123',
|
|
57
|
+
}
|
|
58
|
+
const context = observableContext({ awsRequestId: '123' })
|
|
59
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
60
|
+
// @ts-expect-error
|
|
61
|
+
const handlerResp = await transaction.execute(async () => {
|
|
62
|
+
return null
|
|
63
|
+
})
|
|
64
|
+
// check resp
|
|
65
|
+
c_expect(handlerResp).to.be.null
|
|
66
|
+
// ctx
|
|
67
|
+
expect(context.fail).not.toBeCalled()
|
|
68
|
+
expect(context.done).not.toBeCalled()
|
|
69
|
+
expect(context.succeed).toBeCalledWith({
|
|
70
|
+
statusCode: 400,
|
|
71
|
+
body: JSON.stringify(b),
|
|
72
|
+
headers: defaultHeaders,
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('Not valid response returned success - sync return', async () => {
|
|
77
|
+
const b = {
|
|
78
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
79
|
+
rollback: true,
|
|
80
|
+
errCode: Globals.ErrorCode_APIError,
|
|
81
|
+
transactionID: '123',
|
|
82
|
+
}
|
|
83
|
+
const context = observableContext()
|
|
84
|
+
const transaction = new Transaction<null, typeof b>(
|
|
85
|
+
emptyEvent({
|
|
86
|
+
requestContext: { requestId: '123' },
|
|
87
|
+
}),
|
|
88
|
+
context,
|
|
89
|
+
{
|
|
90
|
+
syncReturn: true,
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
// @ts-expect-error
|
|
94
|
+
const handlerResp = await transaction.execute(async () => {
|
|
95
|
+
return null
|
|
96
|
+
})
|
|
97
|
+
// check resp
|
|
98
|
+
c_expect(handlerResp).to.be.an.instanceof(Response)
|
|
99
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equals(b)
|
|
100
|
+
c_expect(handlerResp?.getCode()).to.be.equals(400)
|
|
101
|
+
// ctx
|
|
102
|
+
expect(context.fail).not.toBeCalled()
|
|
103
|
+
expect(context.done).not.toBeCalled()
|
|
104
|
+
expect(context.succeed).not.toBeCalled()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('Transaction error invocation path with non-Response class response', () => {
|
|
109
|
+
test('Not a response-class response returned success', async () => {
|
|
110
|
+
const b = {
|
|
111
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
112
|
+
rollback: true,
|
|
113
|
+
errCode: Globals.ErrorCode_APIError,
|
|
114
|
+
transactionID: 'unknown',
|
|
115
|
+
}
|
|
116
|
+
const b2 = { name: 'abc' }
|
|
117
|
+
const context = observableContext()
|
|
118
|
+
const transaction = new Transaction<null, null, typeof b2>(emptyEvent(), context)
|
|
119
|
+
const handlerResp = await transaction.execute(async () => {
|
|
120
|
+
return b2
|
|
121
|
+
})
|
|
122
|
+
// check resp
|
|
123
|
+
c_expect(handlerResp).to.be.null
|
|
124
|
+
// ctx
|
|
125
|
+
expect(context.fail).not.toBeCalled()
|
|
126
|
+
expect(context.done).not.toBeCalled()
|
|
127
|
+
expect(context.succeed).toBeCalledWith({
|
|
128
|
+
statusCode: 400,
|
|
129
|
+
body: JSON.stringify(b),
|
|
130
|
+
headers: defaultHeaders,
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('Not a response-class response returned success - sync return', async () => {
|
|
135
|
+
const b2 = { name: 'abc' }
|
|
136
|
+
const context = observableContext()
|
|
137
|
+
const transaction = new Transaction<null, null, typeof b2>(emptyEvent(), context, {
|
|
138
|
+
syncReturn: true,
|
|
139
|
+
})
|
|
140
|
+
const handlerResp = await transaction.execute(async () => {
|
|
141
|
+
return b2
|
|
142
|
+
})
|
|
143
|
+
// check resp
|
|
144
|
+
c_expect(handlerResp).to.be.deep.equals(b2)
|
|
145
|
+
// ctx
|
|
146
|
+
expect(context.fail).not.toBeCalled()
|
|
147
|
+
expect(context.done).not.toBeCalled()
|
|
148
|
+
expect(context.succeed).not.toBeCalled()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('Transaction error invocation path with exception', () => {
|
|
153
|
+
test('Transaction exception', async () => {
|
|
154
|
+
const b = {
|
|
155
|
+
err: Globals.ErrorResponseUnhandledError,
|
|
156
|
+
rollback: true,
|
|
157
|
+
errCode: Globals.ErrorCode_APIError,
|
|
158
|
+
transactionID: 'unknown',
|
|
159
|
+
}
|
|
160
|
+
const context = observableContext()
|
|
161
|
+
const transaction = new Transaction<null, null, null>(emptyEvent(), context)
|
|
162
|
+
const handlerResp = await transaction.execute(async () => {
|
|
163
|
+
throw new Error('Failed!')
|
|
164
|
+
})
|
|
165
|
+
// check resp
|
|
166
|
+
c_expect(handlerResp).to.be.null
|
|
167
|
+
// ctx
|
|
168
|
+
expect(context.fail).not.toBeCalled()
|
|
169
|
+
expect(context.done).not.toBeCalled()
|
|
170
|
+
expect(context.succeed).toBeCalledWith({
|
|
171
|
+
statusCode: 400,
|
|
172
|
+
body: JSON.stringify(b),
|
|
173
|
+
headers: defaultHeaders,
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test('Transaction exception - sync return', async () => {
|
|
178
|
+
const b = {
|
|
179
|
+
err: Globals.ErrorResponseUnhandledError,
|
|
180
|
+
rollback: true,
|
|
181
|
+
errCode: Globals.ErrorCode_APIError,
|
|
182
|
+
transactionID: 'unknown',
|
|
183
|
+
}
|
|
184
|
+
const context = observableContext()
|
|
185
|
+
const transaction = new Transaction<null, null, null>(emptyEvent(), context, {
|
|
186
|
+
syncReturn: true,
|
|
187
|
+
})
|
|
188
|
+
const handlerResp = await transaction.execute(async () => {
|
|
189
|
+
throw new Error('Failed!')
|
|
190
|
+
})
|
|
191
|
+
// check resp
|
|
192
|
+
c_expect(handlerResp?.getBody()).to.be.deep.equals(b)
|
|
193
|
+
c_expect(handlerResp?.getCode()).to.be.equals(400)
|
|
194
|
+
// ctx
|
|
195
|
+
expect(context.fail).not.toBeCalled()
|
|
196
|
+
expect(context.done).not.toBeCalled()
|
|
197
|
+
expect(context.succeed).not.toBeCalled()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('Transaction exception - sync return', async () => {
|
|
201
|
+
const context = observableContext()
|
|
202
|
+
const transaction = new Transaction<null, null, null>(emptyEvent(), context, {
|
|
203
|
+
throwOnErrors: true,
|
|
204
|
+
})
|
|
205
|
+
const error = new Error('Failed!')
|
|
206
|
+
let handlerResp: any = null,
|
|
207
|
+
err: any = null
|
|
208
|
+
try {
|
|
209
|
+
handlerResp = await transaction.execute(async () => {
|
|
210
|
+
throw error
|
|
211
|
+
})
|
|
212
|
+
} catch (e) {
|
|
213
|
+
err = e
|
|
214
|
+
}
|
|
215
|
+
// check resp
|
|
216
|
+
c_expect(handlerResp).to.be.null
|
|
217
|
+
c_expect(err).to.be.deep.equals(error)
|
|
218
|
+
// ctx
|
|
219
|
+
expect(context.fail).not.toBeCalled()
|
|
220
|
+
expect(context.done).not.toBeCalled()
|
|
221
|
+
expect(context.succeed).not.toBeCalled()
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
describe('Transaction - db integration', () => {
|
|
226
|
+
const testResources = () => {
|
|
227
|
+
const mockTrans = {
|
|
228
|
+
select: jest.fn(),
|
|
229
|
+
closeSuccess: jest.fn(),
|
|
230
|
+
closeFailure: jest.fn(),
|
|
231
|
+
}
|
|
232
|
+
const mockCreate = jest.fn(
|
|
233
|
+
() => ({ transaction: () => new Promise(resolve => resolve(mockTrans)) }) as any
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
const transaction = new Transaction(emptyEvent(), observableContext())
|
|
237
|
+
transaction['databaseManager'] = {
|
|
238
|
+
create: mockCreate,
|
|
239
|
+
} as any
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
transaction,
|
|
243
|
+
mockCreate,
|
|
244
|
+
mockTrans,
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const config = { username: 'test' } as DbConfig<'knex'>
|
|
249
|
+
|
|
250
|
+
test('Transaction provides dbTransaction and calls closeSuccess', async () => {
|
|
251
|
+
const { mockCreate, mockTrans, transaction } = testResources()
|
|
252
|
+
|
|
253
|
+
await transaction.execute(async () => {
|
|
254
|
+
const knexTransaction = await transaction.getDbTransaction(config)
|
|
255
|
+
|
|
256
|
+
knexTransaction.select('name')
|
|
257
|
+
|
|
258
|
+
return Response.SuccessResponse(null)
|
|
259
|
+
})
|
|
260
|
+
expect(mockCreate).toBeCalledWith(config)
|
|
261
|
+
expect(mockTrans.select).toBeCalledWith('name')
|
|
262
|
+
expect(mockTrans.closeSuccess).toBeCalled()
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('Errored transaction calls closeFailure', async () => {
|
|
266
|
+
const { mockCreate, transaction, mockTrans } = testResources()
|
|
267
|
+
|
|
268
|
+
const ss = await transaction.execute(async () => {
|
|
269
|
+
const _ii = await transaction.getDbTransaction(config)
|
|
270
|
+
console.log('ii', _ii)
|
|
271
|
+
throw new Error('Something went wrong!')
|
|
272
|
+
|
|
273
|
+
return Response.SuccessResponse(null)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
console.log(ss)
|
|
277
|
+
|
|
278
|
+
expect(mockCreate).toBeCalledWith(config)
|
|
279
|
+
expect(mockTrans.closeFailure).toBeCalled()
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
describe('Transaction response types', () => {
|
|
284
|
+
test('Non-response return', async () => {
|
|
285
|
+
const b = { name: '123' }
|
|
286
|
+
const b2 = {
|
|
287
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
288
|
+
rollback: true,
|
|
289
|
+
errCode: Globals.ErrorCode_APIError,
|
|
290
|
+
transactionID: 'unknown',
|
|
291
|
+
}
|
|
292
|
+
const context = observableContext()
|
|
293
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
294
|
+
// @ts-expect-error
|
|
295
|
+
const handlerResp = await transaction.execute(async () => {
|
|
296
|
+
return { d: 1 }
|
|
297
|
+
})
|
|
298
|
+
// check resp
|
|
299
|
+
c_expect(handlerResp).to.be.null
|
|
300
|
+
// ctx
|
|
301
|
+
expect(context.fail).not.toBeCalled()
|
|
302
|
+
expect(context.done).not.toBeCalled()
|
|
303
|
+
expect(context.succeed).toBeCalledWith({
|
|
304
|
+
statusCode: 400,
|
|
305
|
+
body: JSON.stringify(b2),
|
|
306
|
+
headers: defaultHeaders,
|
|
307
|
+
})
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
test('Null return', async () => {
|
|
311
|
+
const b = { name: '123' }
|
|
312
|
+
const b2 = {
|
|
313
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
314
|
+
rollback: true,
|
|
315
|
+
errCode: Globals.ErrorCode_APIError,
|
|
316
|
+
transactionID: 'unknown',
|
|
317
|
+
}
|
|
318
|
+
const context = observableContext()
|
|
319
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
320
|
+
// @ts-expect-error
|
|
321
|
+
const handlerResp = await transaction.execute(async () => {
|
|
322
|
+
return null
|
|
323
|
+
})
|
|
324
|
+
// check resp
|
|
325
|
+
c_expect(handlerResp).to.be.null
|
|
326
|
+
// ctx
|
|
327
|
+
expect(context.fail).not.toBeCalled()
|
|
328
|
+
expect(context.done).not.toBeCalled()
|
|
329
|
+
expect(context.succeed).toBeCalledWith({
|
|
330
|
+
statusCode: 400,
|
|
331
|
+
body: JSON.stringify(b2),
|
|
332
|
+
headers: defaultHeaders,
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
test('Undefined return', async () => {
|
|
337
|
+
const b = { name: '123' }
|
|
338
|
+
const b2 = {
|
|
339
|
+
err: Globals.ErrorResponseInvalidServerResponse,
|
|
340
|
+
rollback: true,
|
|
341
|
+
errCode: Globals.ErrorCode_APIError,
|
|
342
|
+
transactionID: 'unknown',
|
|
343
|
+
}
|
|
344
|
+
const context = observableContext()
|
|
345
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
346
|
+
// @ts-expect-error
|
|
347
|
+
const handlerResp = await transaction.execute(async () => {
|
|
348
|
+
return
|
|
349
|
+
})
|
|
350
|
+
// check resp
|
|
351
|
+
c_expect(handlerResp).to.be.null
|
|
352
|
+
// ctx
|
|
353
|
+
expect(context.fail).not.toBeCalled()
|
|
354
|
+
expect(context.done).not.toBeCalled()
|
|
355
|
+
expect(context.succeed).toBeCalledWith({
|
|
356
|
+
statusCode: 400,
|
|
357
|
+
body: JSON.stringify(b2),
|
|
358
|
+
headers: defaultHeaders,
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
test('Null inner-response return', async () => {
|
|
363
|
+
const b = { name: '123' }
|
|
364
|
+
const b2 = { transactionID: 'unknown' }
|
|
365
|
+
const context = observableContext()
|
|
366
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
367
|
+
// @ts-expect-error
|
|
368
|
+
const handlerResp = await transaction.execute(async () => {
|
|
369
|
+
return Response.SuccessResponse(null)
|
|
370
|
+
})
|
|
371
|
+
// check resp
|
|
372
|
+
c_expect(handlerResp).to.be.null
|
|
373
|
+
// ctx
|
|
374
|
+
expect(context.fail).not.toBeCalled()
|
|
375
|
+
expect(context.done).not.toBeCalled()
|
|
376
|
+
expect(context.succeed).toBeCalledWith({
|
|
377
|
+
statusCode: 200,
|
|
378
|
+
body: JSON.stringify(b2),
|
|
379
|
+
headers: defaultHeaders,
|
|
380
|
+
})
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
test('Wrong keys inner-response return', async () => {
|
|
384
|
+
const b = { name: '123' }
|
|
385
|
+
const b2 = {
|
|
386
|
+
notName: '123',
|
|
387
|
+
transactionID: 'unknown',
|
|
388
|
+
}
|
|
389
|
+
const context = observableContext()
|
|
390
|
+
const transaction = new Transaction<null, typeof b>(emptyEvent(), context)
|
|
391
|
+
// @ts-expect-error
|
|
392
|
+
const handlerResp = await transaction.execute(async () => {
|
|
393
|
+
return Response.SuccessResponse({ notName: '123' })
|
|
394
|
+
})
|
|
395
|
+
// check resp
|
|
396
|
+
c_expect(handlerResp).to.be.null
|
|
397
|
+
// ctx
|
|
398
|
+
expect(context.fail).not.toBeCalled()
|
|
399
|
+
expect(context.done).not.toBeCalled()
|
|
400
|
+
expect(context.succeed).toBeCalledWith({
|
|
401
|
+
statusCode: 200,
|
|
402
|
+
body: JSON.stringify(b2),
|
|
403
|
+
headers: defaultHeaders,
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
export {}
|