Socket
Socket
Sign inDemoInstall

agenda

Package Overview
Dependencies
27
Maintainers
1
Versions
88
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.6.28 to 0.7.0

.eslintrc

236

lib/agenda.js

@@ -0,8 +1,27 @@

/* Code forked from https://github.com/rschmukler/agenda
*
* Updates by Neville Franks neville.franks@gmail.com www.clibu.com
* - Refactored MongoDB code to use the MongoDB Native Driver V2 instead of MongoSkin.
* - Renamed _db to _collection because it is a collection, not a db.
* - Moved code into Agenda.db_init() and call same for all initialization functions.
* - Removed findJobsResultWrapper() and replaced with inline code.
* - Removed db code from jobs.js
* - Comments.
*
* TODO:
* - Refactor remaining deprecated MongoDB Native Driver methods. findAndModify()
*
* Last change: NF 4/06/2015 2:06:12 PM
*/
var Job = require('./job.js'),
humanInterval = require('human-interval'),
utils = require('util'),
Emitter = require('events').EventEmitter,
mongo = require('mongoskin');
Emitter = require('events').EventEmitter;
var Agenda = module.exports = function(config) {
var MongoClient = require('mongodb').MongoClient,
Db = require('mongodb').Db;
var Agenda = module.exports = function(config, cb) {
if (!(this instanceof Agenda)) {

@@ -21,20 +40,31 @@ return new Agenda(config);

if (config.db) {
this.database(config.db.address, config.db.collection, config.db.options);
this.database(config.db.address, config.db.collection, config.db.options, function(err, coll) {
if (cb) {
cb(err, coll);
}
});
} else if (config.mongo) {
this._db = config.mongo;
this._mdb = config.mongo;
this.db_init( config.db ? config.db.collection : undefined ); // NF 20/04/2015
}
};
utils.inherits(Agenda, Emitter);
utils.inherits(Agenda, Emitter); // Job uses emit() to fire job events client can use.
// Configuration Methods
Agenda.prototype.mongo = function(db) {
this._db = db;
Agenda.prototype.mongo = function( mdb ){
this._mdb = mdb;
this.db_init(); // NF 20/04/2015
return this;
};
Agenda.prototype.database = function(url, collection, options) {
collection = collection || 'agendaJobs';
options = options || {w: 0};
/** Connect to the spec'd MongoDB server and database.
* Notes:
* - If `url` inludes auth details then `options` must specify: { 'uri_decode_auth': true }. This does Auth on the specified
* database, not the Admin database. If you are using Auth on the Admin DB and not on the Agenda DB, then you need to
* authenticate against the Admin DB and then pass the MongoDB instance in to the Constructor or use Agenda.mongo().
* - If your app already has a MongoDB connection then use that. ie. specify config.mongo in the Constructor or use Agenda.mongo().
*/
Agenda.prototype.database = function(url, collection, options, cb) {
if (!url.match(/^mongodb:\/\/.*/)) {

@@ -44,13 +74,34 @@ url = 'mongodb://' + url;

this._db = mongo.db(url, options).collection(collection);
var ignoreErrors = function() {};
this._db.ensureIndex("nextRunAt", ignoreErrors)
.ensureIndex("lockedAt", ignoreErrors)
.ensureIndex("name", ignoreErrors)
.ensureIndex("priority", ignoreErrors);
collection = collection || 'agendaJobs';
options = options || {};
var self = this;
MongoClient.connect(url, options, function( error, db ){
if (error) throw error; // Auth failed etc.
self._mdb = db;
self.db_init( collection ); // NF 20/04/2015
if (cb) {
cb(error, this._collection);
}
});
return this;
};
/** Setup and initialize the collection used to manage Jobs.
* @param collection collection name or undefined for default 'agendaJobs'
* NF 20/04/2015
*/
Agenda.prototype.db_init = function( collection ){
this._collection = this._mdb.collection(collection || 'agendaJobs');
this._collection.createIndexes([{
"key": {"name" : 1, "priority" : -1, "lockedAt" : 1, "nextRunAt" : 1, "disabled" : 1},
"name": "findAndLockNextJobIndex1"
}, {
"key": {"name" : 1, "lockedAt" : 1, "priority" : -1, "nextRunAt" : 1, "disabled" : 1},
"name": "findAndLockNextJobIndex2"
}],
function( err, result ){
if (err) throw err;
});
};
Agenda.prototype.name = function(name) {

@@ -88,15 +139,21 @@ this._name = name;

Agenda.prototype.jobs = function() {
var args = Array.prototype.slice.call(arguments);
if (typeof args[args.length - 1] === 'function') {
args.push(findJobsResultWrapper(this, args.pop()));
}
return this._db.findItems.apply(this._db, args);
/** Find all Jobs matching `query` and pass same back in cb().
* refactored. NF 21/04/2015
*/
Agenda.prototype.jobs = function( query, cb ){
var self = this;
this._collection.find( query ).toArray( function( error, result ){
var jobs;
if( !error ){
jobs = result.map( createJob.bind( null, self ) );
}
cb( error, jobs );
});
};
Agenda.prototype.purge = function(cb) {
var definedNames = Object.keys(this._definitions);
this._db.remove({name: {$not: {$in: definedNames}}}, cb);
this.cancel( {name: {$not: {$in: definedNames}}}, cb ); // NF refactored 21/04/2015
};

@@ -173,4 +230,15 @@

/** Cancels any jobs matching the passed mongodb query, and removes them from the database.
* @param query mongo db query
* @param cb callback( error, numRemoved )
*
* @caller client code, Agenda.purge(), Job.remove()
*/
Agenda.prototype.cancel = function(query, cb) {
return this._db.remove(query, cb);
// NF refactored 21/04/2015
this._collection.deleteMany( query, function( error, result ){
cb( error, result && result.result ? result.result.n : undefined );
});
};

@@ -197,3 +265,3 @@

if (id) {
this._db.findAndModify({_id: id}, {}, update, {new: true}, processDbResult);
this._collection.findAndModify({_id: id}, {}, update, {new: true}, processDbResult );
} else if (props.type == 'single') {

@@ -208,24 +276,27 @@ if (props.nextRunAt && props.nextRunAt <= now) {

// Try an upsert.
this._db.findAndModify({name: props.name, type: 'single'}, {}, update, {upsert: true, new: true}, processDbResult);
this._collection.findAndModify({name: props.name, type: 'single'}, {}, update, {upsert: true, new: true}, processDbResult);
} else if (unique) {
var query = job.attrs.unique;
query.name = props.name;
this._db.findAndModify(query, {}, update, {upsert: true, new: true}, processDbResult);
this._collection.findAndModify(query, {}, update, {upsert: true, new: true}, processDbResult);
} else {
this._db.insert(props, processDbResult);
this._collection.insertOne(props, processDbResult); // NF updated 22/04/2015
}
function processDbResult(err, res) {
function processDbResult(err, result) {
if (err) {
throw(err);
} else if (res) {
if (Array.isArray(res)) {
res = res[0];
}
} else if (result) {
var res = result.ops ? result.ops : result.value; // result is different for findAndModify() vs. insertOne(). NF 20/04/2015
if ( res ){
if (Array.isArray(res)) {
res = res[0];
}
job.attrs._id = res._id;
job.attrs.nextRunAt = res.nextRunAt;
job.attrs._id = res._id;
job.attrs.nextRunAt = res.nextRunAt;
if (job.attrs.nextRunAt && job.attrs.nextRunAt < self._nextScanAt) {
processJobs.call(self, job);
if (job.attrs.nextRunAt && job.attrs.nextRunAt < self._nextScanAt) {
processJobs.call(self, job);
}
}

@@ -253,3 +324,3 @@ }

this._processInterval = undefined;
unlockJobs.call(this, cb);
this._unlockJobs( cb );
};

@@ -262,47 +333,37 @@

* @protected
* @caller jobQueueFilling() only
*/
Agenda.prototype._findAndLockNextJob = function(jobName, definition, cb) {
var now = new Date(),
var self = this,
now = new Date(),
lockDeadline = new Date(Date.now().valueOf() - definition.lockLifetime);
this._db.findAndModify(
{
nextRunAt: {$lte: this._nextScanAt},
disabled: { $ne: true },
$or: [
{lockedAt: null},
{lockedAt: {$exists: false}},
{lockedAt: {$lte: lockDeadline}}
],
name: jobName
},
{'priority': -1},
{$set: {lockedAt: now}},
{'new': true},
findJobsResultWrapper(this, cb)
);
// Don't try and access Mongo Db if we've lost connection to it. Also see clibu_automation.js db.on.close code. NF 29/04/2015
// Trying to resolve crash on Dev PC when it resumes from sleep.
if ( this._mdb.s.topology.connections().length === 0 ) {
cb( new MongoError( 'No MongoDB Connection') );
} else {
this._collection.findAndModify(
{
$or: [
{name: jobName, lockedAt: null, nextRunAt: {$lte: this._nextScanAt}, disabled: { $ne: true }},
{name: jobName, lockedAt: {$exists: false}, nextRunAt: {$lte: this._nextScanAt}, disabled: { $ne: true }},
{name: jobName, lockedAt: {$lte: lockDeadline}, nextRunAt: {$lte: this._nextScanAt}, disabled: { $ne: true }}
]
},
{'priority': -1}, // sort
{$set: {lockedAt: now}}, // Doc
{'new': true}, // options
function( error, result ){
var jobs;
if ( !error && result.value ){
jobs = createJob( self, result.value );
}
cb( error, jobs );
}
);
}
};
/**
*
* @param agenda
* @param cb
* @return {Function}
* @private
*/
function findJobsResultWrapper(agenda, cb) {
return function (err, jobs) {
if (jobs) {
//query result can be array or one record
if (Array.isArray(jobs)) {
jobs = jobs.map(createJob.bind(null, agenda));
} else {
jobs = createJob(agenda, jobs);
}
}
cb(err, jobs);
};
}
/**

@@ -320,3 +381,5 @@ * Create Job object from data

function unlockJobs(done) {
// Refactored to Agenda method. NF 22/04/2015
// @caller Agenda.stop() only. Could be moved into stop(). NF
Agenda.prototype._unlockJobs = function(done) {
function getJobId(j) {

@@ -328,5 +391,6 @@ return j.attrs._id;

.concat(this._runningJobs.map(getJobId));
this._db.update({_id: { $in: jobIds } }, { $set: { lockedAt: null } }, {multi: true}, done);
}
this._collection.updateMany({_id: { $in: jobIds } }, { $set: { lockedAt: null } }, done); // NF refactored .update() 22/04/2015
};
function processJobs(extraJob) {

@@ -349,4 +413,4 @@ if (!this._processInterval) {

var now = new Date();
self._db.findAndModify({ _id: extraJob.attrs._id, lockedAt: null }, {}, { $set: { lockedAt: now } }, function(err, resp) {
if (resp) {
self._collection.findAndModify({ _id: extraJob.attrs._id, lockedAt: null, disabled: { $ne: true } }, {}, { $set: { lockedAt: now } }, function(err, resp) {
if ( resp.value ){ // NF 20/04/2015
jobQueue.unshift(extraJob);

@@ -360,3 +424,3 @@ jobProcessing();

var now = new Date();
self._nextScanAt = new Date(now.valueOf() + self._processEvery),
self._nextScanAt = new Date(now.valueOf() + self._processEvery);
self._findAndLockNextJob(name, definitions[name], function (err, job) {

@@ -363,0 +427,0 @@ if (err) {

@@ -0,1 +1,10 @@

/** @package
job.js
21/04/2015 NF Removed database code. ie. From Job.remove()
Last change: NF 21/04/2015 1:20:47 PM
*/
var humanInterval = require('human-interval'),

@@ -172,7 +181,16 @@ CronTime = require('cron').CronTime,

self.attrs.lockedAt = null;
self.save(function(saveErr, job) {
cb && cb(err || saveErr, job);
if (self.attrs.nextRunAt) {
self.save(postCommit);
} else {
self.remove(function(removeErr) {
postCommit(removeErr, self);
});
}
function postCommit(commitErr, job) {
cb && cb(err || commitErr, job);
agenda.emit('complete', self);
agenda.emit('complete:' + self.attrs.name, self);
});
}
};

@@ -214,2 +232,5 @@

Job.prototype.remove = function(cb) {
// refactored NF 21/04/2015
this.agenda.cancel( {_id: this.attrs._id}, cb );
/*
var self = this;

@@ -222,2 +243,3 @@ this.agenda._db.remove({_id: this.attrs._id}, function(err, count) {

});
*/
};

@@ -224,0 +246,0 @@

{
"name": "agenda",
"version": "0.6.28",
"version": "0.7.0",
"description": "Light weight job scheduler for Node.js",

@@ -15,3 +15,3 @@ "main": "index.js",

"type": "git",
"url": "git://github.com/rschmukler/agenda"
"url": "git://github.com/classdojo/agenda"
},

@@ -34,13 +34,13 @@ "keywords": [

"date.js": "~0.2.0",
"mongoskin": "~1.4.1",
"mongodb": "2.0.34",
"cron": "~1.0.1"
},
"devDependencies": {
"mocha": "~1.13.0",
"expect.js": "~0.2.0",
"mocha-lcov-reporter": "0.0.1",
"coveralls": "~2.3.0",
"blanket": "~1.1.5",
"q": "~1.0.0"
"blanket": "1.1.5",
"coveralls": "~2.11.4",
"expect.js": "~0.3.1",
"mocha": "~2.3.2",
"mocha-lcov-reporter": "0.0.2",
"q": "~1.4.1"
}
}
# Agenda
[![Build Status](https://api.travis-ci.org/rschmukler/agenda.svg)](http://travis-ci.org/rschmukler/agenda)
[![Code Climate](https://d3s6mut3hikguw.cloudfront.net/github/rschmukler/agenda.png)](https://codeclimate.com/github/rschmukler/agenda/badges)
[![Coverage Status](https://coveralls.io/repos/rschmukler/agenda/badge.png)](https://coveralls.io/r/rschmukler/agenda)
[![Dependency Status](https://david-dm.org/rschmukler/agenda.svg)](https://david-dm.org/rschmukler/agenda)
[![devDependency Status](https://david-dm.org/rschmukler/agenda/dev-status.svg)](https://david-dm.org/rschmukler/agenda#info=devDependencies)
[![Code Climate](https://d3s6mut3hikguw.cloudfront.net/github/rschmukler/agenda.svg)](https://codeclimate.com/github/rschmukler/agenda/badges)
[![Coverage Status](https://coveralls.io/repos/rschmukler/agenda/badge.svg)](https://coveralls.io/r/rschmukler/agenda)

@@ -17,2 +19,3 @@ Agenda is a light-weight job scheduling library for Node.js.

# Installation

@@ -24,3 +27,3 @@

You will also need a working [mongo](http://www.mongodb.org/) database (2.4+) to point it to.
You will also need a working [mongo](http://www.mongodb.org/) database (2.6+) to point it to.

@@ -30,4 +33,16 @@ # Example Usage

```js
var agenda = new Agenda({db: { address: 'localhost:27017/agenda-example'}});
var mongoConnectionString = "mongodb://127.0.0.1/agenda";
var agenda = new Agenda({db: {address: mongoConnectionString}});
// or override the default collection name:
// var agenda = new Agenda({db: {address: mongoConnectionString, collection: "jobCollectionName"}});
// or pass additional connection options:
// var agenda = new Agenda({db: {address: mongoConnectionString, collection: "jobCollectionName", options: {server:{auto_reconnect:true}}});
// or pass in an existing mongodb-native MongoClient instance
// var agenda = new Agenda({mongo: myMongoClient});
agenda.define('delete old users', function(job, done) {

@@ -40,6 +55,6 @@ User.remove({lastLogIn: { $lt: twoDaysAgo }}, done);

// Alternatively, you could also do:
agenda.every('*/3 * * * *', 'delete old users');
agenda.start();
```

@@ -63,3 +78,3 @@

```js
var weeklyReport = agenda.schedule('Saturday at noon', 'send email report', {to: 'another-guy@example.com'});
var weeklyReport = agenda.create('send email report', {to: 'another-guy@example.com'})
weeklyReport.repeatEvery('1 week').save();

@@ -127,5 +142,5 @@ agenda.start();

### mongo(mongoSkinInstance)
### mongo(mongoClientInstance)
Use an existing mongoskin instance. This can help consolidate connections to a
Use an existing mongodb-native MongoClient instance. This can help consolidate connections to a
database. You can instead use `.database` to have agenda handle connecting for

@@ -136,3 +151,3 @@ you.

Please note that this must be a *collection*. Also, you will want to run the following
Please note that this must be a *collection*. Also, you will want to run the following
afterwards to ensure the database has the proper indexes:

@@ -154,3 +169,3 @@

```js
var agenda = new Agenda({mongo: mongoSkinInstance});
var agenda = new Agenda({mongo: mongoClientInstance});
```

@@ -388,6 +403,6 @@

### jobs(mongoskin query)
### jobs(mongodb-native query)
Lets you query all of the jobs in the agenda job's database. This is a full [mongoskin](https://github.com/kissjs/node-mongoskin)
`find` query. See mongoskin's documentation for details.
Lets you query all of the jobs in the agenda job's database. This is a full [mongodb-native](https://github.com/mongodb/node-mongodb-native)
`find` query. See mongodb-native's documentation for details.

@@ -400,5 +415,5 @@ ```js

### cancel(mongoskin query, cb)
### cancel(mongodb-native query, cb)
Cancels any jobs matching the passed mongoskin query, and removes them from the database.
Cancels any jobs matching the passed mongodb-native query, and removes them from the database.

@@ -827,3 +842,3 @@ ```js

```
node server.js
node server.js
```

@@ -858,2 +873,3 @@ Fire up an instance with no `JOB_TYPES`, giving you the ability to process jobs,

- [@nwkeeley](http://github.com/nwkeeley)
- [@liamdon](http://github.com/liamdon)

@@ -860,0 +876,0 @@

/* globals before, describe, it, beforeEach, after, afterEach */
var mongoHost = process.env.MONGODB_HOST || 'localhost',
mongoPort = process.env.MONGODB_PORT || '27017',
mongoCfg = 'mongodb://' + mongoHost + ':' + mongoPort + '/agenda-test';
var mongoCfg = 'localhost:27017/agenda-test',
expect = require('expect.js'),
var expect = require('expect.js'),
path = require('path'),
cp = require('child_process'),
mongo = require('mongoskin').db('mongodb://' + mongoCfg, {w: 0}),
Agenda = require( path.join('..', 'index.js') ),
jobs = new Agenda({
defaultConcurrency: 5,
db: {
address: mongoCfg
}
}),
Job = require( path.join('..', 'lib', 'job.js') );
var MongoClient = require('mongodb').MongoClient;
var mongo = null;
// create agenda instances
var jobs = null;
function clearJobs(done) {

@@ -22,283 +23,315 @@ mongo.collection('agendaJobs').remove({}, done);

// Slow timeouts for travis
var jobTimeout = process.env.TRAVIS ? 1000 : 300;
var jobTimeout = process.env.TRAVIS ? 1500 : 300;
before(clearJobs);
var jobProcessor = function(job) { };
before(function() {
jobs.define('someJob', jobProcessor);
jobs.define('send email', jobProcessor);
jobs.define('some job', jobProcessor);
});
var jobType = 'do work';
var jobProcessor = function(job) { };
describe('Agenda', function() {
it('sets a default processEvery', function() {
expect(jobs._processEvery).to.be(5000);
});
describe('configuration methods', function() {
describe('database', function() {
it('sets the database', function() {
jobs.database('localhost:27017/agenda-test-new');
expect(jobs._db._skin_db._connect_args[0]).to.contain('agenda-test-new');
});
it('sets the collection', function() {
jobs.database(mongoCfg, 'myJobs');
expect(jobs._db._collection_args[0]).to.be('myJobs');
});
it('returns itself', function() {
expect(jobs.database(mongoCfg)).to.be(jobs);
});
});
function failOnError(err) {
if (err) {
throw err;
}
}
describe('mongo', function() {
it('sets the _db directly', function() {
var agenda = new Agenda();
agenda.mongo({hello: 'world'});
expect(agenda._db).to.have.property('hello', 'world');
describe("agenda", function() {
before(function(done) {
jobs = new Agenda({
db: {
address: mongoCfg
}
}, function(err) {
MongoClient.connect(mongoCfg, function( error, db ){
mongo = db;
setTimeout(function() {
clearJobs(function() {
jobs.define('someJob', jobProcessor);
jobs.define('send email', jobProcessor);
jobs.define('some job', jobProcessor);
jobs.define(jobType, jobProcessor);
done();
});
}, 50);
});
it('returns itself', function() {
var agenda = new Agenda();
expect(agenda.mongo({hello: 'world'})).to.be(agenda);
});
});
});
describe('name', function() {
it('sets the agenda name', function() {
jobs.name('test queue');
expect(jobs._name).to.be('test queue');
});
it('returns itself', function() {
expect(jobs.name('test queue')).to.be(jobs);
});
after(function(done) {
setTimeout(function() {
mongo.close(done);
}, 50);
});
describe('Agenda', function() {
it('sets a default processEvery', function() {
expect(jobs._processEvery).to.be(5000);
});
describe('processEvery', function() {
it('sets the processEvery time', function() {
jobs.processEvery('3 minutes');
expect(jobs._processEvery).to.be(180000);
describe('configuration methods', function() {
it('sets the _db directly when passed as an option', function() {
var agenda = new Agenda({mongo: mongo});
expect(agenda._mdb.databaseName).to.equal('agenda-test');
});
it('returns itself', function() {
expect(jobs.processEvery('3 minutes')).to.be(jobs);
});
});
describe('maxConcurrency', function() {
it('sets the maxConcurrency', function() {
jobs.maxConcurrency(10);
expect(jobs._maxConcurrency).to.be(10);
});
it('returns itself', function() {
expect(jobs.maxConcurrency(10)).to.be(jobs);
});
});
describe('defaultConcurrency', function() {
it('sets the defaultConcurrency', function() {
jobs.defaultConcurrency(1);
expect(jobs._defaultConcurrency).to.be(1);
});
it('returns itself', function() {
expect(jobs.defaultConcurrency(5)).to.be(jobs);
});
});
describe('defaultLockLifetime', function(){
it('returns itself', function() {
expect(jobs.defaultLockLifetime(1000)).to.be(jobs);
});
it('sets the default lock lifetime', function(){
jobs.defaultLockLifetime(9999);
expect(jobs._defaultLockLifetime).to.be(9999);
});
it('is inherited by jobs', function(){
jobs.defaultLockLifetime(7777);
jobs.define('testDefaultLockLifetime', function(job, done){});
expect(jobs._definitions.testDefaultLockLifetime.lockLifetime).to.be(7777);
});
});
});
describe('job methods', function() {
describe('configuration methods', function() {
describe('create', function() {
var job;
beforeEach(function() {
job = jobs.create('sendEmail', {to: 'some guy'});
describe('mongo', function() {
it('sets the _db directly', function() {
var agenda = new Agenda();
agenda.mongo(mongo);
expect(agenda._mdb.databaseName).to.equal('agenda-test');
});
it('returns itself', function() {
var agenda = new Agenda();
expect(agenda.mongo(mongo)).to.be(agenda);
});
});
it('returns a job', function() {
expect(job).to.be.a(Job);
describe('name', function() {
it('sets the agenda name', function() {
jobs.name('test queue');
expect(jobs._name).to.be('test queue');
});
it('returns itself', function() {
expect(jobs.name('test queue')).to.be(jobs);
});
});
it('sets the name', function() {
expect(job.attrs.name).to.be('sendEmail');
describe('processEvery', function() {
it('sets the processEvery time', function() {
jobs.processEvery('3 minutes');
expect(jobs._processEvery).to.be(180000);
});
it('returns itself', function() {
expect(jobs.processEvery('3 minutes')).to.be(jobs);
});
});
it('sets the type', function() {
expect(job.attrs.type).to.be('normal');
describe('maxConcurrency', function() {
it('sets the maxConcurrency', function() {
jobs.maxConcurrency(10);
expect(jobs._maxConcurrency).to.be(10);
});
it('returns itself', function() {
expect(jobs.maxConcurrency(10)).to.be(jobs);
});
});
it('sets the agenda', function() {
expect(job.agenda).to.be(jobs);
describe('defaultConcurrency', function() {
it('sets the defaultConcurrency', function() {
jobs.defaultConcurrency(1);
expect(jobs._defaultConcurrency).to.be(1);
});
it('returns itself', function() {
expect(jobs.defaultConcurrency(5)).to.be(jobs);
});
});
it('sets the data', function() {
expect(job.attrs.data).to.have.property('to', 'some guy');
describe('defaultLockLifetime', function(){
it('returns itself', function() {
expect(jobs.defaultLockLifetime(1000)).to.be(jobs);
});
it('sets the default lock lifetime', function(){
jobs.defaultLockLifetime(9999);
expect(jobs._defaultLockLifetime).to.be(9999);
});
it('is inherited by jobs', function(){
jobs.defaultLockLifetime(7777);
jobs.define('testDefaultLockLifetime', function(job, done){});
expect(jobs._definitions.testDefaultLockLifetime.lockLifetime).to.be(7777);
});
});
});
describe('define', function() {
describe('job methods', function() {
it('stores the definition for the job', function() {
expect(jobs._definitions.someJob).to.have.property('fn', jobProcessor);
});
describe('create', function() {
var job;
beforeEach(function() {
job = jobs.create('sendEmail', {to: 'some guy'});
});
it('sets the default concurrency for the job', function() {
expect(jobs._definitions.someJob).to.have.property('concurrency', 5);
});
it('sets the default priority for the job', function() {
expect(jobs._definitions.someJob).to.have.property('priority', 0);
});
it('takes concurrency option for the job', function() {
jobs.define('highPriority', {priority: 10}, jobProcessor);
expect(jobs._definitions.highPriority).to.have.property('priority', 10);
});
});
describe('every', function() {
describe('with a job name specified', function() {
it('returns a job', function() {
expect(jobs.every('5 minutes', 'send email')).to.be.a(Job);
expect(job).to.be.a(Job);
});
it('sets the repeatEvery', function() {
expect(jobs.every('5 seconds', 'send email').attrs.repeatInterval).to.be('5 seconds');
it('sets the name', function() {
expect(job.attrs.name).to.be('sendEmail');
});
it('sets the type', function() {
expect(job.attrs.type).to.be('normal');
});
it('sets the agenda', function() {
expect(jobs.every('5 seconds', 'send email').agenda).to.be(jobs);
expect(job.agenda).to.be(jobs);
});
it('should update a job that was previously scheduled with `every`', function(done) {
jobs.every(10, 'shouldBeSingleJob');
setTimeout(function() {
jobs.every(20, 'shouldBeSingleJob');
}, 10);
it('sets the data', function() {
expect(job.attrs.data).to.have.property('to', 'some guy');
});
});
// Give the saves a little time to propagate
setTimeout(function() {
jobs.jobs({name: 'shouldBeSingleJob'}, function(err, res) {
expect(res).to.have.length(1);
done();
});
}, jobTimeout);
describe('define', function() {
it('stores the definition for the job', function() {
expect(jobs._definitions.someJob).to.have.property('fn', jobProcessor);
});
});
describe('with array of names specified', function () {
it('returns array of jobs', function () {
expect(jobs.every('5 minutes', ['send email', 'some job'])).to.be.an('array');
it('sets the default concurrency for the job', function() {
expect(jobs._definitions.someJob).to.have.property('concurrency', 5);
});
});
after(clearJobs);
});
describe('schedule', function() {
describe('with a job name specified', function() {
it('returns a job', function() {
expect(jobs.schedule('in 5 minutes', 'send email')).to.be.a(Job);
it('sets the default priority for the job', function() {
expect(jobs._definitions.someJob).to.have.property('priority', 0);
});
it('sets the schedule', function() {
var fiveish = (new Date()).valueOf() + 250000;
expect(jobs.schedule('in 5 minutes', 'send email').attrs.nextRunAt.valueOf()).to.be.greaterThan(fiveish);
it('takes concurrency option for the job', function() {
jobs.define('highPriority', {priority: 10}, jobProcessor);
expect(jobs._definitions.highPriority).to.have.property('priority', 10);
});
});
describe('with array of names specified', function () {
it('returns array of jobs', function () {
expect(jobs.schedule('5 minutes', ['send email', 'some job'])).to.be.an('array');
});
});
after(clearJobs);
});
describe('unique', function() {
describe('every', function() {
describe('with a job name specified', function() {
it('returns a job', function() {
expect(jobs.every('5 minutes', 'send email')).to.be.a(Job);
});
it('sets the repeatEvery', function() {
expect(jobs.every('5 seconds', 'send email').attrs.repeatInterval).to.be('5 seconds');
});
it('sets the agenda', function() {
expect(jobs.every('5 seconds', 'send email').agenda).to.be(jobs);
});
it('should update a job that was previously scheduled with `every`', function(done) {
jobs.every(10, 'shouldBeSingleJob');
setTimeout(function() {
jobs.every(20, 'shouldBeSingleJob');
}, 10);
describe('should demonstrate unique contraint', function(done) {
it('should create one job when unique matches', function(done) {
var time = new Date();
jobs.create('unique job', {type: 'active', userId: '123', 'other': true}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time}).schedule(time).save(function(err, job) {
jobs.create('unique job', {type: 'active', userId: '123', 'other': false}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time}).schedule(time).save(function(err, job) {
mongo.collection('agendaJobs').find({name: 'unique job'}).toArray(function(err, j) {
expect(j).to.have.length(1);
// Give the saves a little time to propagate
setTimeout(function() {
jobs.jobs({name: 'shouldBeSingleJob'}, function(err, res) {
expect(res).to.have.length(1);
done();
});
});
}, jobTimeout);
});
});
describe('with array of names specified', function () {
it('returns array of jobs', function () {
expect(jobs.every('5 minutes', ['send email', 'some job'])).to.be.an('array');
});
});
after(clearJobs);
});
describe('should demonstrate non-unique contraint', function(done) {
it('should create two jobs when unique doesn\t match', function(done) {
var time = new Date(Date.now() + 1000*60*3);
var time2 = new Date(Date.now() + 1000*60*4);
jobs.create('unique job', {type: 'active', userId: '123', 'other': true}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time}).schedule(time).save(function(err, job) {
jobs.create('unique job', {type: 'active', userId: '123', 'other': false}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time2}).schedule(time).save(function(err, job) {
mongo.collection('agendaJobs').find({name: 'unique job'}).toArray(function(err, j) {
expect(j).to.have.length(2);
done();
});
});
describe('schedule', function() {
describe('with a job name specified', function() {
it('returns a job', function() {
expect(jobs.schedule('in 5 minutes', 'send email')).to.be.a(Job);
});
it('sets the schedule', function() {
var fiveish = (new Date()).valueOf() + 250000;
expect(jobs.schedule('in 5 minutes', 'send email').attrs.nextRunAt.valueOf()).to.be.greaterThan(fiveish);
});
});
describe('with array of names specified', function () {
it('returns array of jobs', function () {
expect(jobs.schedule('5 minutes', ['send email', 'some job'])).to.be.an('array');
});
});
after(clearJobs);
});
describe('unique', function() {
describe('should demonstrate unique contraint', function(done) {
it('should create one job when unique matches', function(done) {
var time = new Date();
jobs.create('unique job', {type: 'active', userId: '123', 'other': true}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time}).schedule(time).save(function(err, job) {
jobs.create('unique job', {type: 'active', userId: '123', 'other': false}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time}).schedule(time).save(function(err, job) {
mongo.collection('agendaJobs').find({name: 'unique job'}).toArray(function(err, j) {
expect(j).to.have.length(1);
done();
});
});
});
});
after(clearJobs);
});
after(clearJobs);
});
});
describe('now', function() {
it('returns a job', function() {
expect(jobs.now('send email')).to.be.a(Job);
describe('should demonstrate non-unique contraint', function(done) {
it('should create two jobs when unique doesn\t match', function(done) {
var time = new Date(Date.now() + 1000*60*3);
var time2 = new Date(Date.now() + 1000*60*4);
jobs.create('unique job', {type: 'active', userId: '123', 'other': true}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time}).schedule(time).save(function(err, job) {
jobs.create('unique job', {type: 'active', userId: '123', 'other': false}).unique({'data.type': 'active', 'data.userId': '123', nextRunAt: time2}).schedule(time).save(function(err, job) {
mongo.collection('agendaJobs').find({name: 'unique job'}).toArray(function(err, j) {
expect(j).to.have.length(2);
done();
});
});
});
});
after(clearJobs);
});
});
it('sets the schedule', function() {
var now = new Date();
expect(jobs.now('send email').attrs.nextRunAt.valueOf()).to.be.greaterThan(now.valueOf() - 1);
});
it('runs the job immediately', function(done) {
jobs.define('immediateJob', function(job) {
jobs.stop(done);
describe('now', function() {
it('returns a job', function() {
expect(jobs.now('send email')).to.be.a(Job);
});
jobs.now('immediateJob');
jobs.start();
it('sets the schedule', function() {
var now = new Date();
expect(jobs.now('send email').attrs.nextRunAt.valueOf()).to.be.greaterThan(now.valueOf() - 1);
});
it('runs the job immediately', function(done) {
jobs.define('immediateJob', function(job) {
expect(job.isRunning()).to.be(true);
jobs.stop(done);
});
jobs.now('immediateJob');
jobs.start();
});
after(clearJobs);
});
after(clearJobs);
});
describe('jobs', function() {
it('returns jobs', function(done) {
var job = jobs.create('test');
job.save(function() {
jobs.jobs({}, function(err, c) {
expect(c.length).to.not.be(0);
expect(c[0]).to.be.a(Job);
clearJobs(done);
describe('jobs', function() {
it('returns jobs', function(done) {
var job = jobs.create('test');
job.save(function() {
jobs.jobs({}, function(err, c) {
expect(c.length).to.not.be(0);
expect(c[0]).to.be.a(Job);
clearJobs(done);
});
});
});
});
});
describe('purge', function() {
it('removes all jobs without definitions', function(done) {
var job = jobs.create('no definition');
jobs.stop(function() {
job.save(function() {
jobs.jobs({name: 'no definition'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(1);
jobs.purge(function(err) {
describe('purge', function() {
it('removes all jobs without definitions', function(done) {
var job = jobs.create('no definition');
jobs.stop(function() {
job.save(function() {
jobs.jobs({name: 'no definition'}, function(err, j) {
if(err) return done(err);
jobs.jobs({name: 'no definition'}, function(err, j) {
expect(j).to.have.length(1);
jobs.purge(function(err) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
jobs.jobs({name: 'no definition'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
});
});

@@ -310,80 +343,80 @@ });

});
});
describe('saveJob', function() {
it('persists job to the database', function(done) {
var job = jobs.create('someJob', {});
job.save(function(err, job) {
expect(job.attrs._id).to.be.ok();
clearJobs(done);
describe('saveJob', function() {
it('persists job to the database', function(done) {
var job = jobs.create('someJob', {});
job.save(function(err, job) {
expect(job.attrs._id).to.be.ok();
clearJobs(done);
});
});
});
});
});
describe('cancel', function() {
beforeEach(function(done) {
var remaining = 3;
var checkDone = function(err) {
if(err) return done(err);
remaining--;
if(!remaining) {
describe('cancel', function() {
beforeEach(function(done) {
var remaining = 3;
var checkDone = function(err) {
if(err) return done(err);
remaining--;
if(!remaining) {
done();
}
};
jobs.create('jobA').save(checkDone);
jobs.create('jobA', 'someData').save(checkDone);
jobs.create('jobB').save(checkDone);
});
afterEach(function(done) {
jobs._collection.remove({name: {$in: ['jobA', 'jobB']}}, function(err) {
if(err) return done(err);
done();
}
};
jobs.create('jobA').save(checkDone);
jobs.create('jobA', 'someData').save(checkDone);
jobs.create('jobB').save(checkDone);
});
afterEach(function(done) {
jobs._db.remove({name: {$in: ['jobA', 'jobB']}}, function(err) {
if(err) return done(err);
done();
});
});
});
it('should cancel a job', function(done) {
jobs.jobs({name: 'jobA'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(2);
jobs.cancel({name: 'jobA'}, function(err) {
it('should cancel a job', function(done) {
jobs.jobs({name: 'jobA'}, function(err, j) {
if(err) return done(err);
jobs.jobs({name: 'jobA'}, function(err, j) {
expect(j).to.have.length(2);
jobs.cancel({name: 'jobA'}, function(err) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
jobs.jobs({name: 'jobA'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
});
});
});
});
});
it('should cancel multiple jobs', function(done) {
jobs.jobs({name: {$in: ['jobA', 'jobB']}}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(3);
jobs.cancel({name: {$in: ['jobA', 'jobB']}}, function(err) {
it('should cancel multiple jobs', function(done) {
jobs.jobs({name: {$in: ['jobA', 'jobB']}}, function(err, j) {
if(err) return done(err);
jobs.jobs({name: {$in: ['jobA', 'jobB']}}, function(err, j) {
expect(j).to.have.length(3);
jobs.cancel({name: {$in: ['jobA', 'jobB']}}, function(err) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
jobs.jobs({name: {$in: ['jobA', 'jobB']}}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
});
});
});
});
});
it('should cancel jobs only if the data matches', function(done){
jobs.jobs({name: 'jobA', data: 'someData'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(1);
jobs.cancel({name: 'jobA', data: 'someData'}, function(err) {
it('should cancel jobs only if the data matches', function(done){
jobs.jobs({name: 'jobA', data: 'someData'}, function(err, j) {
if(err) return done(err);
jobs.jobs({name: 'jobA', data: 'someData'}, function(err, j) {
expect(j).to.have.length(1);
jobs.cancel({name: 'jobA', data: 'someData'}, function(err) {
if(err) return done(err);
expect(j).to.have.length(0);
jobs.jobs({name: 'jobA'}, function(err, j) {
jobs.jobs({name: 'jobA', data: 'someData'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(1);
done();
expect(j).to.have.length(0);
jobs.jobs({name: 'jobA'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(1);
done();
});
});

@@ -395,175 +428,175 @@ });

});
});
describe('Job', function() {
describe('repeatAt', function() {
var job = new Job();
it('sets the repeat at', function() {
job.repeatAt('3:30pm');
expect(job.attrs.repeatAt).to.be('3:30pm');
describe('Job', function() {
describe('repeatAt', function() {
var job = new Job();
it('sets the repeat at', function() {
job.repeatAt('3:30pm');
expect(job.attrs.repeatAt).to.be('3:30pm');
});
it('returns the job', function() {
expect(job.repeatAt('3:30pm')).to.be(job);
});
});
it('returns the job', function() {
expect(job.repeatAt('3:30pm')).to.be(job);
});
});
describe('unique', function() {
var job = new Job();
it('sets the unique property', function() {
job.unique({'data.type': 'active', 'data.userId': '123'});
expect(JSON.stringify(job.attrs.unique)).to.be(JSON.stringify({'data.type': 'active', 'data.userId': '123'}));
});
it('returns the job', function() {
expect(job.unique({'data.type': 'active', 'data.userId': '123'})).to.be(job);
});
});
describe('repeatEvery', function() {
var job = new Job();
it('sets the repeat interval', function() {
job.repeatEvery(5000);
expect(job.attrs.repeatInterval).to.be(5000);
describe('unique', function() {
var job = new Job();
it('sets the unique property', function() {
job.unique({'data.type': 'active', 'data.userId': '123'});
expect(JSON.stringify(job.attrs.unique)).to.be(JSON.stringify({'data.type': 'active', 'data.userId': '123'}));
});
it('returns the job', function() {
expect(job.unique({'data.type': 'active', 'data.userId': '123'})).to.be(job);
});
});
it('returns the job', function() {
expect(job.repeatEvery('one second')).to.be(job);
});
});
describe('schedule', function() {
var job;
beforeEach(function() {
job = new Job();
describe('repeatEvery', function() {
var job = new Job();
it('sets the repeat interval', function() {
job.repeatEvery(5000);
expect(job.attrs.repeatInterval).to.be(5000);
});
it('returns the job', function() {
expect(job.repeatEvery('one second')).to.be(job);
});
});
it('sets the next run time', function() {
job.schedule('in 5 minutes');
expect(job.attrs.nextRunAt).to.be.a(Date);
});
it('sets the next run time Date object', function() {
var when = new Date(Date.now() + 1000*60*3);
job.schedule(when);
expect(job.attrs.nextRunAt).to.be.a(Date);
expect(job.attrs.nextRunAt.getTime()).to.eql(when.getTime());
});
it('returns the job', function() {
expect(job.schedule('tomorrow at noon')).to.be(job);
});
});
describe('priority', function() {
var job;
beforeEach(function() {
job = new Job();
describe('schedule', function() {
var job;
beforeEach(function() {
job = new Job();
});
it('sets the next run time', function() {
job.schedule('in 5 minutes');
expect(job.attrs.nextRunAt).to.be.a(Date);
});
it('sets the next run time Date object', function() {
var when = new Date(Date.now() + 1000*60*3);
job.schedule(when);
expect(job.attrs.nextRunAt).to.be.a(Date);
expect(job.attrs.nextRunAt.getTime()).to.eql(when.getTime());
});
it('returns the job', function() {
expect(job.schedule('tomorrow at noon')).to.be(job);
});
});
it('sets the priority to a number', function() {
job.priority(10);
expect(job.attrs.priority).to.be(10);
});
it('returns the job', function() {
expect(job.priority(50)).to.be(job);
});
it('parses written priorities', function() {
job.priority('high');
expect(job.attrs.priority).to.be(10);
});
});
describe('computeNextRunAt', function() {
var job;
beforeEach(function() {
job = new Job();
describe('priority', function() {
var job;
beforeEach(function() {
job = new Job();
});
it('sets the priority to a number', function() {
job.priority(10);
expect(job.attrs.priority).to.be(10);
});
it('returns the job', function() {
expect(job.priority(50)).to.be(job);
});
it('parses written priorities', function() {
job.priority('high');
expect(job.attrs.priority).to.be(10);
});
});
it('returns the job', function() {
expect(job.computeNextRunAt()).to.be(job);
});
describe('computeNextRunAt', function() {
var job;
it('sets to undefined if no repeat at', function() {
job.attrs.repeatAt = null;
job.computeNextRunAt();
expect(job.attrs.nextRunAt).to.be(undefined);
});
beforeEach(function() {
job = new Job();
});
it('it understands repeatAt times', function() {
var d = new Date();
d.setHours(23);
d.setMinutes(59);
d.setSeconds(0);
job.attrs.repeatAt = '11:59pm';
job.computeNextRunAt();
expect(job.attrs.nextRunAt.getHours()).to.be(d.getHours());
expect(job.attrs.nextRunAt.getMinutes()).to.be(d.getMinutes());
});
it('returns the job', function() {
expect(job.computeNextRunAt()).to.be(job);
});
it('sets to undefined if no repeat interval', function() {
job.attrs.repeatInterval = null;
job.computeNextRunAt();
expect(job.attrs.nextRunAt).to.be(undefined);
});
it('sets to undefined if no repeat at', function() {
job.attrs.repeatAt = null;
job.computeNextRunAt();
expect(job.attrs.nextRunAt).to.be(undefined);
});
it('it understands human intervals', function() {
var now = new Date();
job.attrs.lastRunAt = now;
job.repeatEvery('2 minutes');
job.computeNextRunAt();
expect(job.attrs.nextRunAt).to.be(now.valueOf() + 120000);
});
it('understands cron intervals', function() {
var now = new Date();
now.setMinutes(1);
now.setMilliseconds(0);
now.setSeconds(0);
job.attrs.lastRunAt = now;
job.repeatEvery('*/2 * * * *');
job.computeNextRunAt();
expect(job.attrs.nextRunAt.valueOf()).to.be(now.valueOf() + 60000);
});
describe('when repeat at time is invalid', function () {
beforeEach(function () {
try {
job.attrs.repeatAt = 'foo';
job.computeNextRunAt();
} catch(e) {}
it('it understands repeatAt times', function() {
var d = new Date();
d.setHours(23);
d.setMinutes(59);
d.setSeconds(0);
job.attrs.repeatAt = '11:59pm';
job.computeNextRunAt();
expect(job.attrs.nextRunAt.getHours()).to.be(d.getHours());
expect(job.attrs.nextRunAt.getMinutes()).to.be(d.getMinutes());
});
it('sets nextRunAt to undefined', function () {
it('sets to undefined if no repeat interval', function() {
job.attrs.repeatInterval = null;
job.computeNextRunAt();
expect(job.attrs.nextRunAt).to.be(undefined);
});
it('fails the job', function () {
expect(job.attrs.failReason).to.equal('failed to calculate repeatAt time due to invalid format');
it('it understands human intervals', function() {
var now = new Date();
job.attrs.lastRunAt = now;
job.repeatEvery('2 minutes');
job.computeNextRunAt();
expect(job.attrs.nextRunAt).to.be(now.valueOf() + 120000);
});
});
describe('when repeat interval is invalid', function () {
beforeEach(function () {
try {
job.attrs.repeatInterval = 'asd';
job.computeNextRunAt();
} catch(e) {}
it('understands cron intervals', function() {
var now = new Date();
now.setMinutes(1);
now.setMilliseconds(0);
now.setSeconds(0);
job.attrs.lastRunAt = now;
job.repeatEvery('*/2 * * * *');
job.computeNextRunAt();
expect(job.attrs.nextRunAt.valueOf()).to.be(now.valueOf() + 60000);
});
it('sets nextRunAt to undefined', function () {
expect(job.attrs.nextRunAt).to.be(undefined);
describe('when repeat at time is invalid', function () {
beforeEach(function () {
try {
job.attrs.repeatAt = 'foo';
job.computeNextRunAt();
} catch(e) {}
});
it('sets nextRunAt to undefined', function () {
expect(job.attrs.nextRunAt).to.be(undefined);
});
it('fails the job', function () {
expect(job.attrs.failReason).to.equal('failed to calculate repeatAt time due to invalid format');
});
});
it('fails the job', function () {
expect(job.attrs.failReason).to.equal('failed to calculate nextRunAt due to invalid repeat interval');
describe('when repeat interval is invalid', function () {
beforeEach(function () {
try {
job.attrs.repeatInterval = 'asd';
job.computeNextRunAt();
} catch(e) {}
});
it('sets nextRunAt to undefined', function () {
expect(job.attrs.nextRunAt).to.be(undefined);
});
it('fails the job', function () {
expect(job.attrs.failReason).to.equal('failed to calculate nextRunAt due to invalid repeat interval');
});
});
});
});
describe('remove', function() {
it('removes the job', function(done) {
var job = new Job({agenda: jobs, name: 'removed job'});
job.save(function(err) {
if(err) return done(err);
job.remove(function(err) {
describe('remove', function() {
it('removes the job', function(done) {
var job = new Job({agenda: jobs, name: 'removed job'});
job.save(function(err) {
if(err) return done(err);
mongo.collection('agendaJobs').find({_id: job.attrs._id}).toArray(function(err, j) {
expect(j).to.have.length(0);
done();
job.remove(function(err) {
if(err) return done(err);
mongo.collection('agendaJobs').find({_id: job.attrs._id}).toArray(function(err, j) {
expect(j).to.have.length(0);
done();
});
});

@@ -573,176 +606,177 @@ });

});
});
describe('run', function() {
var job,
definitions = jobs._definitions;
describe('run', function() {
var job;
jobs.define('testRun', function(job, done) {
setTimeout(function() {
done();
}, 100);
});
before(function() {
jobs.define('testRun', function(job, done) {
setTimeout(function() {
done();
}, 100);
});
});
beforeEach(function() {
job = new Job({agenda: jobs, name: 'testRun'});
});
beforeEach(function() {
job = new Job({agenda: jobs, name: 'testRun'});
});
it('updates lastRunAt', function(done) {
var now = new Date();
setTimeout(function() {
job.run(function() {
expect(job.attrs.lastRunAt.valueOf()).to.be.greaterThan(now.valueOf());
done();
});
}, 5);
});
it('updates lastRunAt', function(done) {
var now = new Date();
setTimeout(function() {
job.run(function() {
expect(job.attrs.lastRunAt.valueOf()).to.be.greaterThan(now.valueOf());
done();
});
}, 5);
});
it('fails if job is undefined', function(done) {
job = new Job({agenda: jobs, name: 'not defined'});
job.run(function() {
expect(job.attrs.failedAt).to.be.ok();
expect(job.attrs.failReason).to.be('Undefined job');
done();
});
});
it('updates nextRunAt', function(done) {
var now = new Date();
job.repeatEvery('10 minutes');
setTimeout(function() {
it('fails if job is undefined', function(done) {
job = new Job({agenda: jobs, name: 'not defined'});
job.run(function() {
expect(job.attrs.nextRunAt.valueOf()).to.be.greaterThan(now.valueOf() + 59999);
expect(job.attrs.failedAt).to.be.ok();
expect(job.attrs.failReason).to.be('Undefined job');
done();
});
}, 5);
});
it('handles errors', function(done) {
job.attrs.name = 'failBoat';
jobs.define('failBoat', function(job, cb) {
throw(new Error("Zomg fail"));
});
job.run(function(err) {
expect(err).to.be.ok();
done();
it('updates nextRunAt', function(done) {
var now = new Date();
job.repeatEvery('10 minutes');
setTimeout(function() {
job.run(function() {
expect(job.attrs.nextRunAt.valueOf()).to.be.greaterThan(now.valueOf() + 59999);
done();
});
}, 5);
});
});
it('handles errors with q promises', function(done) {
job.attrs.name = 'failBoat2';
jobs.define('failBoat2', function(job, cb) {
var Q = require('q');
Q.delay(100).then(function(){
it('handles errors', function(done) {
job.attrs.name = 'failBoat';
jobs.define('failBoat', function(job, cb) {
throw(new Error("Zomg fail"));
}).fail(cb).done();
});
job.run(function(err) {
expect(err).to.be.ok();
done();
});
});
job.run(function(err) {
expect(err).to.be.ok();
done();
it('handles errors with q promises', function(done) {
job.attrs.name = 'failBoat2';
jobs.define('failBoat2', function(job, cb) {
var Q = require('q');
Q.delay(100).then(function(){
throw(new Error("Zomg fail"));
}).fail(cb).done();
});
job.run(function(err) {
expect(err).to.be.ok();
done();
});
});
});
it('doesn\'t allow a stale job to be saved', function(done) {
var flag = false;
job.attrs.name = 'failBoat3';
job.save(function(err) {
if(err) return done(err);
jobs.define('failBoat3', function(job, cb) {
// Explicitly find the job again,
// so we have a new job object
jobs.jobs({name: 'failBoat3'}, function(err, j) {
if(err) return done(err);
j[0].remove(function(err) {
it('doesn\'t allow a stale job to be saved', function(done) {
var flag = false;
job.attrs.name = 'failBoat3';
job.save(function(err) {
if(err) return done(err);
jobs.define('failBoat3', function(job, cb) {
// Explicitly find the job again,
// so we have a new job object
jobs.jobs({name: 'failBoat3'}, function(err, j) {
if(err) return done(err);
cb();
j[0].remove(function(err) {
if(err) return done(err);
cb();
});
});
});
});
job.run(function(err) {
// Expect the deleted job to not exist in the database
jobs.jobs({name: 'failBoat3'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
job.run(function(err) {
// Expect the deleted job to not exist in the database
jobs.jobs({name: 'failBoat3'}, function(err, j) {
if(err) return done(err);
expect(j).to.have.length(0);
done();
});
});
});
});
});
});
describe('touch', function(done) {
it('extends the lock lifetime', function(done) {
var lockedAt = new Date();
var job = new Job({agenda: jobs, name: 'some job', lockedAt: lockedAt});
job.save = function(cb) { cb(); };
setTimeout(function() {
job.touch(function() {
expect(job.attrs.lockedAt).to.be.greaterThan(lockedAt);
done();
});
}, 2);
});
});
describe('fail', function() {
var job = new Job();
it('takes a string', function() {
job.fail('test');
expect(job.attrs.failReason).to.be('test');
describe('touch', function(done) {
it('extends the lock lifetime', function(done) {
var lockedAt = new Date();
var job = new Job({agenda: jobs, name: 'some job', lockedAt: lockedAt});
job.save = function(cb) { cb(); };
setTimeout(function() {
job.touch(function() {
expect(job.attrs.lockedAt).to.be.greaterThan(lockedAt);
done();
});
}, 2);
});
});
it('takes an error object', function() {
job.fail(new Error('test'));
expect(job.attrs.failReason).to.be('test');
});
it('sets the failedAt time', function() {
job.fail('test');
expect(job.attrs.failedAt).to.be.a(Date);
});
});
describe('enable', function() {
it('sets disabled to false on the job', function() {
var job = new Job({disabled: true});
job.enable();
expect(job.attrs.disabled).to.be(false);
describe('fail', function() {
var job = new Job();
it('takes a string', function() {
job.fail('test');
expect(job.attrs.failReason).to.be('test');
});
it('takes an error object', function() {
job.fail(new Error('test'));
expect(job.attrs.failReason).to.be('test');
});
it('sets the failedAt time', function() {
job.fail('test');
expect(job.attrs.failedAt).to.be.a(Date);
});
});
it('returns the job', function() {
var job = new Job({disabled: true});
expect(job.enable()).to.be(job);
});
});
describe('enable', function() {
it('sets disabled to false on the job', function() {
var job = new Job({disabled: true});
job.enable();
expect(job.attrs.disabled).to.be(false);
});
describe('disable', function() {
it('sets disabled to true on the job', function() {
var job = new Job();
job.disable();
expect(job.attrs.disabled).to.be(true);
it('returns the job', function() {
var job = new Job({disabled: true});
expect(job.enable()).to.be(job);
});
});
it('returns the job', function() {
var job = new Job();
expect(job.disable()).to.be(job);
});
});
describe('save', function() {
it('calls saveJob on the agenda', function(done) {
var oldSaveJob = jobs.saveJob;
jobs.saveJob = function() {
jobs.saveJob = oldSaveJob;
done();
};
var job = jobs.create('some job', { wee: 1});
job.save();
describe('disable', function() {
it('sets disabled to true on the job', function() {
var job = new Job();
job.disable();
expect(job.attrs.disabled).to.be(true);
});
it('returns the job', function() {
var job = new Job();
expect(job.disable()).to.be(job);
});
});
it('doesnt save the job if its been removed', function(done) {
var job = jobs.create('another job');
// Save, then remove, then try and save again.
// The second save should fail.
job.save(function(err, j) {
j.remove(function() {
j.save(function(err, res) {
jobs.jobs({name: 'another job'}, function(err, res) {
expect(res).to.have.length(0);
done();
describe('save', function() {
it('calls saveJob on the agenda', function(done) {
var oldSaveJob = jobs.saveJob;
jobs.saveJob = function() {
jobs.saveJob = oldSaveJob;
done();
};
var job = jobs.create('some job', { wee: 1});
job.save();
});
it('doesnt save the job if its been removed', function(done) {
var job = jobs.create('another job');
// Save, then remove, then try and save again.
// The second save should fail.
job.save(function(err, j) {
j.remove(function() {
j.save(function(err, res) {
jobs.jobs({name: 'another job'}, function(err, res) {
expect(res).to.have.length(0);
done();
});
});

@@ -752,370 +786,396 @@ });

});
});
it('returns the job', function() {
var job = jobs.create('some job', { wee: 1});
expect(job.save()).to.be(job);
it('returns the job', function() {
var job = jobs.create('some job', { wee: 1});
expect(job.save()).to.be(job);
});
});
});
describe("start/stop", function() {
it("starts/stops the job queue", function(done) {
jobs.define('jobQueueTest', function jobQueueTest(job, cb) {
jobs.stop(function() {
clearJobs(function() {
cb();
jobs.define('jobQueueTest', function(job, cb) {
describe("start/stop", function() {
it("starts/stops the job queue", function(done) {
jobs.define('jobQueueTest', function jobQueueTest(job, cb) {
jobs.stop(function() {
clearJobs(function() {
cb();
jobs.define('jobQueueTest', function(job, cb) {
cb();
});
done();
});
done();
});
});
jobs.every('1 second', 'jobQueueTest');
jobs.processEvery('1 second');
jobs.start();
});
jobs.every('1 second', 'jobQueueTest');
jobs.processEvery('1 second');
jobs.start();
});
it('does not run disabled jobs', function(done) {
var ran = false;
jobs.define('disabledJob', function() {
ran = true;
it('does not run disabled jobs', function(done) {
var ran = false;
jobs.define('disabledJob', function() {
ran = true;
});
var job = jobs.create('disabledJob').disable().schedule('now');
job.save(function(err) {
if (err) return done(err);
jobs.start();
setTimeout(function() {
expect(ran).to.be(false);
jobs.stop(done);
}, jobTimeout);
});
});
var job = jobs.create('disabledJob').disable().schedule('now');
job.save(function(err) {
if (err) return done(err);
it('clears locks on stop', function(done) {
jobs.define('longRunningJob', function(job, cb) {
//Job never finishes
});
jobs.every('10 seconds', 'longRunningJob');
jobs.processEvery('1 second');
jobs.start();
setTimeout(function() {
expect(ran).to.be(false);
jobs.stop(done);
jobs.stop(function(err, res) {
jobs._collection.findOne({name: 'longRunningJob'}, function(err, job) {
expect(job.lockedAt).to.be(null);
done();
});
});
}, jobTimeout);
});
});
it('clears locks on stop', function(done) {
jobs.define('longRunningJob', function(job, cb) {
//Job never finishes
});
jobs.every('10 seconds', 'longRunningJob');
jobs.processEvery('1 second');
jobs.start();
setTimeout(function() {
jobs.stop(function(err, res) {
jobs._db.findOne({name: 'longRunningJob'}, function(err, job) {
expect(job.lockedAt).to.be(null);
describe('events', function() {
beforeEach(clearJobs);
it('emits start event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('start', function(j) {
expect(j).to.be(job);
done();
});
job.run();
});
}, jobTimeout);
});
describe('events', function() {
beforeEach(clearJobs);
it('emits start event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('start', function(j) {
expect(j).to.be(job);
done();
it('emits start:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('start:jobQueueTest', function(j) {
expect(j).to.be(job);
done();
});
job.run();
});
job.run();
});
it('emits start:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('start:jobQueueTest', function(j) {
expect(j).to.be(job);
done();
it('emits complete event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('complete', function(j) {
expect(j).to.be(job);
done();
});
job.run();
});
job.run();
});
it('emits complete event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('complete', function(j) {
expect(j).to.be(job);
done();
it('emits complete:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('complete:jobQueueTest', function(j) {
expect(j).to.be(job);
done();
});
job.run();
});
job.run();
});
it('emits complete:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('complete:jobQueueTest', function(j) {
expect(j).to.be(job);
done();
it('emits success event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('success', function(j) {
expect(j).to.be.ok();
done();
});
job.run();
});
job.run();
});
it('emits success event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('success', function(j) {
expect(j).to.be.ok();
done();
it('emits success:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('success:jobQueueTest', function(j) {
expect(j).to.be.ok();
done();
});
job.run();
});
job.run();
});
it('emits success:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'jobQueueTest'});
jobs.once('success:jobQueueTest', function(j) {
expect(j).to.be.ok();
done();
it('emits fail event', function(done){
var job = new Job({agenda: jobs, name: 'failBoat'});
jobs.once('fail', function(err, j) {
expect(err.message).to.be('Zomg fail');
expect(j).to.be(job);
done();
});
job.run();
});
job.run();
});
it('emits fail event', function(done){
var job = new Job({agenda: jobs, name: 'failBoat'});
jobs.once('fail', function(err, j) {
expect(err.message).to.be('Zomg fail');
expect(j).to.be(job);
done();
it('emits fail:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'failBoat'});
jobs.once('fail:failBoat', function(err, j) {
expect(err.message).to.be('Zomg fail');
expect(j).to.be(job);
done();
});
job.run();
});
job.run();
});
it('emits fail:job name event', function(done) {
var job = new Job({agenda: jobs, name: 'failBoat'});
jobs.once('fail:failBoat', function(err, j) {
expect(err.message).to.be('Zomg fail');
expect(j).to.be(job);
done();
});
job.run();
});
});
});
describe("job lock", function(){
describe("job lock", function(){
it("runs job after a lock has expired", function(done) {
var startCounter = 0;
it("runs job after a lock has expired", function(done) {
var startCounter = 0;
jobs.define("lock job", {lockLifetime: 50}, function(job, cb){
startCounter++;
jobs.define("lock job", {lockLifetime: 50}, function(job, cb){
startCounter++;
if(startCounter != 1) {
expect(startCounter).to.be(2);
jobs.stop(done);
}
if(startCounter != 1) {
expect(startCounter).to.be(2);
jobs.stop(done);
}
});
expect(jobs._definitions["lock job"].lockLifetime).to.be(50);
jobs.defaultConcurrency(100);
jobs.processEvery(10);
jobs.every('0.02 seconds', 'lock job');
jobs.stop();
jobs.start();
});
expect(jobs._definitions["lock job"].lockLifetime).to.be(50);
jobs.defaultConcurrency(100);
jobs.processEvery(10);
jobs.every('0.02 seconds', 'lock job');
jobs.stop();
jobs.start();
});
});
describe("every running", function() {
before(function(done) {
jobs.defaultConcurrency(1);
jobs.processEvery(5);
describe("every running", function() {
before(function(done) {
jobs.defaultConcurrency(1);
jobs.processEvery(5);
jobs.stop(done);
jobs.stop(done);
});
it('should run the same job multiple times', function(done) {
var counter = 0;
});
it('should run the same job multiple times', function(done) {
var counter = 0;
jobs.define('everyRunTest1', function(job, cb) {
if(counter < 2) {
counter++;
}
cb();
});
jobs.define('everyRunTest1', function(job, cb) {
if(counter < 2) {
counter++;
}
cb();
jobs.every(10, 'everyRunTest1');
jobs.start();
setTimeout(function() {
jobs.jobs({name: 'everyRunTest1'}, function(err, res) {
expect(counter).to.be(2);
jobs.stop(done);
});
}, jobTimeout);
});
jobs.every(10, 'everyRunTest1');
it('should reuse the same job on multiple runs', function(done) {
var counter = 0;
jobs.start();
setTimeout(function() {
jobs.jobs({name: 'everyRunTest1'}, function(err, res) {
expect(counter).to.be(2);
jobs.stop(done);
jobs.define('everyRunTest2', function(job, cb) {
if(counter < 2) {
counter++;
}
cb();
});
}, jobTimeout);
});
jobs.every(10, 'everyRunTest2');
it('should reuse the same job on multiple runs', function(done) {
var counter = 0;
jobs.start();
jobs.define('everyRunTest2', function(job, cb) {
if(counter < 2) {
counter++;
}
cb();
setTimeout(function() {
jobs.jobs({name: 'everyRunTest2'}, function(err, res) {
expect(res).to.have.length(1);
jobs.stop(done);
});
}, jobTimeout);
});
jobs.every(10, 'everyRunTest2');
});
jobs.start();
describe("Integration Tests", function() {
setTimeout(function() {
jobs.jobs({name: 'everyRunTest2'}, function(err, res) {
expect(res).to.have.length(1);
jobs.stop(done);
describe('.every()', function() {
it('Should not rerun completed jobs after restart', function(done) {
var i = 0;
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == "ran" ) {
expect(i).to.be(0);
i += 1;
startService();
} else if( msg == 'notRan' ) {
expect(i).to.be(1);
done();
} else return done( new Error('Unexpected response returned!') );
};
var startService = function() {
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'daily' ] );
n.on('message', receiveMessage);
n.on('error', serviceError);
};
startService();
});
}, jobTimeout);
});
});
describe("Integration Tests", function() {
it('Should properly run jobs when defined via an array', function(done) {
var ran1 = false, ran2 = true, doneCalled = false;
describe('.every()', function() {
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == "test1-ran" ) {
ran1 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else if( msg == "test2-ran") {
ran2 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else return done( new Error('Jobs did not run!') );
};
it('Should not rerun completed jobs after restart', function(done) {
var i = 0;
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == "ran" ) {
expect(i).to.be(0);
i += 1;
startService();
} else if( msg == 'notRan' ) {
expect(i).to.be(1);
done();
} else return done( new Error('Unexpected response returned!') );
};
var startService = function() {
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'daily' ] );
var n = cp.fork( serverPath, [ mongoCfg, 'daily-array' ] );
n.on('message', receiveMessage);
n.on('error', serviceError);
};
});
startService();
});
it('should not run if job is disabled', function(done) {
var counter = 0;
it('Should properly run jobs when defined via an array', function(done) {
var ran1 = false, ran2 = true, doneCalled = false;
jobs.define('everyDisabledTest', function(job, cb) {
counter++;
cb();
});
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == "test1-ran" ) {
ran1 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else if( msg == "test2-ran") {
ran2 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else return done( new Error('Jobs did not run!') );
};
var job = jobs.every(10, 'everyDisabledTest');
jobs.start();
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'daily-array' ] );
job.disable();
n.on('message', receiveMessage);
n.on('error', serviceError);
job.save();
setTimeout(function() {
jobs.jobs({name: 'everyDisabledTest'}, function(err, res) {
expect(counter).to.be(0);
jobs.stop(done);
});
}, jobTimeout);
});
});
});
describe('schedule()', function() {
describe('schedule()', function() {
it('Should not run jobs scheduled in the future', function(done) {
var i = 0;
it('Should not run jobs scheduled in the future', function(done) {
var i = 0;
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == 'notRan' ) {
if( i < 5 ) return done();
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == 'notRan' ) {
if( i < 5 ) return done();
i += 1;
startService();
} else return done( new Error('Job scheduled in future was ran!') );
};
i += 1;
startService();
} else return done( new Error('Job scheduled in future was ran!') );
};
var startService = function() {
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'define-future-job' ] );
var startService = function() {
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'define-future-job' ] );
n.on('message', receiveMessage);
n.on('error', serviceError);
};
n.on('message', receiveMessage);
n.on('error', serviceError);
};
startService();
});
startService();
});
it('Should run past due jobs when process starts', function(done) {
it('Should run past due jobs when process starts', function(done) {
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == 'ran' ) {
done();
} else return done( new Error('Past due job did not run!') );
};
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == 'ran' ) {
done();
} else return done( new Error('Past due job did not run!') );
};
var startService = function() {
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'define-past-due-job' ] );
var startService = function() {
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'define-past-due-job' ] );
n.on('message', receiveMessage);
n.on('error', serviceError);
};
n.on('message', receiveMessage);
n.on('error', serviceError);
};
startService();
});
startService();
});
it('Should schedule using array of names', function(done) {
var ran1 = false, ran2 = false, doneCalled = false;
it('Should schedule using array of names', function(done) {
var ran1 = false, ran2 = false, doneCalled = false;
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == "test1-ran" ) {
ran1 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else if( msg == "test2-ran") {
ran2 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else return done( new Error('Jobs did not run!') );
};
if( msg == "test1-ran" ) {
ran1 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else if( msg == "test2-ran") {
ran2 = true;
if( !!ran1 && !!ran2 && !doneCalled) {
doneCalled = true;
done();
return n.send('exit');
}
} else return done( new Error('Jobs did not run!') );
};
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'schedule-array' ] );
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'schedule-array' ] );
n.on('message', receiveMessage);
n.on('error', serviceError);
});
n.on('message', receiveMessage);
n.on('error', serviceError);
});
});
describe('now()', function() {
describe('now()', function() {
it('Should immediately run the job', function(done) {
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == 'ran' ) {
return done();
} else return done( new Error("Job did not immediately run!") );
};
it('Should immediately run the job', function(done) {
var serviceError = function(e) { done(e); };
var receiveMessage = function(msg) {
if( msg == 'ran' ) {
return done();
} else return done( new Error("Job did not immediately run!") );
};
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'now' ] );
var serverPath = path.join( __dirname, 'fixtures', 'agenda-instance.js' );
var n = cp.fork( serverPath, [ mongoCfg, 'now' ] );
n.on('message', receiveMessage);
n.on('error', serviceError);
n.on('message', receiveMessage);
n.on('error', serviceError);
});
});
});
});
});

@@ -8,19 +8,22 @@ var connStr = process.argv[2];

var agenda = new Agenda({ db: { address: connStr } });
var agenda = new Agenda({ db: { address: connStr } }, function(err, collection) {
tests.forEach(function(test) {
addTests[test](agenda);
});
tests.forEach(function(test) {
addTests[test](agenda);
});
agenda.start();
agenda.start();
// Ensure we can shut down the process from tests
process.on('message', function(msg) {
if( msg == 'exit' ) process.exit(0);
// Ensure we can shut down the process from tests
process.on('message', function(msg) {
if( msg == 'exit' ) process.exit(0);
});
// Send default message of "notRan" after 400ms
setTimeout(function() {
process.send('notRan');
process.exit(0);
}, 400);
});
// Send default message of "notRan" after 200ms
setTimeout(function() {
process.send('notRan');
process.exit(0);
}, 200);
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc