1
|
|
|
/* eslint-env mocha */ |
2
|
|
|
/* eslint-disable no-unused-expressions */ |
3
|
|
|
|
4
|
|
|
var Sinon = require('sinon') |
5
|
|
|
var Chai = require('chai') |
6
|
|
|
var expect = Chai.expect |
7
|
|
|
|
8
|
|
|
Chai.use(require('chai-as-promised')) |
9
|
|
|
|
10
|
|
|
var Executor = require('../../../../lib/Execution/Executor').Executor |
11
|
|
|
var SDK = require('@ama-team/voxengine-sdk') |
12
|
|
|
var CancellationToken = SDK.Concurrent.CancellationToken |
13
|
|
|
var TimeoutException = SDK.Concurrent.TimeoutException |
14
|
|
|
|
15
|
|
|
var branchStopper = function () { |
16
|
|
|
throw new Error('This logical path should not have been taken') |
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
describe('Unit', function () { |
20
|
|
|
describe('/Execution', function () { |
21
|
|
|
describe('/Executor.js', function () { |
22
|
|
|
describe('.Executor', function () { |
23
|
|
|
var factory = function (ctx) { |
24
|
|
|
return new Executor(ctx) |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
var callable |
28
|
|
|
var context |
29
|
|
|
var executor |
30
|
|
|
|
31
|
|
|
var autoFactory = function () { |
32
|
|
|
executor = factory(context) |
33
|
|
|
return executor |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
beforeEach(function () { |
37
|
|
|
callable = Sinon.stub() |
38
|
|
|
context = {} |
39
|
|
|
autoFactory() |
40
|
|
|
}) |
41
|
|
|
|
42
|
|
|
describe('#execute()', function () { |
43
|
|
|
it('passes context and arguments', function () { |
44
|
|
|
var ctx = {x: 12} |
45
|
|
|
var args = [1, 2, 3] |
46
|
|
|
var result = ['uno', 'duo', 'tres'] |
47
|
|
|
callable.returns(result) |
48
|
|
|
expect(factory(ctx).execute(callable, args)).to.eq(result) |
49
|
|
|
expect(callable.callCount).to.eq(1) |
50
|
|
|
expect(callable.getCall(0).thisValue).to.eq(ctx) |
51
|
|
|
for (var i = 0; i < args.length; i++) { |
52
|
|
|
expect(callable.getCall(0).args[i]).to.eq(args[i]) |
53
|
|
|
} |
54
|
|
|
}) |
55
|
|
|
|
56
|
|
|
it('doesn\'t catch errors', function () { |
57
|
|
|
var error = new Error() |
58
|
|
|
callable.throws(error) |
59
|
|
|
var lambda = function () { |
60
|
|
|
return factory().execute(callable) |
61
|
|
|
} |
62
|
|
|
expect(lambda).to.throw(error) |
63
|
|
|
}) |
64
|
|
|
}) |
65
|
|
|
|
66
|
|
|
describe('#promise()', function () { |
67
|
|
|
it('passes context and arguments', function () { |
68
|
|
|
var ctx = {x: 12} |
69
|
|
|
var args = [1, 2, 3] |
70
|
|
|
var result = ['uno', 'duo', 'tres'] |
71
|
|
|
callable.returns(result) |
72
|
|
|
return factory(ctx) |
73
|
|
|
.promise(callable, args) |
74
|
|
|
.then(function (value) { |
75
|
|
|
expect(value).to.eq(result) |
76
|
|
|
expect(callable.callCount).to.eq(1) |
77
|
|
|
expect(callable.getCall(0).thisValue).to.eq(ctx) |
78
|
|
|
for (var i = 0; i < args.length; i++) { |
79
|
|
|
expect(callable.getCall(0).args[i]).to.eq(args[i]) |
80
|
|
|
} |
81
|
|
|
}) |
82
|
|
|
}) |
83
|
|
|
|
84
|
|
|
it('catches error and rejects promise with it', function () { |
85
|
|
|
var error = new Error() |
86
|
|
|
callable.throws(error) |
87
|
|
|
return factory(null) |
88
|
|
|
.promise(callable) |
89
|
|
|
.then(function () { |
90
|
|
|
throw new Error('This branch should not have been executed') |
91
|
|
|
}, function (reason) { |
92
|
|
|
expect(reason).to.eq(error) |
93
|
|
|
}) |
94
|
|
|
}) |
95
|
|
|
}) |
96
|
|
|
|
97
|
|
|
describe('#runHandler()', function () { |
98
|
|
|
var callable |
99
|
|
|
var onTimeout |
100
|
|
|
var onTimeoutTimeout |
101
|
|
|
var handler |
102
|
|
|
|
103
|
|
|
var autoHandlerFactory = function () { |
104
|
|
|
handler = { |
105
|
|
|
id: 'handler', |
106
|
|
|
handler: callable, |
107
|
|
|
timeout: null, |
108
|
|
|
onTimeout: { |
109
|
|
|
id: 'onHandlerTimeout', |
110
|
|
|
handler: onTimeout, |
111
|
|
|
timeout: null, |
112
|
|
|
onTimeout: { |
113
|
|
|
id: 'onOnHandlerTimeoutTimeout', |
114
|
|
|
handler: onTimeoutTimeout, |
115
|
|
|
timeout: null |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
return handler |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
beforeEach(function () { |
123
|
|
|
callable = Sinon.stub().returns(1) |
124
|
|
|
onTimeout = Sinon.stub().returns(2) |
125
|
|
|
onTimeoutTimeout = Sinon.stub().returns(3) |
126
|
|
|
autoHandlerFactory() |
127
|
|
|
}) |
128
|
|
|
|
129
|
|
|
it('performs standard, no-error, no-timeout run', function () { |
130
|
|
|
var args = [1, 2] |
131
|
|
|
var result = autoFactory().runHandler(handler, args) |
132
|
|
|
return result |
133
|
|
|
.then(function (result) { |
134
|
|
|
expect(result).to.eq(1) |
135
|
|
|
expect(callable.callCount).to.eq(1) |
136
|
|
|
expect(callable.getCall(0).thisValue).to.eq(context) |
137
|
|
|
for (var i = 0; i < args.length; i++) { |
138
|
|
|
expect(callable.getCall(0).args[i]).to.eq(args[i]) |
139
|
|
|
} |
140
|
|
|
expect(callable.getCall(0).args[args.length]).to.be.instanceOf(CancellationToken) |
141
|
|
|
expect(onTimeout.callCount).to.eq(0) |
142
|
|
|
}) |
143
|
|
|
}) |
144
|
|
|
|
145
|
|
|
it('delegates work to onTimeout handler in case of timeout', function () { |
146
|
|
|
var args = [1, 2] |
147
|
|
|
callable.returns(new Promise(function () {})) |
148
|
|
|
autoHandlerFactory() |
149
|
|
|
handler.timeout = 0 |
150
|
|
|
var result = autoFactory().runHandler(handler, args) |
151
|
|
|
return result |
152
|
|
|
.then(function (value) { |
153
|
|
|
expect(value).to.eq(2) |
154
|
|
|
expect(callable.callCount).to.eq(1) |
155
|
|
|
expect(onTimeout.callCount).to.eq(1) |
156
|
|
|
expect(onTimeout.getCall(0).thisValue).to.eq(context) |
157
|
|
|
for (var i = 0; i < args.length; i++) { |
158
|
|
|
expect(onTimeout.getCall(0).args[i]).to.eq(args[i]) |
159
|
|
|
} |
160
|
|
|
expect(onTimeout.getCall(0).args[args.length]).to.be.instanceOf(CancellationToken) |
161
|
|
|
expect(onTimeout.getCall(0).args[args.length + 1]).to.be.instanceOf(TimeoutException) |
162
|
|
|
expect(onTimeoutTimeout.callCount).to.eq(0) |
163
|
|
|
}) |
164
|
|
|
}) |
165
|
|
|
|
166
|
|
|
it('delegates work to onTimeoutTimeout handler in case of multiple timeouts', function () { |
167
|
|
|
var args = [1, 2] |
168
|
|
|
callable.returns(new Promise(function () {})) |
169
|
|
|
onTimeout.returns(new Promise(function () {})) |
170
|
|
|
autoHandlerFactory() |
171
|
|
|
handler.timeout = 0 |
172
|
|
|
handler.onTimeout.timeout = 0 |
173
|
|
|
var result = autoFactory().runHandler(handler, args) |
174
|
|
|
return result |
175
|
|
|
.then(function (value) { |
176
|
|
|
expect(value).to.eq(3) |
177
|
|
|
expect(callable.callCount).to.eq(1) |
178
|
|
|
expect(onTimeout.callCount).to.eq(1) |
179
|
|
|
expect(onTimeoutTimeout.callCount).to.eq(1) |
180
|
|
|
expect(onTimeoutTimeout.getCall(0).thisValue).to.eq(context) |
181
|
|
|
for (var i = 0; i < args.length; i++) { |
182
|
|
|
expect(onTimeoutTimeout.getCall(0).args[i]).to.eq(args[i]) |
183
|
|
|
} |
184
|
|
|
expect(onTimeoutTimeout.getCall(0).args[args.length]).to.be.instanceOf(CancellationToken) |
185
|
|
|
expect(onTimeoutTimeout.getCall(0).args[args.length + 1]).to.be.instanceOf(TimeoutException) |
186
|
|
|
expect(onTimeoutTimeout.getCall(0).args[args.length + 2]).to.be.instanceOf(TimeoutException) |
187
|
|
|
}) |
188
|
|
|
}) |
189
|
|
|
|
190
|
|
|
it('rejects with timeout error if every part of chain times out', function () { |
191
|
|
|
callable.returns(new Promise(function () {})) |
192
|
|
|
onTimeout.returns(new Promise(function () {})) |
193
|
|
|
onTimeoutTimeout.returns(new Promise(function () {})) |
194
|
|
|
autoHandlerFactory() |
195
|
|
|
handler.timeout = 0 |
196
|
|
|
handler.onTimeout.timeout = 0 |
197
|
|
|
handler.onTimeout.onTimeout.timeout = 0 |
198
|
|
|
return autoFactory() |
199
|
|
|
.runHandler(handler) |
200
|
|
|
.then(branchStopper, function (e) { |
201
|
|
|
expect(e).to.be.instanceOf(TimeoutException) |
202
|
|
|
}) |
203
|
|
|
}) |
204
|
|
|
|
205
|
|
|
it('cancels token on timeout', function () { |
206
|
|
|
callable.returns(new Promise(function () {})) |
207
|
|
|
autoHandlerFactory() |
208
|
|
|
handler.timeout = 0 |
209
|
|
|
return autoFactory() |
210
|
|
|
.runHandler(handler) |
211
|
|
|
.then(function () { |
212
|
|
|
expect(callable.callCount).to.eq(1) |
213
|
|
|
var token = callable.getCall(0).args[0] |
214
|
|
|
expect(token).to.be.instanceOf(CancellationToken) |
215
|
|
|
expect(token.isCancelled()).to.be.true |
|
|
|
|
216
|
|
|
}) |
217
|
|
|
}) |
218
|
|
|
|
219
|
|
|
it('creates tokens dependent on passed one', function () { |
220
|
|
|
callable = Sinon.spy(function (token) { |
221
|
|
|
// token is a natural thenable |
222
|
|
|
return token |
223
|
|
|
}) |
224
|
|
|
autoHandlerFactory() |
225
|
|
|
var token = new CancellationToken() |
226
|
|
|
var promise = autoFactory().runHandler(handler, [], token) |
227
|
|
|
token.cancel() |
228
|
|
|
return promise |
229
|
|
|
.then(function () { |
230
|
|
|
expect(callable.callCount).to.eq(1) |
231
|
|
|
var token = callable.getCall(0).args[0] |
232
|
|
|
expect(token).to.be.instanceOf(CancellationToken) |
233
|
|
|
expect(token.isCancelled()).to.be.true |
|
|
|
|
234
|
|
|
}) |
235
|
|
|
}) |
236
|
|
|
}) |
237
|
|
|
}) |
238
|
|
|
}) |
239
|
|
|
}) |
240
|
|
|
}) |
241
|
|
|
|