Completed
Push — dev ( d30456...4fd03f )
by Fike
33s
created

test/suites/integration/http/basic.spec.js   F

Complexity

Total Complexity 79
Complexity/F 1.39

Size

Lines of Code 368
Function Count 57

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
nc 2
dl 0
loc 368
rs 3.745
c 1
b 0
f 0
wmc 79
mnd 1
bc 58
fnc 57
bpm 1.0175
cpm 1.3859
noi 2

How to fix   Complexity   

Complexity

Complex classes like test/suites/integration/http/basic.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
/* global transportFactory, Net */
4
5
var Sinon = require('sinon')
6
var Chai = require('chai')
7
var expect = Chai.expect
8
var SDK = require('../../../../lib')
9
var Clients = SDK.Http
10
var Method = Clients.Method
11
var Client = Clients.Basic
12
var Loggers = SDK.Logger
13
14
Chai.use(require('chai-as-promised'))
15
Chai.use(require('chai-string'))
16
17
function branchStopper () {
18
  Chai.assert(false, 'This branch should have never been executed')
19
}
20
21
describe('Integration', function () {
22
  describe('/http', function () {
23
    describe('/basic.js', function () {
24
      describe('.Client', function () {
25
        var clientFactory = function (transport, options) {
26
          options = options || {}
27
          options.logger = {level: Loggers.Level.Trace}
28
          return new Client(options, transport)
29
        }
30
31
        var transport
32
        var client
33
        var BASE_URL = 'http://localhost'
34
35
        beforeEach(function () {
36
          transport = transportFactory({code: 200, headers: [], text: '{}'})
37
          client = clientFactory(transport, {url: BASE_URL})
38
        })
39
40
        describe('< new', function () {
41
          it('uses Net.httpRequestAsync transport if none passed', function () {
42
            Net.httpRequestAsync = Sinon.spy(function () {
43
              return Promise.resolve({code: 200})
44
            })
45
            var client = clientFactory()
46
            var path = '/path'
47
            return client
48
              .request(Method.Get, path)
49
              .then(function () {
50
                expect(Net.httpRequestAsync.callCount).to.eq(1)
51
                expect(Net.httpRequestAsync.getCall(0).args[0]).to.endWith(path)
52
              })
53
          })
54
55
          it('tolerates invalid options input', function () {
56
            var client = new Client(null, transport)
57
            var path = '/path'
58
            return client
59
              .request(Method.Get, path)
60
              .then(function () {
61
                expect(transport.callCount).to.eq(1)
62
                expect(transport.getCall(0).args[0]).to.endWith(path)
63
              })
64
          })
65
        })
66
67
        describe('#request', function () {
68
          it('concatenates base url with provided url', function () {
69
            var path = '/search'
70
            return client
71
              .request(Method.Get, path)
72
              .then(function () {
73
                expect(transport.getCall(0).args[0]).to.eq(BASE_URL + path)
74
              })
75
          })
76
77
          it('refuses falsey method', function () {
78
            return client
79
              .request(null, '/')
80
              .then(branchStopper, function (error) {
81
                var ExceptionClass = Clients.InvalidConfigurationException
82
                expect(error).to.be.instanceOf(ExceptionClass)
83
              })
84
          })
85
86
          it('ignores falsey path', function () {
87
            return client
88
              .request(Method.Get)
89
              .then(function () {
90
                expect(transport.getCall(0).args[0]).to.eq(BASE_URL)
91
              })
92
          })
93
94
          it('encodes and passes provided headers', function () {
95
            var headers = {
96
              Cookie: ['a=b', 'c=d'],
97
              Authorization: 'Basic amM6MDQ1MQ=='
98
            }
99
            var expectation = [
100
              'Cookie: a=b',
101
              'Cookie: c=d',
102
              'Authorization: Basic amM6MDQ1MQ=='
103
            ]
104
            return client
105
              .request(Method.Get, '/', null, null, headers)
106
              .then(function () {
107
                var options = transport.getCall(0).args[1]
108
                expect(options.headers).to.deep.eq(expectation)
109
              })
110
          })
111
112
          it('encodes and passes provided query', function () {
113
            var query = {
114
              category: 'books',
115
              filter: ['title=Once upon*', 'author=*John*']
116
            }
117
            var expectation = '?category=books&filter=title%3dOnce%20upon*&filter=author%3d*John*'
118
            return client
119
              .request(Method.Get, '/', query)
120
              .then(function () {
121
                var url = transport.getCall(0).args[0]
122
                expect(url.toLowerCase()).to.endWith(expectation.toLowerCase())
123
              })
124
          })
125
126
          it('passes through payload', function () {
127
            var payload = '{}'
128
            return client
129
              .request(Method.Post, '/', null, payload)
130
              .then(function () {
131
                var options = transport.getCall(0).args[1]
132
                expect(options.postData).to.deep.eq(payload)
133
              })
134
          })
135
136
          it('decodes and returns response', function () {
137
            var headers = [
138
              {key: 'Server', value: 'nginx/1.9.9'},
139
              {key: 'Set-Cookie', value: 'lang=en'}
140
            ]
141
            var dummy = {code: 200, headers: headers, text: '{}'}
142
            var transport = transportFactory(dummy)
143
            var expectation = {
144
              Server: ['nginx/1.9.9'],
145
              'Set-Cookie': ['lang=en']
146
            }
147
            return clientFactory(transport)
148
              .request(Method.Get, '/test')
149
              .then(function (response) {
150
                expect(response.code).to.eq(dummy.code)
151
                expect(response.headers).to.deep.eq(expectation)
152
                expect(response.payload).to.eq(dummy.text)
153
              })
154
          })
155
156
          it('ignores method override header on GET/POST request', function () {
157
            var options = {methodOverrideHeader: 'X-HMO'}
158
            var client = clientFactory(transport, options)
159
            return client
160
              .request(Method.Get, '/')
161
              .then(function () {
162
                var options = transport.getCall(0).args[1]
163
                expect(options.headers).to.be.empty
164
              })
165
          })
166
167
          it('prevents non-GET/POST request without method override header', function () {
168
            var transport = transportFactory()
169
            var client = clientFactory(transport)
170
171
            return client
172
              .request(Method.Patch, '/')
173
              .then(branchStopper, function (error) {
174
                expect(error).to.be.instanceOf(Clients.InvalidConfigurationException)
175
                expect(transport.callCount).to.eq(0)
176
              })
177
          })
178
179
          it('adds method override header for non-GET/POST request', function () {
180
            var transport = transportFactory({code: 200})
181
            var options = {methodOverrideHeader: 'X-HMO'}
182
            var client = clientFactory(transport, options)
183
            var method = Method.Patch
184
185
            return client
186
              .request(method, '/')
187
              .then(function () {
188
                var headers = transport.getCall(0).args[1].headers
189
                expect(headers).to.deep.eq(['X-HMO: ' + method])
190
              })
191
          })
192
193
          it('merges and overwrites default headers', function () {
194
            var headers = {overwritten: ['a', 'b'], kept: ['c', 'd']}
195
            var options = {headers: headers}
196
            var overrides = {introduced: ['e', 'f'], overwritten: ['g']}
197
            var expectation = [
198
              'introduced: e',
199
              'introduced: f',
200
              'overwritten: g',
201
              'kept: c',
202
              'kept: d'
203
            ]
204
            var client = clientFactory(transport, options)
205
            return client
206
              .request(Method.Get, '/', [], null, overrides)
207
              .then(function () {
208
                var headers = transport.getCall(0).args[1].headers
209
                expect(headers.sort()).to.deep.eq(expectation.sort())
210
              })
211
          })
212
213
          var errorTypes = {
214
            NetworkError: {
215
              code: -1,
216
              exception: Clients.NetworkException
217
            },
218
            ServerError: {
219
              code: 500,
220
              exception: Clients.ServerErrorException
221
            },
222
            ClientError: {
223
              code: 400,
224
              exception: Clients.ClientErrorException
225
            },
226
            NotFound: {
227
              code: 404,
228
              exception: Clients.NotFoundException
229
            }
230
          }
231
232
          Object.keys(errorTypes).forEach(function (errorType) {
233
            var error = errorTypes[errorType]
234
            it('retries on ' + errorType + ' error if told so', function () {
235
              var transport = transportFactory({code: error.code}, {code: 200})
236
              var opts = {}
237
              var client
238
239
              opts['retryOn' + errorType] = true
240
              client = clientFactory(transport, opts)
241
242
              return client
243
                .get('/test')
244
                .then(function (response) {
245
                  expect(response.code).to.eq(200)
246
                  expect(transport.callCount).to.eq(2)
247
                })
248
            })
249
250
            it('does not retry on ' + errorType + ' error unless told so', function () {
251
              var transport = transportFactory({code: error.code}, {code: 200})
252
              var opts = {}
253
              var client
254
255
              opts['retryOn' + errorType] = false
256
              opts['throwOn' + errorType] = true
257
              client = clientFactory(transport, opts)
258
259
              return client
260
                .get('/test')
261
                .then(branchStopper, function (e) {
262
                  expect(e).to.be.instanceOf(error.exception)
263
                  expect(transport.callCount).to.eq(1)
264
                })
265
            })
266
267
            if (errorType === 'NetworkError') {
268
              return
269
            }
270
271
            it('does not throw on ' + errorType + ' error if told so', function () {
272
              var transport = transportFactory({code: error.code})
273
              var opts = {}
274
              var client
275
276
              opts['retryOn' + errorType] = false
277
              opts['throwOn' + errorType] = false
278
              client = clientFactory(transport, opts)
279
280
              return client
281
                .get('/test')
282
                .then(function (response) {
283
                  expect(response.code).eq(error.code)
284
                  expect(transport.callCount).to.eq(1)
285
                })
286
            })
287
          })
288
289
          it('retries not more times than specified in settings', function () {
290
            var transport = transportFactory({code: 500})
291
            var options = {
292
              retries: 4,
293
              throwOnServerError: false,
294
              retryOnServerError: true
295
            }
296
            var client = clientFactory(transport, options)
297
298
            return client
299
              .request(Method.Get, '/')
300
              .then(function (response) {
301
                expect(response.code).to.eq(500)
302
                expect(transport.callCount).to.eq(5)
303
              })
304
          })
305
306
          it('handles response with unknown code', function () {
307
            var transport = transportFactory({code: -500})
308
            var client = clientFactory(transport, {retryOnNetworkError: false})
309
310
            return client
311
              .request(Method.Get, '/')
312
              .then(branchStopper, function (error) {
313
                expect(error).to.be.instanceOf(Clients.NetworkException)
314
                expect(error.code).to.eq(-500)
315
              })
316
          })
317
318
          it('handles response with no code', function () {
319
            var transport = transportFactory({})
320
            var client = clientFactory(transport, {retryOnNetworkError: false})
321
322
            return client
323
              .request(Method.Get, '/')
324
              .then(branchStopper, function (error) {
325
                expect(error).to.be.instanceOf(Clients.NetworkException)
326
                expect(error.code).to.eq(-1)
327
              })
328
          })
329
        })
330
331
        var methods = ['get', 'head']
332
        methods.forEach(function (method) {
333
          describe('#' + method, function () {
334
            it('provides ' + method + ' request method', function () {
335
              var options = {methodOverrideHeader: 'X-HMO'}
336
              var client = clientFactory(transport, options)
337
              var query = {alpha: 'beta'}
338
              var headers = {'X-Gamma': 'delta'}
339
              return client[method]('/', query, headers)
340
                .then(function () {
341
                  var call = transport.getCall(0)
342
                  expect(call.args[1].headers).to.contain('X-Gamma: delta')
343
                  expect(call.args[1].postData).to.be.not.ok
344
                  expect(call.args[0]).to.eq('/?alpha=beta')
345
                })
346
            })
347
          })
348
        })
349
350
        methods = ['post', 'put', 'patch', 'delete']
351
        methods.forEach(function (method) {
352
          describe('#' + method, function () {
353
            it('provides ' + method + ' request method', function () {
354
              var options = {methodOverrideHeader: 'X-HMO'}
355
              var client = clientFactory(transport, options)
356
              var payload = '{"alpha": "beta"}'
357
              var headers = {'X-Gamma': 'delta'}
358
              var query = {alpha: 'beta'}
359
              return client[method]('/', payload, headers, query)
360
                .then(function () {
361
                  var call = transport.getCall(0)
362
                  expect(call.args[1].headers).to.contain('X-Gamma: delta')
363
                  expect(call.args[1].postData).to.eq(payload)
364
                  expect(call.args[0]).to.eq('/?alpha=beta')
365
                })
366
            })
367
          })
368
        })
369
      })
370
    })
371
  })
372
})
373