Completed
Push — dev ( 249398...dac991 )
by Fike
39s
created

test/suites/integration/Execution/Transition.spec.js   F

Complexity

Total Complexity 138
Complexity/F 2.3

Size

Lines of Code 286
Function Count 60

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
wmc 138
nc 128
mnd 1
bc 60
fnc 60
dl 0
loc 286
rs 3.12
bpm 1
cpm 2.3
noi 2
c 1
b 0
f 0

1 Function

Rating   Name   Duplication   Size   Complexity  
B Transition.spec.js ➔ describe(ꞌIntegrationꞌ) 0 272 1

How to fix   Complexity   

Complexity

Complex classes like test/suites/integration/Execution/Transition.spec.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 SDK = require('@ama-team/voxengine-sdk')
11
var TimeoutException = SDK.Concurrent.TimeoutException
12
var Future = SDK.Concurrent.Future
13
14
var Transition = require('../../../../lib/Execution/Transition').Transition
15
var Executor = require('../../../../lib/Execution/Executor').Executor
16
var Status = Transition.Status
17
18
describe('Integration', function () {
19
  describe('/Execution', function () {
20
    describe('/Transition.js', function () {
21
      describe('.Transition', function () {
22
        var executor = new Executor(null)
23
24
        var handlerFactory = function (handler, name, timeout) {
25
          name = name || 'handler'
26
          return {
27
            id: name,
28
            handler: Sinon.spy(handler),
29
            timeout: typeof timeout === 'number' ? timeout : null,
30
            onTimeout: {
31
              id: 'timeout' + name[0].toUpperCase() + name.substr(1),
32
              handler: Sinon.spy(handler),
33
              timeout: typeof timeout === 'number' ? timeout : null
34
            }
35
          }
36
        }
37
38
        var stateFactory = function (handler, timeout, id) {
39
          handler = handler || function () {
40
            return Promise.resolve()
41
          }
42
          var structure = {
43
            id: id || 'target-stub',
44
            timeout: null,
45
            entrypoint: false,
46
            terminal: false
47
          }
48
          var handlers = ['transition', 'abort']
49
          handlers.forEach(function (name) {
50
            structure[name] = handlerFactory(handler, name, timeout)
51
          })
52
          return structure
53
        }
54
55
        var factory = function (target, hints, origin) {
56
          var options = {
57
            origin: origin || stateFactory(null, null, 'origin-stub'),
58
            target: target || stateFactory(),
59
            hints: hints,
60
            executor: executor
61
          }
62
          return new Transition(options)
63
        }
64
65
        describe('< new', function () {
66
          it('throws error if target state not set', function () {
67
            expect(function () { return new Transition({}) }).to.throw()
68
          })
69
70
          it('throws error if options are omitted', function () {
71
            expect(function () { return new Transition() }).to.throw()
72
          })
73
74
          it('throws error if garbage is passed as options', function () {
75
            expect(function () { return new Transition(false) }).to.throw()
76
          })
77
        })
78
79
        describe('#run()', function () {
80
          it('throws on second call', function () {
81
            var transition = factory()
82
            transition.run()
83
            expect(transition.run).to.throw()
84
          })
85
86
          it('ends with Executed status if everything went smoothly', function () {
87
            var value = {x: 12}
88
            var target = stateFactory(function () {
89
              return value
90
            })
91
            var transition = factory(target)
92
            return transition
93
              .run()
94
              .then(function (result) {
95
                expect(result.value).to.eq(value)
96
                expect(result.status).to.eq(Transition.Status.Executed)
97
              })
98
          })
99
100
          it('ends with Executed status if timeout handler has rescued situation', function () {
101
            var value = {x: 12}
102
            var target = stateFactory()
103
            var promise = new Promise(function () {})
104
            target.transition.handler = Sinon.stub().returns(promise)
105
            target.transition.timeout = 0
106
            target.transition.onTimeout.handler = Sinon.stub().returns(value)
107
            var transition = factory(target)
108
            return transition
109
              .run()
110
              .then(function (result) {
111
                expect(target.transition.handler.callCount).to.eq(1)
112
                expect(target.transition.onTimeout.handler.callCount).to.eq(1)
113
                var token = target.transition.handler.getCall(0).args[2]
114
                expect(token.isCancelled()).to.be.true
0 ignored issues
show
introduced by
The result of the property access to expect(token.isCancelled()).to.be.true is not used.
Loading history...
115
                expect(result.value).to.eq(value)
116
                expect(result.status).to.eq(Status.Executed)
117
              })
118
          })
119
120
          it('ends with ExecutionFailure status if everything has timed out', function () {
121
            var target = stateFactory(function () {
122
              return new Promise(function () {
123
              })
124
            }, 0)
125
            var transition = factory(target)
126
            return transition
127
              .run()
128
              .then(function (result) {
129
                expect(result.value).to.be.instanceOf(TimeoutException)
130
                expect(result.status).to.eq(Status.ExecutionFailure)
131
              })
132
          })
133
134
          it('passes hints to handler', function () {
135
            var target = stateFactory()
136
            var hints = {x: 12}
137
            var transition = factory(target, hints)
138
            return transition
139
                .run()
140
                .then(function () {
141
                  var arg = target.transition.handler.getCall(0).args[1]
142
                  expect(arg).to.eq(hints)
143
                })
144
          })
145
        })
146
147
        describe('#abort()', function () {
148
          it('throws if transition is not started', function () {
149
            var transition = factory()
150
            expect(transition.abort).to.throw()
151
          })
152
153
          it('throws if transition has already finished', function () {
154
            var transition = factory()
155
            return transition
156
              .run()
157
              .then(function () {
158
                expect(transition.abort).to.throw()
159
              })
160
          })
161
162
          it('ends with Aborted status if has been aborted before completion', function () {
163
            var value = {x: 12}
164
            var target = stateFactory()
165
            target.transition.handler = function () {
166
              return new Promise(function () {})
167
            }
168
            target.abort.handler = function () {
169
              return value
170
            }
171
            var transition = factory(target)
172
            transition.run()
173
            var promise = transition.abort()
174
            return promise
175
              .then(function (result) {
176
                expect(result.value).to.eq(value)
177
                expect(result.status).to.eq(Status.Aborted)
178
              })
179
          })
180
181
          it('ends with AbortFailure status if has been aborted before completion and failed it', function () {
182
            var error = new Error()
183
            var target = stateFactory()
184
            var transition = factory(target)
185
            target.transition.handler = function () {
186
              return new Promise(function () {})
187
            }
188
            target.abort.handler = function () {
189
              throw error
190
            }
191
            transition.run()
192
            var promise = transition.abort()
193
            return promise
194
              .then(function (result) {
195
                expect(result.value).to.eq(error)
196
                expect(result.status).to.eq(Status.AbortFailure)
197
              })
198
          })
199
200
          it('ends with AbortFailure if abort handlers timed out', function () {
201
            var handler = function () {
202
              return new Promise(function () {})
203
            }
204
            var target = stateFactory(handler, 0)
205
            target.transition.timeout = null
206
            var transition = factory(target)
207
            transition.run()
208
            var promise = transition.abort()
209
            return promise
210
              .then(function (result) {
211
                expect(result.value).to.be.instanceOf(TimeoutException)
212
                expect(result.status).to.eq(Status.AbortFailure)
213
              })
214
          })
215
216
          it('prevents transition from completing', function () {
217
            var abortValue = {result: 'aborted'}
218
            var completionValue = {result: 'completed'}
219
            var abortBarrier = new Future()
220
            var completionBarrier = new Future()
221
            var completionHandler = function () {
222
              return completionBarrier
223
            }
224
            var abortHandler = function () {
225
              return abortBarrier
226
            }
227
            var target = stateFactory()
228
            target.transition.handler = completionHandler
229
            target.abort.handler = abortHandler
230
            var transition = factory(target)
231
            var promise = transition.run()
232
            expect(transition.getStatus()).to.eq(Status.Executing)
233
            transition.abort()
234
            expect(transition.getStatus()).to.eq(Status.Aborting)
235
            return completionBarrier
236
              .resolve(completionValue)
237
              .then(function () {
238
                abortBarrier.resolve(abortValue)
239
              })
240
              .then(function () {
241
                return promise
242
              })
243
              .then(function (result) {
244
                expect(result.status).to.eq(Status.Aborted)
245
              })
246
          })
247
        })
248
249
        describe('#toDetails()', function () {
250
          it('provides simplified details', function () {
251
            var target = stateFactory()
252
            var hints = {x: 12}
253
            var options = {
254
              executor: executor,
255
              target: target,
256
              origin: null,
257
              hints: hints
258
            }
259
            var transition = new Transition(options)
260
            var expectation = {
261
              origin: null,
262
              target: target.id,
263
              hints: hints
264
            }
265
            expect(transition.toDetails()).to.deep.eq(expectation)
266
          })
267
        })
268
269
        describe('#launchedAt', function () {
270
          it('returns null for non-launched transition', function () {
271
            var transition = factory()
272
            expect(transition.getLaunchedAt()).to.be.null
0 ignored issues
show
introduced by
The result of the property access to expect(transition.getLaunchedAt()).to.be.null is not used.
Loading history...
273
          })
274
275
          it('returns date for launched transition', function () {
276
            var transition = factory()
277
            var lowerBound = new Date()
278
            return transition
279
              .run(null, {})
280
              .then(function () {
281
                expect(transition.getLaunchedAt()).to.be.at.least(lowerBound)
282
                expect(transition.getLaunchedAt()).to.be.at.most(new Date())
283
              })
284
          })
285
        })
286
      })
287
    })
288
  })
289
})
290