UNPKG

14 kBJavaScriptView Raw
1describe('Copacetic', () => {
2 const expect = require('chai').expect
3 const sinon = require('sinon')
4 const nock = require('nock')
5 const noop = require('node-noop').noop
6
7 let Copacetic
8 let dependencyLevel
9
10 before(() => {
11 const Dependency = require('../lib/dependency')
12 const Injector = require('../lib/util/injector')
13 const CodependencyMock = require('./mocks/codependency')
14
15 dependencyLevel = require('../lib/dependency-level')
16 Copacetic = require('../lib/copacetic')(
17 Dependency(Injector(CodependencyMock({
18 'node-fetch': require('node-fetch')
19 })))
20 )
21 })
22
23 it('should export a function', () => {
24 expect(Copacetic).to.be.a('function')
25 })
26
27 it('should return an instance of Copacetic', () => {
28 expect(Copacetic()).to.be.a('object')
29 })
30
31 it('should be in event emitter mode by default', () => {
32 expect(Copacetic().eventEmitterMode).to.equal(true)
33 })
34
35 describe('isHealthy', () => {
36 it('should return false if a hard dependency is unhealthy', (done) => {
37 const copacetic = Copacetic()
38 copacetic.registerDependency({
39 name: 'My-Dependency',
40 url: 'http://example.com',
41 level: dependencyLevel.HARD
42 })
43
44 nock('http://example.com')
45 .get('/')
46 .reply(400)
47
48 return copacetic
49 .check({ name: 'My-Dependency' })
50 .on('unhealthy', (healthInfo) => {
51 expect(copacetic.isHealthy).to.equal(false)
52 done()
53 })
54 })
55
56 it('should return true if a soft dependency is unhealthy', (done) => {
57 const copacetic = Copacetic()
58 copacetic.registerDependency({
59 name: 'My-Dependency',
60 url: 'http://example.com',
61 level: dependencyLevel.SOFT
62 })
63
64 nock('http://example.com')
65 .get('/')
66 .reply(400)
67
68 return copacetic
69 .check({ name: 'My-Dependency' })
70 .on('unhealthy', () => {
71 expect(copacetic.isHealthy).to.equal(true)
72 done()
73 })
74 })
75
76 it('should return true if all dependencies are healthy', (done) => {
77 const copacetic = Copacetic()
78 copacetic.registerDependency({
79 name: 'My-Dependency',
80 url: 'http://example.com',
81 level: dependencyLevel.SOFT
82 })
83
84 nock('http://example.com')
85 .get('/')
86 .reply(200)
87
88 return copacetic
89 .check({ name: 'My-Dependency' })
90 .on('healthy', () => {
91 expect(copacetic.isHealthy).to.equal(true)
92 done()
93 })
94 })
95 })
96
97 describe('getCopaceticInfo', () => {
98 it('should return the health info for all registered dependencies', () => {
99 const copacetic = Copacetic()
100 copacetic.registerDependency({
101 name: 'My-Dependency',
102 url: 'http://example.com'
103 })
104
105 expect(copacetic.healthInfo).to.deep.equal(
106 [
107 {
108 name: 'My-Dependency',
109 healthy: true,
110 level: 'SOFT',
111 lastChecked: undefined
112 }
113 ]
114 )
115 })
116 })
117
118 describe('healthReport', () => {
119 it('should return the full health report', () => {
120 const copacetic = Copacetic('my-service')
121 copacetic.registerDependency({
122 name: 'My-Dependency',
123 url: 'http://example.com'
124 })
125
126 expect(copacetic.healthReport).to.deep.equal(
127 {
128 name: 'my-service',
129 isHealthy: true,
130 dependencies: [
131 {
132 name: 'My-Dependency',
133 healthy: true,
134 level: 'SOFT',
135 lastChecked: undefined
136 }
137 ]
138 }
139 )
140 })
141 })
142
143 describe('getDependency()', () => {
144 let copacetic
145
146 it('should look up a dependency given a dependency name', () => {
147 copacetic = Copacetic()
148 copacetic.registerDependency({
149 name: 'My-Dependency',
150 url: 'http://example.com'
151 })
152
153 expect(copacetic.getDependency('My-Dependency').name).to.equal('My-Dependency')
154 })
155
156 it('should look up a dependency given a dependency object', () => {
157 expect(
158 copacetic.getDependency(copacetic.getDependency('My-Dependency')).name
159 ).to.equal('My-Dependency')
160 })
161 })
162
163 describe('isDependencyRegistered', () => {
164 let copacetic
165
166 it('should return true if a dependency exists', () => {
167 copacetic = Copacetic()
168 copacetic.registerDependency({
169 name: 'My-Dependency',
170 url: 'http://example.com'
171 })
172
173 expect(copacetic.isDependencyRegistered('My-Dependency')).to.equal(true)
174 })
175
176 it('should return false if a dependency does not exist', () => {
177 expect(copacetic.isDependencyRegistered('Test-Dependency')).to.equal(false)
178 })
179 })
180
181 describe('registerDependency()', () => {
182 let copacetic
183
184 it('should register a dependency if it does not exist', () => {
185 copacetic = Copacetic()
186 copacetic.registerDependency({
187 name: 'My-Dependency',
188 url: 'http://example.com'
189 })
190
191 expect(copacetic.getDependency('My-Dependency').name).to.equal('My-Dependency')
192 })
193
194 it('should throw an error if a dependency exists', () => {
195 expect(() => copacetic.registerDependency({
196 name: 'My-Dependency',
197 url: 'http://example.com'
198 })).to.throw(Error)
199 })
200 })
201
202 describe('deregister()', () => {
203 let copacetic
204
205 it('should deregister a dependency if it does exist', () => {
206 copacetic = Copacetic()
207
208 copacetic.registerDependency({
209 name: 'My-Dependency',
210 url: 'http://example.com'
211 })
212
213 expect(copacetic.dependencyNames.indexOf('My-Dependency')).not.to.equal(-1)
214 expect(() => copacetic.deregisterDependency('My-Dependency')).not.to.throw(Error)
215 expect(copacetic.dependencyNames.indexOf('My-Dependency')).to.equal(-1)
216 })
217
218 it('should throw an error if the dependency does not exist', () => {
219 expect(() => copacetic.deregisterDependency('My-Dependencyyy')).to.throw(Error)
220 })
221 })
222
223 describe('checkAll()', () => {
224 let copacetic
225
226 beforeEach(() => {
227 copacetic = Copacetic()
228 copacetic.registerDependency({
229 name: 'My-Dependency',
230 url: 'http://example.com'
231 })
232 copacetic.registerDependency({
233 name: 'My-Other-Dependency',
234 url: 'http://other-example.com'
235 })
236
237 nock('http://example.com')
238 .get('/')
239 .reply(200)
240 nock('http://other-example.com')
241 .get('/')
242 .reply(200)
243 })
244
245 it('should check the health of all registered dependencies', (done) => {
246 copacetic
247 .checkAll()
248 .on('health', (dependencies) => {
249 expect(dependencies).to.deep.equal([
250 {
251 name: 'My-Dependency',
252 healthy: true,
253 level: 'SOFT',
254 lastChecked: dependencies[0].lastChecked
255 },
256 {
257 name: 'My-Other-Dependency',
258 healthy: true,
259 level: 'SOFT',
260 lastChecked: dependencies[1].lastChecked
261 }
262 ])
263 done()
264 })
265 })
266
267 it('should return a promise when not in eventEmitterMode', () => {
268 copacetic.eventEmitterMode = false
269 return copacetic
270 .checkAll()
271 .then((dependencies) => {
272 expect(dependencies).to.deep.equal([
273 {
274 name: 'My-Dependency',
275 healthy: true,
276 level: 'SOFT',
277 lastChecked: dependencies[0].lastChecked
278 },
279 {
280 name: 'My-Other-Dependency',
281 healthy: true,
282 level: 'SOFT',
283 lastChecked: dependencies[1].lastChecked
284 }
285 ])
286 })
287 })
288 })
289
290 describe('check()', () => {
291 describe('when checking one dependency', () => {
292 let copacetic
293
294 it('should emit an "healthy" event when checking a single healthy dependency', (done) => {
295 copacetic = Copacetic()
296 copacetic.registerDependency({
297 name: 'My-Dependency',
298 url: 'http://example.com'
299 })
300
301 nock('http://example.com')
302 .get('/')
303 .reply(200)
304
305 copacetic
306 .check({ name: 'My-Dependency' })
307 .on('healthy', (dependency) => {
308 expect(dependency).to.deep.equal({
309 name: 'My-Dependency',
310 healthy: true,
311 level: 'SOFT',
312 lastChecked: dependency.lastChecked
313 })
314 done()
315 })
316 })
317 it('should emit an "unhealthy" event when checking a single unhealthy dependency', (done) => {
318 nock('http://example.com')
319 .get('/')
320 .reply(400)
321
322 copacetic
323 .check({ name: 'My-Dependency' })
324 .on('unhealthy', (dependency) => {
325 expect(dependency).to.deep.equal({
326 name: 'My-Dependency',
327 healthy: false,
328 level: 'SOFT',
329 lastChecked: dependency.lastChecked
330 })
331 done()
332 })
333 })
334
335 it('should return a promise when not in eventEmitterMode', () => {
336 nock('http://example.com')
337 .get('/')
338 .reply(200)
339
340 copacetic.eventEmitterMode = false
341
342 return copacetic
343 .check({ name: 'My-Dependency' })
344 .then((dependency) => {
345 expect(dependency).to.deep.equal({
346 name: 'My-Dependency',
347 healthy: true,
348 level: 'SOFT',
349 lastChecked: dependency.lastChecked
350 })
351 })
352 })
353 })
354
355 describe('when checking multiple dependencies', () => {
356 it('should emit a "health" event when checking dependencies', (done) => {
357 let copacetic = Copacetic()
358 copacetic.registerDependency({
359 name: 'My-Dependency',
360 url: 'http://example.com'
361 })
362 .registerDependency({
363 name: 'My-Other-Dependency',
364 url: 'http://dankdependency.com'
365 })
366
367 nock('http://example.com')
368 .get('/')
369 .reply(200)
370 nock('http://dankdependency.com')
371 .get('/')
372 .reply(400)
373
374 copacetic
375 .check({
376 dependencies: [
377 { name: 'My-Dependency' },
378 { name: 'My-Other-Dependency' }
379 ],
380 parallel: false
381 })
382 .on('health', (healthSummary) => {
383 expect(healthSummary).to.deep.equal([
384 {
385 name: 'My-Dependency',
386 healthy: true,
387 level: 'SOFT',
388 lastChecked: healthSummary[0].lastChecked
389 },
390 {
391 name: 'My-Other-Dependency',
392 healthy: false,
393 level: 'SOFT',
394 lastChecked: healthSummary[1].lastChecked
395 }
396 ])
397 done()
398 })
399 })
400 })
401 })
402
403 describe('waitFor', () => {
404 it('should call check with unlimited retries', () => {
405 const copacetic = Copacetic()
406 copacetic.registerDependency({
407 name: 'My-Dependency',
408 url: 'http://example.com'
409 })
410
411 const checkSpy = sinon.stub(copacetic, 'check').callsFake(noop)
412
413 copacetic.waitFor({ name: 'My-Dependency' })
414 copacetic.waitFor({
415 dependencies: [ { name: 'My-Dependency' } ]
416 })
417
418 expect(checkSpy.getCall(0).args[0]).to.deep.equal({
419 name: 'My-Dependency', retries: 0
420 })
421
422 expect(checkSpy.getCall(1).args[0]).to.deep.equal({
423 dependencies: [ { name: 'My-Dependency', retries: 0, maxDelay: 0 } ]
424 })
425 })
426 })
427
428 describe('poll()', () => {
429 it('should emit a "health" event, describing the status of dependencies', (done) => {
430 const copacetic = Copacetic()
431 copacetic.registerDependency({
432 name: 'My-Dependency',
433 url: 'http://example.com'
434 })
435 copacetic.registerDependency({
436 name: 'My-Other-Dependency',
437 url: 'http://dankdependency.com'
438 })
439
440 nock('http://example.com')
441 .get('/')
442 .reply(200)
443 nock('http://dankdependency.com')
444 .get('/')
445 .reply(400)
446
447 copacetic
448 .poll({
449 dependencies: [
450 { name: 'My-Dependency' },
451 { name: 'My-Other-Dependency' }
452 ]
453 })
454 .on('health', (healthSummary, stop) => {
455 expect(healthSummary).to.deep.equal([
456 {
457 name: 'My-Dependency',
458 healthy: true,
459 level: 'SOFT',
460 lastChecked: healthSummary[0].lastChecked
461 },
462 {
463 name: 'My-Other-Dependency',
464 healthy: false,
465 level: 'SOFT',
466 lastChecked: healthSummary[1].lastChecked
467 }
468 ])
469
470 stop()
471 done()
472 })
473 })
474
475 it('should poll a single dependency', (done) => {
476 const copacetic = Copacetic()
477 copacetic.registerDependency({
478 name: 'My-Dependency',
479 url: 'http://example.com'
480 })
481 nock('http://example.com')
482 .get('/')
483 .reply(200)
484
485 return copacetic
486 .poll({ name: 'My-Dependency' })
487 .on('health', (healthSummary, stop) => {
488 expect(healthSummary).to.deep.equal({
489 name: 'My-Dependency',
490 healthy: true,
491 level: 'SOFT',
492 lastChecked: healthSummary.lastChecked
493 })
494
495 stop()
496 done()
497 })
498 })
499 })
500
501 describe('stop()', () => {
502 it('should stop polling dependencies', () => {
503 const copacetic = Copacetic()
504 copacetic.registerDependency({
505 name: 'My-Dependency',
506 url: 'http://example.com'
507 })
508
509 nock('http://example.com')
510 .get('/')
511 .reply(200)
512
513 copacetic.pollAll()
514 expect(copacetic.isPolling).to.equal(true)
515 copacetic.stop()
516 expect(copacetic.isPolling).to.equal(false)
517 })
518 })
519})