UNPKG

fivebeans

Version:

beanstalkd client & worker daemon for node.

454 lines (408 loc) 10.2 kB
/*global describe:true, it:true, before:true, after:true */ var demand = require('must'), fivebeans = require('../index'), fs = require('fs'), semver = require('semver') ; var host = '127.0.0.1'; var port = 11300; var tube = 'testtube'; function readTestImage() { return fs.readFileSync('./test/test.png'); } describe('FiveBeansClient', function() { var producer, consumer, testjobid; var version; before(function() { producer = new fivebeans.client(host); consumer = new fivebeans.client(host, port); }); describe('#FiveBeansClient()', function() { it('creates a client with the passed-in options', function() { producer.host.must.equal(host); producer.port.must.equal(port); }); }); describe('#connect()', function() { it('creates and saves a connection', function(done) { producer.on('connect', function() { producer.stream.must.exist(); done(); }).on('error', function(err) { throw(err); }); producer.connect(); }); }); describe('job producer:', function() { it('#use() connects to a specific tube', function(done) { producer.use(tube, function(err, response) { demand(err).not.exist(); response.must.equal(tube); done(); }); }); it('#list_tube_used() returns the tube used by a producer', function(done) { producer.list_tube_used(function(err, response) { demand(err).not.exist(); response.must.equal(tube); done(); }); }); it('#put() submits a job', function(done) { var data = { type: 'test', payload: 'the explosive energy of the warhead of a missile or of the bomb load of an aircraft' }; producer.put(0, 0, 60, JSON.stringify(data), function(err, jobid) { demand(err).not.exist(); jobid.must.exist(); done(); }); }); after(function(done) { producer.stats(function(err, response) { demand(err).not.exist(); if (response.version) version = response.version + '.0'; done(); }); }); }); describe('job consumer:', function() { it('#watch() watches a tube', function(done) { consumer.on('connect', function() { consumer.watch(tube, function(err, response) { demand(err).not.exist(); response.must.equal('2'); done(); }); }).on('error', function(err) { throw(err); }); consumer.connect(); }); it('#ignore() ignores a tube', function(done) { consumer.ignore('default', function(err, response) { demand(err).not.exist(); response.must.equal('1'); done(); }); }); it('#list_tubes_watched() returns the tubes the consumer watches', function(done) { consumer.list_tubes_watched(function(err, response) { demand(err).not.exist(); response.length.must.equal(1); response.indexOf(tube).must.equal(0); done(); }); }); it('#peek_ready() peeks ahead at jobs', function(done) { this.timeout(4000); producer.peek_ready(function(err, jobid, payload) { demand(err).not.exist(); jobid.must.exist(); testjobid = jobid; var parsed = JSON.parse(payload); parsed.must.have.property('type'); parsed.type.must.equal('test'); done(); }); }); it('#stats_job() returns job stats', function(done) { consumer.stats_job(testjobid, function(err, response) { demand(err).not.exist(); response.must.be.an.object(); response.must.have.property('id'); response.id.must.equal(parseInt(testjobid, 10)); response.tube.must.equal(tube); done(); }); }); it('consumer can run stats_job() while a job is reserved', function(done) { consumer.reserve(function(err, jobid, payload) { demand(err).not.exist(); consumer.stats_job(jobid, function(err, res) { demand(err).not.exist(); res.must.be.an.object(); res.must.have.property('id'); res.id.must.equal(parseInt(jobid, 10)); res.state.must.equal('reserved'); consumer.release(jobid, 1, 1, function(err) { demand(err).not.exist(); done(); }); }); }); }); it('#reserve() returns a job', function(done) { consumer.reserve(function(err, jobid, payload) { demand(err).not.exist(); jobid.must.equal(testjobid); var parsed = JSON.parse(payload); parsed.must.have.property('type'); parsed.type.must.equal('test'); done(); }); }); it('#touch() informs the server the client is still working', function(done) { consumer.touch(testjobid, function(err) { demand(err).not.exist(); done(); }); }); it('#release() releases a job', function(done) { consumer.release(testjobid, 1, 1, function(err) { demand(err).not.exist(); done(); }); }); it('jobs can contain binary data', function(done) { var payload = readTestImage(); var ptr = 0; producer.put(0, 0, 60, payload, function(err, jobid) { demand(err).not.exist(); jobid.must.exist(); consumer.reserve(function(err, returnID, returnPayload) { demand(err).not.exist(); returnID.must.equal(jobid); // we should get back exactly the same bytes we put in returnPayload.length.must.equal(payload.length); while (ptr < returnPayload.length) { returnPayload[ptr].must.equal(payload[ptr]); ptr++; } consumer.destroy(returnID, function(err) { demand(err).not.exist(); done(); }); }); }); }); it('jobs can contain utf8 data', function(done) { var payload = 'Many people like crème brûlée.'; var returnString; producer.put(0, 0, 60, payload, function(err, jobid) { demand(err).not.exist(); jobid.must.exist(); consumer.reserve(function(err, returnID, returnPayload) { demand(err).not.exist(); returnID.must.equal(jobid); // we should get back exactly the same bytes we put in returnString = returnPayload.toString(); returnString.must.equal(payload); consumer.destroy(returnID, function(err) { demand(err).not.exist(); done(); }); }); }); }); it('#peek_delayed() returns data for a delayed job', function(done) { producer.peek_delayed(function(err, jobid, payload) { demand(err).not.exist(); jobid.must.equal(testjobid); done(); }); }); it('#bury() buries a job (> 1sec expected)', function(done) { // this takes a second because of the minumum delay enforced by release() above this.timeout(3000); consumer.reserve(function(err, jobid, payload) { demand(err).not.exist(); consumer.bury(jobid, fivebeans.LOWEST_PRIORITY, function(err) { demand(err).not.exist(); done(); }); }); }); it('#peek_buried() returns data for a buried job', function(done) { producer.peek_buried(function(err, jobid, payload) { demand(err).not.exist(); jobid.must.equal(testjobid); done(); }); }); it('#kick() un-buries jobs in the producer\'s used queue', function(done) { producer.kick(10, function(err, count) { demand(err).not.exist(); count.must.equal('1'); done(); }); }); it('#kick_job() kicks a specific job id', function(done) { // Skip the test if the version of beanstalkd doesn't have this command. // Beanstalkd does not have semver-compliant version numbers, however. if (version.match(/\d+\.\d+\.\d+\.\d+/)) { version = version.replace(/\.\d+$/, ''); } if (!semver.satisfies(version, '>= 1.8.0')) return done(); consumer.reserve(function(err, jobid, payload) { demand(err).not.exist(); consumer.bury(testjobid, fivebeans.LOWEST_PRIORITY, function(err) { demand(err).not.exist(); producer.kick_job(testjobid, function(err) { demand(err).not.exist(); done(); }); }); }); }); it('#pause_tube() suspends new job reservations (> 1sec expected)', function(done) { consumer.pause_tube(tube, 3, function(err) { demand(err).not.exist(); consumer.reserve_with_timeout(1, function(err, jobid, payload) { err.must.equal('TIMED_OUT'); done(); }); }); }); it('#destroy() deletes a job (nearly 2 sec expected)', function(done) { // this takes a couple of seconds because of the minumum delay enforced by pause_tube() above this.timeout(5000); consumer.reserve(function(err, jobid, payload) { demand(err).not.exist(); consumer.destroy(jobid, function(err) { demand(err).not.exist(); done(); }); }); }); it('#reserve_with_timeout() times out when no jobs are waiting (> 1sec expected)', function(done) { this.timeout(3000); consumer.reserve_with_timeout(1, function(err, jobid, payload) { err.must.equal('TIMED_OUT'); done(); }); }); }); describe('server statistics', function() { it('#stats() returns a hash of server stats', function(done) { consumer.stats(function(err, response) { demand(err).not.exist(); response.must.be.an.object(); response.must.have.property('pid'); response.must.have.property('version'); done(); }); }); it('#list_tubes() returns a list of tubes', function(done) { consumer.list_tubes(function(err, response) { demand(err).not.exist(); response.length.must.be.above(0); response.indexOf(tube).must.be.above(-1); done(); }); }); it('#stats_tube() returns a hash of tube stats', function(done) { consumer.stats_tube(tube, function(err, response) { demand(err).not.exist(); response.must.be.an.object(); done(); }); }); it('#stats_tube() returns not found for non-existent tubes', function(done) { consumer.stats_tube('i-dont-exist', function(err, response) { err.must.be.a.string(); err.must.equal('NOT_FOUND'); done(); }); }); }); describe('concurrent commands', function() { it('can be handled', function(done) { var concurrency = 10; var replied = 0; var handleResponse = function(err, response) { demand(err).not.exist(); if (++replied >= concurrency) done(); }; for (var i = 0; i < 10; ++i) consumer.stats_tube(tube, handleResponse); }); }); });