UNPKG

tribune

Version:

Holy advocate to the Roman Senate, your Tribune will ensure the Consuls have your interests in mind.

472 lines (390 loc) 14.4 kB
/* eslint-disable max-len */ /* global describe it */ const nock = require('nock'); const assert = require('assert'); const Tribune = require('../lib/tribune'); const Service = require('../lib/service'); describe('new Tribune(opts) -> this', () => { it('throws if opts.agentUrl is not given', () => { assert.throws( () => new Tribune({}), /agentUrl/ ); }); describe('#get isRegistered -> isRegistered', () => { it('evaluates to true if _serviceId and _preparedQueryId is truthy', () => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceId = '__SERVICE_ID__'; tribune._preparedQueryId = '__PREPARED_QUERY_ID__'; assert.ok(tribune.isRegistered); }); it('evaluates to false if _serviceId or _preparedQueryId is falsy', () => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceId = ''; tribune._preparedQueryId = ''; assert.ok(!tribune.isRegistered); }); }); describe('#register(url, opts, cb(err))', () => { it('calls back with error if serviceName is not given', (cb) => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.register(undefined, { url : 'http://service.url', healthCheckUrl: 'http://service.url/status', routes : ['foo/bar'] }, (err) => { assert.ok(err.message.match(/serviceName/)); cb(null); }); }); it('calls back with error if opts.url is not given', (cb) => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.register('serviceName', { healthCheckUrl: 'http://service.url/status', routes : ['foo/bar'] }, (err) => { assert.ok(err.message.match(/url/)); cb(null); }); }); it('calls back with error if opts.healthCheckUrl is not given', (cb) => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.register('serviceName', { url : 'http://service.url', routes: ['foo/bar'] }, (err) => { assert.ok(err.message.match(/healthCheckUrl/)); cb(null); }); }); it('removes ghosts and dupes of itself from consul then registers itself', (cb) => { nock('http://agent.url') .get('/v1/agent/services') .reply(200, { __SERVICE_ID__: { ID: '__SERVICE_ID__', Service: 'serviceName', Tags: ['%24protocol%3Ahttp%5C%3A', '%24routes%3A%2Ffoo%2Fbar%2C%2Fbaz'], Address: 'service.url', Port: 8000, EnableTagOverride: false, CreateIndex: 0, ModifyIndex: 0 } }) .get('/v1/agent/service/deregister/__SERVICE_ID__') .reply(200) .get('/v1/agent/checks') .reply(200, { 'service:__SERVICE_ID__': { Node: 'computer.local', CheckID: 'service:__SERVICE_ID__', Name: 'Service \'serviceName\' check', Status: 'critical', Notes: '', Output: 'HTTP GET http://localhost:8000/status: 204 No Content Output: ', ServiceID: '__SERVICE_ID__', ServiceName: 'serviceName', CreateIndex: 0, ModifyIndex: 0 } }) .get('/v1/agent/service/deregister/__SERVICE_ID__') .reply(200) .put('/v1/agent/service/register', { ID: '__SERVICE_ID__', Name: 'serviceName', Tags: ['%24protocol%3Ahttp%5C%3A', '%24routes%3A%2Ffoo%2Fbar%2C%2Fbaz'], Address: 'service.url', Port: 8000, Check: { HTTP: 'http://service.url:8000/status', Interval: '1000ms' } }) .reply(200) .get('/v1/query') .reply(200, []) .post('/v1/query', { Name : 'serviceName', Service: { Service: 'serviceName' } }) .reply(200, { ID: '__PREPARED_QUERY_ID__' }); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._generateServiceId = function() { this._serviceId = '__SERVICE_ID__'; }; tribune.register('serviceName', { url : 'http://service.url:8000', healthCheckUrl: 'http://service.url:8000/status', routes : ['/foo/bar', '/baz'], interval : 1000 }, (err) => { assert.ifError(err); nock.cleanAll(); cb(null); }); }); it('calls back with an error if service registration responds with a non 200 status code', (cb) => { nock('http://agent.url') .get('/v1/agent/services') .reply(200, {}) .get('/v1/agent/checks') .reply(200, {}) .put('/v1/agent/service/register') .reply(400) .get('/v1/query') .reply(200, []) .post('/v1/query', {}) .reply(200, {}); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.register('serviceName', { url : 'http://service.url:8000', healthCheckUrl: 'http://service.url:8000/status', routes : ['/foo/bar', '/baz'], interval : 1000 }, (err) => { assert.ok(err.message.match(/400/)); nock.cleanAll(); cb(null); }); }); it('calls back with an error if prepared query registration responds with a non 200 status code', (cb) => { nock('http://agent.url') .get('/v1/agent/services') .reply(200, {}) .get('/v1/agent/checks') .reply(200, {}) .put('/v1/agent/service/register') .reply(200, {}) .get('/v1/query') .reply(200, []) .post('/v1/query', {}) .reply(400, {}); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.register('serviceName', { url : 'http://service.url:8000', healthCheckUrl: 'http://service.url:8000/status', routes : ['/foo/bar', '/baz'], interval : 1000 }, (err) => { assert.ok(err.message.match(/400/)); nock.cleanAll(); cb(null); }); }); it('sets a _serviceId and _preparedQueryId during register', (cb) => { nock('http://agent.url') .get('/v1/agent/services') .reply(200, {}) .get('/v1/agent/checks') .reply(200, {}) .put('/v1/agent/service/register') .reply(200) .get('/v1/query') .reply(200, []) .post('/v1/query', { Name : 'serviceName', Service: { Service: 'serviceName' } }) .reply(200, { ID: '__PREPARED_QUERY_ID__' }); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.register('serviceName', { url : 'http://service.url:8000', healthCheckUrl: 'http://service.url:8000/status', routes : ['/foo/bar', '/baz'], interval : 1000 }, (err) => { assert.ifError(err); assert.ok(tribune._serviceId.match(/serviceName-[\d]{12}/)); assert.equal(tribune._preparedQueryId, '__PREPARED_QUERY_ID__'); nock.cleanAll(); cb(null); }); }); }); describe('#deregister(cb(err))', () => { const executePreparedQueryResponse = { Service : 'serviceName', DNS : { TTL: '' }, Datacenter : 'dc1', Failovers : 0, Index : 0, KnownLeader: true, LastContact: 0, Nodes: [{ Service: { ID : '__SERVICE_ID__', Address : 'service.url', CreateIndex : 0, EnableTagOverride: false, ModifyIndex : 0, Port : 8000, Service : 'serviceName', Tags : ['%24protocol%3Ahttp%5C%3A', '%24routes%3A%2Ffoo%2Fbar%2C%2Fbaz'] } }] }; it('returns an error if service register was not previously called', (cb) => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._preparedQueryId = true; tribune._serviceId = undefined; tribune.deregister((err) => { assert.ok(err.message.match(/Cannot deregister\. Register not called\./)); cb(null); }); }); it('returns an error if prepared query register was not previously called', (cb) => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceId = true; tribune._preparedQueryId = undefined; tribune.deregister((err) => { assert.ok(err.message.match(/Cannot deregister\. Register not called\./)); cb(null); }); }); it('makes a request to the consul agent to deregister as a service', (cb) => { nock('http://agent.url') .get('/v1/agent/service/deregister/__SERVICE_ID__') .reply(200) .get('/v1/query/serviceName/execute') .reply(200, executePreparedQueryResponse); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceName = 'serviceName'; tribune._serviceId = '__SERVICE_ID__'; tribune._preparedQueryId = '__PREPARED_QUERY_ID__'; tribune.deregister((err) => { assert.ifError(err); nock.cleanAll(); cb(null); }); }); it('calls back with an error if consul agent deregister responds with a non 200 status code', (cb) => { nock('http://agent.url') .get('/v1/agent/service/deregister/__SERVICE_ID__') .reply(400); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceId = '__SERVICE_ID__'; tribune._preparedQueryId = '__PREPARED_QUERY_ID__'; tribune.deregister((err) => { assert.ok(err.message.match(/400/)); nock.cleanAll(); cb(null); }); }); it('calls back with an error if consul prepared query deregister responds with a non 200 || 404 status code', (cb) => { nock('http://agent.url') .get('/v1/agent/service/deregister/__SERVICE_ID__') .reply(200) .get('/v1/query/serviceName/execute') .reply(400, executePreparedQueryResponse); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceName = 'serviceName'; tribune._serviceId = '__SERVICE_ID__'; tribune._preparedQueryId = '__PREPARED_QUERY_ID__'; tribune.deregister((err) => { assert.ok(err.message.match(/400/)); nock.cleanAll(); cb(null); }); }); it('sets _serviceId and _preparedQueryId to an empty string during deregister', (cb) => { nock('http://agent.url') .get('/v1/agent/service/deregister/__SERVICE_ID__') .reply(200) .get('/v1/query/serviceName/execute') .reply(200, { Service : 'serviceName', DNS : { TTL: '' }, Datacenter : 'dc1', Failovers : 0, Index : 0, KnownLeader: true, LastContact: 0, Nodes: [] }) .delete('/v1/query/__PREPARED_QUERY_ID__') .reply(200); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune._serviceName = 'serviceName'; tribune._serviceId = '__SERVICE_ID__'; tribune._preparedQueryId = '__PREPARED_QUERY_ID__'; tribune.deregister((err) => { assert.ifError(err); assert.equal(tribune._serviceId, ''); assert.equal(tribune._preparedQueryId, ''); nock.cleanAll(); cb(null); }); }); }); describe('#service(serviceName) -> Service', () => { it('returns a service instance with the service name set', () => { const tribune = new Tribune({ agentUrl: 'http://agent.url' }); const service = tribune.service('serviceName'); assert.ok(service instanceof Service); assert.equal(service._tribune, tribune); assert.equal(service._name, 'serviceName'); }); }); describe('#status(opts, cb(err))', () => { it('returns a status code of 503 if consult leader check returns an empty string', (cb) => { nock('http://agent.url') .get('/v1/status/leader') .reply(200, '') .get('/v1/status/peers') .reply(200, []); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.status({}, (err, res) => { if (err) { return cb(err); } assert.equal(res.statusCode, 503); nock.cleanAll(); cb(null); }); }); it('returns a 503 status code if consult peers check returns an empty array', (cb) => { nock('http://agent.url') .get('/v1/status/leader') .reply(200, 'http://localhost:8100') .get('/v1/status/peers') .reply(200, []); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.status({}, (err, res) => { if (err) { return cb(err); } assert.equal(res.statusCode, 503); nock.cleanAll(); cb(null); }); }); it('returns a 503 status code if consult peers check returns less peers than defined in options', (cb) => { nock('http://agent.url') .get('/v1/status/leader') .reply(200, 'http://localhost:8100') .get('/v1/status/peers') .reply(200, ['peer1']); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.status({ minPeers: 2 }, (err, res) => { if (err) { return cb(err); } assert.equal(res.statusCode, 503); nock.cleanAll(); cb(null); }); }); it('returns a 200 status code if consult peers and leader checks are returns a success response', (cb) => { nock('http://agent.url') .get('/v1/status/leader') .reply(200, 'http://localhost:8100') .get('/v1/status/peers') .reply(200, ['peer1', 'peer1']); const tribune = new Tribune({ agentUrl: 'http://agent.url' }); tribune.status({ minPeers: 2 }, (err, res) => { if (err) { return cb(err); } assert.equal(res.statusCode, 200); nock.cleanAll(); cb(null); }); }); }); });