UNPKG

@gameleap/hydra

Version:

Hydra is a NodeJS light-weight library for building distributed computing applications such as microservices

383 lines (356 loc) 10.7 kB
/* eslint no-invalid-this: 0 */ require('./helpers/chai.js'); let hydra; const version = require('../package.json').version; const redis = require('redis-mock'); const redisPort = 6379; const redisUrl = '127.0.0.1'; const SECOND = 1000; /** * @name getConfig * @summary Get a new copy of a config object * @return {undefined} */ function getConfig() { return Object.assign({}, { 'hydra': { 'serviceName': 'test-service', 'serviceDescription': 'Raison d\'etre', 'serviceIP': '127.0.0.1', 'servicePort': 5000, 'serviceType': 'test', 'redis': { 'url': redisUrl, 'port': redisPort, 'db': 0 } }, version }); } /** * Change into specs folder so that config loading can find file using relative path. */ process.chdir('./specs'); /** * @name Index Tests * @summary Hydra Main Test Suite */ describe('Hydra Main', function() { this.timeout(SECOND * 10); beforeEach(() => { hydra = require('../index.js'); redis.removeAllListeners('message'); }); afterEach((done) => { hydra.shutdown().then(() => { let name = require.resolve('../index.js'); delete require.cache[name]; done(); }); }); /** * @description Confirms that hydra can connect to a redis instance */ it('should be able to connect to redis', (done) => { hydra.init(getConfig(), true) .then(() => { done(); }) .catch((_err) => { expect(true); }); }); /** * @description Hydra should fail on init() if called more than once */ it('should fail if init called more than once', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.init(getConfig(), true) .then(() => { expect(true).to.be.false; done(); }) .catch((err) => { expect(err).to.not.be.null; expect(err.message).to.equal('Hydra.init() already invoked'); done(); }); }) .catch((_err) => { expect(true); done(); }); }); /** * @description Hydra should fail to load without a configuration file */ it('should fail without config file', (done) => { hydra.init({}, true) .then(() => { expect(true).to.be.false; done(); }) .catch((err) => { expect(err).to.not.be.null; expect(err.message).to.equal('Config missing serviceName or servicePort'); done(); }); }); /** * @description Hydra should load if serviceName and servicePort is provided */ it('should load if serviceName and servicePort is provided', (done) => { hydra.init({ hydra: { serviceName: 'test-service', servicePort: 3000 } }, true) .then(() => { done(); }) .catch((err) => { expect(err).to.be.null; done(); }); }); /** * @description Hydra should load without a hydra.redis branch in configuration */ it('should load without config hydra.redis branch', (done) => { let config = getConfig(); delete config.hydra.redis; hydra.init(config, true) .then(() => { done(); }) .catch((err) => { expect(err).to.be.null; done(); }); }); /** * @description Hydra should fail if serviceName is missing in config */ it('should fail without serviceName config', (done) => { let config = getConfig(); delete config.hydra.serviceName; hydra.init(config, true) .then(() => { expect(true).to.be.false; done(); }) .catch((err) => { expect(err).to.not.be.null; expect(err.message).to.equal('Config missing serviceName or servicePort'); done(); }); }); /** * @description Confirms that when hydra registers as a service the expected keys can be found in redis */ it('should be able to register as a service', (done) => { hydra.init(getConfig(), true) .then(() => { let r = redis.createClient(); hydra.registerService() .then((_serviceInfo) => { setTimeout(() => { r.keys('*', (err, data) => { expect(err).to.be.null; expect(data.length).to.equal(3); expect(data).to.include('hydra:service:test-service:service'); expect(data).to.include('hydra:service:nodes'); done(); }); r.quit(); }, SECOND); }); }); }); /** * @description expect serviceName, serviceIP, servicePort and instanceID to exists upon service registration */ it('should have a serviceName, serviceIP, servicePort and instanceID', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((serviceInfo) => { expect(serviceInfo).not.null; expect(serviceInfo.serviceName).to.equal('test-service'); expect(serviceInfo.serviceIP).to.equal('127.0.0.1'); expect(serviceInfo.servicePort).to.equal('5000'); done(); }); }); }); /** * @description getServiceName should return name of service */ it('should see that getServiceName returns name of service', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((serviceInfo) => { expect(serviceInfo).not.null; expect(serviceInfo.serviceName).to.equal('test-service'); expect(hydra.getServiceName()).to.equal(serviceInfo.serviceName); done(); }); }); }); /** * @description getServices should return a list of services */ it('should see that getServices returns list of services', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((_serviceInfo) => { hydra.getServices() .then((services) => { expect(services.length).to.be.above(0); expect(services[0]).to.have.property('serviceName'); expect(services[0]).to.have.property('type'); expect(services[0]).to.have.property('registeredOn'); done(); }); }); }); }); /** * @description getServiceNodes should return a list of services */ it('should see that getServiceNodes returns list of service nodes', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((_serviceInfo) => { hydra.getServiceNodes() .then((nodes) => { expect(nodes.length).to.be.above(0); expect(nodes[0]).to.have.property('serviceName'); expect(nodes[0]).to.have.property('instanceID'); expect(nodes[0]).to.have.property('processID'); expect(nodes[0]).to.have.property('ip'); done(); }); }); }); }); /** * @description presence information should update in redis for a running hydra service */ it('should update presence', (done) => { hydra.init(getConfig(), true) .then(() => { let r = redis.createClient(); hydra.registerService() .then((_serviceInfo) => { let instanceID = hydra.getInstanceID(); r.hget('hydra:service:nodes', instanceID, (err, data) => { expect(err).to.be.null; expect(data).to.not.be.null; let entry = JSON.parse(data); setTimeout(() => { r.hget('hydra:service:nodes', instanceID, (err, data) => { expect(err).to.be.null; expect(data).to.not.be.null; let entry2 = JSON.parse(data); expect(entry2.updatedOn).to.not.equal(entry.updatedOn); r.quit(); done(); }); }, SECOND); }); }); }); }); /** * @description ensure keys expire on shutdown */ it('should expire redis keys on shutdown', (done) => { hydra.init(getConfig(), true) .then(() => { let r = redis.createClient(); hydra.registerService() .then((_serviceInfo) => { setTimeout(() => { r.get('hydra:service:test-service:73909f8c96a9d08e876411c0a212a1f4:presence', (err, _data) => { expect(err).to.be.null; done(); r.quit(); }); }, SECOND * 5); }); }); }); /** * @description service should be discoverable */ it('should be able to discover a service', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((_serviceInfo) => { setTimeout(() => { hydra.findService('test-service') .then((data) => { expect(data).not.null; expect(data.serviceName).to.equal('test-service'); expect(data.type).to.equal('test'); done(); }); }, SECOND); }); }); }); /** * @description invalid service should not be discoverable */ it('should return an error if a service doesn\'t exists', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((_serviceInfo) => { setTimeout(() => { hydra.findService('xyxyx-service') .then((_data) => { expect(true).to.be.false; done(); }) .catch((err) => { expect(err).to.not.be.null; expect(err.message).to.equal('Can\'t find xyxyx-service service'); done(); }); }, SECOND); }); }); }); /** * @description get service presence info */ it('should be able to retrieve service presence', (done) => { hydra.init(getConfig(), true) .then(() => { hydra.registerService() .then((_serviceInfo) => { hydra.getServicePresence('test-service') .then((data) => { expect(data).to.not.be.null; expect(data.length).to.be.above(0); expect(data[0]).to.have.property('processID'); expect(data[0].updatedOnTS).to.be.above(1492906823975); done(); }); }); }); }); }); /** * Change back to parent directory to maintain proper state */ process.chdir('..');