@@ -30,6 +30,105 @@ const LOCKS_TERMINATED_ERROR =
30
30
'Neo.TransientError.Transaction.LockClientStopped'
31
31
const OOM_ERROR = 'Neo.DatabaseError.General.OutOfMemoryError'
32
32
33
+ // Not exactly integration tests but annoyingly slow for being a unit tests.
34
+ describe ( '#integration TransactionExecutor' , ( ) => {
35
+ let originalTimeout
36
+
37
+ beforeEach ( ( ) => {
38
+ originalTimeout = jasmine . DEFAULT_TIMEOUT_INTERVAL
39
+ jasmine . DEFAULT_TIMEOUT_INTERVAL = 30000
40
+ } )
41
+
42
+ afterEach ( ( ) => {
43
+ jasmine . DEFAULT_TIMEOUT_INTERVAL = originalTimeout
44
+ } )
45
+
46
+ it ( 'should retry until database error happens' , async ( ) => {
47
+ await testNoRetryOnUnknownError (
48
+ [
49
+ SERVICE_UNAVAILABLE ,
50
+ SERVICE_UNAVAILABLE ,
51
+ TRANSIENT_ERROR_2 ,
52
+ SESSION_EXPIRED ,
53
+ UNKNOWN_ERROR ,
54
+ SESSION_EXPIRED
55
+ ] ,
56
+ 5
57
+ )
58
+ } )
59
+
60
+ it ( 'should stop retrying when time expires' , async ( ) => {
61
+ let clock
62
+ const usedTransactions = [ ]
63
+ try {
64
+ const executor = new TransactionExecutor ( )
65
+ const realWork = transactionWork (
66
+ [
67
+ SERVICE_UNAVAILABLE ,
68
+ SESSION_EXPIRED ,
69
+ TRANSIENT_ERROR_1 ,
70
+ TRANSIENT_ERROR_2
71
+ ] ,
72
+ 42
73
+ )
74
+
75
+ await executor . execute ( transactionCreator ( ) , tx => {
76
+ expect ( tx ) . toBeDefined ( )
77
+ usedTransactions . push ( tx )
78
+ if ( usedTransactions . length === 3 ) {
79
+ const currentTime = Date . now ( )
80
+ clock = lolex . install ( )
81
+ clock . setSystemTime ( currentTime + 30001 ) // move `Date.now()` call forward by 30 seconds
82
+ }
83
+ return realWork ( )
84
+ } )
85
+
86
+ expect ( false ) . toBeTruthy ( 'should have thrown an exception' )
87
+ } catch ( error ) {
88
+ expect ( usedTransactions . length ) . toEqual ( 3 )
89
+ expectAllTransactionsToBeClosed ( usedTransactions )
90
+ expect ( error . code ) . toEqual ( TRANSIENT_ERROR_1 )
91
+ } finally {
92
+ if ( clock ) {
93
+ clock . uninstall ( )
94
+ }
95
+ }
96
+ } )
97
+
98
+ it ( 'should cancel in-flight timeouts when closed' , async ( ) => {
99
+ const fakeSetTimeout = setTimeoutMock . install ( )
100
+ try {
101
+ const executor = new TransactionExecutor ( )
102
+ // do not execute setTimeout callbacks
103
+ fakeSetTimeout . pause ( )
104
+
105
+ executor . execute ( transactionCreator ( [ SERVICE_UNAVAILABLE ] ) , ( ) =>
106
+ Promise . resolve ( 42 )
107
+ )
108
+ executor . execute ( transactionCreator ( [ TRANSIENT_ERROR_1 ] ) , ( ) =>
109
+ Promise . resolve ( 4242 )
110
+ )
111
+ executor . execute ( transactionCreator ( [ SESSION_EXPIRED ] ) , ( ) =>
112
+ Promise . resolve ( 424242 )
113
+ )
114
+
115
+ await new Promise ( ( resolve , reject ) => {
116
+ fakeSetTimeout . setTimeoutOriginal ( ( ) => {
117
+ try {
118
+ executor . close ( )
119
+ expect ( fakeSetTimeout . clearedTimeouts . length ) . toEqual ( 3 )
120
+ resolve ( )
121
+ } catch ( error ) {
122
+ reject ( error )
123
+ }
124
+ } , 1000 )
125
+ } )
126
+ } finally {
127
+ fakeSetTimeout . uninstall ( )
128
+ }
129
+ } )
130
+ } )
131
+
33
132
describe ( '#unit TransactionExecutor' , ( ) => {
34
133
let originalTimeout
35
134
@@ -97,44 +196,6 @@ describe('#unit TransactionExecutor', () => {
97
196
) . toBeRejectedWith ( error )
98
197
} )
99
198
100
- it ( 'should stop retrying when time expires' , async ( ) => {
101
- let clock
102
- const usedTransactions = [ ]
103
- try {
104
- const executor = new TransactionExecutor ( )
105
- const realWork = transactionWork (
106
- [
107
- SERVICE_UNAVAILABLE ,
108
- SESSION_EXPIRED ,
109
- TRANSIENT_ERROR_1 ,
110
- TRANSIENT_ERROR_2
111
- ] ,
112
- 42
113
- )
114
-
115
- await executor . execute ( transactionCreator ( ) , tx => {
116
- expect ( tx ) . toBeDefined ( )
117
- usedTransactions . push ( tx )
118
- if ( usedTransactions . length === 3 ) {
119
- const currentTime = Date . now ( )
120
- clock = lolex . install ( )
121
- clock . setSystemTime ( currentTime + 30001 ) // move `Date.now()` call forward by 30 seconds
122
- }
123
- return realWork ( )
124
- } )
125
-
126
- expect ( false ) . toBeTruthy ( 'should have thrown an exception' )
127
- } catch ( error ) {
128
- expect ( usedTransactions . length ) . toEqual ( 3 )
129
- expectAllTransactionsToBeClosed ( usedTransactions )
130
- expect ( error . code ) . toEqual ( TRANSIENT_ERROR_1 )
131
- } finally {
132
- if ( clock ) {
133
- clock . uninstall ( )
134
- }
135
- }
136
- } )
137
-
138
199
it ( 'should retry when given transaction creator throws once' , async ( ) => {
139
200
await testRetryWhenTransactionCreatorFails ( [ SERVICE_UNAVAILABLE ] )
140
201
} )
@@ -190,20 +251,6 @@ describe('#unit TransactionExecutor', () => {
190
251
] )
191
252
} )
192
253
193
- it ( 'should retry until database error happens' , async ( ) => {
194
- await testNoRetryOnUnknownError (
195
- [
196
- SERVICE_UNAVAILABLE ,
197
- SERVICE_UNAVAILABLE ,
198
- TRANSIENT_ERROR_2 ,
199
- SESSION_EXPIRED ,
200
- UNKNOWN_ERROR ,
201
- SESSION_EXPIRED
202
- ] ,
203
- 5
204
- )
205
- } )
206
-
207
254
it ( 'should retry when transaction work throws and rollback fails' , async ( ) => {
208
255
await testRetryWhenTransactionWorkThrowsAndRollbackFails (
209
256
[
@@ -216,39 +263,6 @@ describe('#unit TransactionExecutor', () => {
216
263
)
217
264
} )
218
265
219
- it ( 'should cancel in-flight timeouts when closed' , async ( ) => {
220
- const fakeSetTimeout = setTimeoutMock . install ( )
221
- try {
222
- const executor = new TransactionExecutor ( )
223
- // do not execute setTimeout callbacks
224
- fakeSetTimeout . pause ( )
225
-
226
- executor . execute ( transactionCreator ( [ SERVICE_UNAVAILABLE ] ) , ( ) =>
227
- Promise . resolve ( 42 )
228
- )
229
- executor . execute ( transactionCreator ( [ TRANSIENT_ERROR_1 ] ) , ( ) =>
230
- Promise . resolve ( 4242 )
231
- )
232
- executor . execute ( transactionCreator ( [ SESSION_EXPIRED ] ) , ( ) =>
233
- Promise . resolve ( 424242 )
234
- )
235
-
236
- await new Promise ( ( resolve , reject ) => {
237
- fakeSetTimeout . setTimeoutOriginal ( ( ) => {
238
- try {
239
- executor . close ( )
240
- expect ( fakeSetTimeout . clearedTimeouts . length ) . toEqual ( 3 )
241
- resolve ( )
242
- } catch ( error ) {
243
- reject ( error )
244
- }
245
- } , 1000 )
246
- } )
247
- } finally {
248
- fakeSetTimeout . uninstall ( )
249
- }
250
- } )
251
-
252
266
it ( 'should allow zero max retry time' , ( ) => {
253
267
const executor = new TransactionExecutor ( 0 )
254
268
expect ( executor . _maxRetryTimeMs ) . toEqual ( 0 )
@@ -396,35 +410,35 @@ describe('#unit TransactionExecutor', () => {
396
410
fakeSetTimeout . uninstall ( )
397
411
}
398
412
}
413
+ } )
399
414
400
- async function testNoRetryOnUnknownError (
401
- errorCodes ,
402
- expectedWorkInvocationCount
403
- ) {
404
- const executor = new TransactionExecutor ( )
405
- const usedTransactions = [ ]
406
- const realWork = transactionWork ( errorCodes , 42 )
407
-
408
- try {
409
- await executor . execute ( transactionCreator ( ) , tx => {
410
- expect ( tx ) . toBeDefined ( )
411
- usedTransactions . push ( tx )
412
- return realWork ( )
413
- } )
414
- } catch ( error ) {
415
- expect ( usedTransactions . length ) . toEqual ( expectedWorkInvocationCount )
416
- expectAllTransactionsToBeClosed ( usedTransactions )
417
- if ( errorCodes . length === 1 ) {
418
- expect ( error . code ) . toEqual ( errorCodes [ 0 ] )
419
- } else {
420
- expect ( error . code ) . toEqual ( errorCodes [ expectedWorkInvocationCount - 1 ] )
421
- }
422
- return
415
+ async function testNoRetryOnUnknownError (
416
+ errorCodes ,
417
+ expectedWorkInvocationCount
418
+ ) {
419
+ const executor = new TransactionExecutor ( )
420
+ const usedTransactions = [ ]
421
+ const realWork = transactionWork ( errorCodes , 42 )
422
+
423
+ try {
424
+ await executor . execute ( transactionCreator ( ) , tx => {
425
+ expect ( tx ) . toBeDefined ( )
426
+ usedTransactions . push ( tx )
427
+ return realWork ( )
428
+ } )
429
+ } catch ( error ) {
430
+ expect ( usedTransactions . length ) . toEqual ( expectedWorkInvocationCount )
431
+ expectAllTransactionsToBeClosed ( usedTransactions )
432
+ if ( errorCodes . length === 1 ) {
433
+ expect ( error . code ) . toEqual ( errorCodes [ 0 ] )
434
+ } else {
435
+ expect ( error . code ) . toEqual ( errorCodes [ expectedWorkInvocationCount - 1 ] )
423
436
}
424
-
425
- expect ( false ) . toBeTruthy ( 'exception expected' )
437
+ return
426
438
}
427
- } )
439
+
440
+ expect ( false ) . toBeTruthy ( 'exception expected' )
441
+ }
428
442
429
443
function transactionCreator ( commitErrorCodes , rollbackErrorCodes ) {
430
444
const remainingCommitErrorCodes = ( commitErrorCodes || [ ] ) . slice ( ) . reverse ( )
0 commit comments