pax_global_header00006660000000000000000000000064130074364120014512gustar00rootroot0000000000000052 comment=8d14e9df94450edf5dae1a16b7a8f051f26670fa orchestrator-0.3.8/000077500000000000000000000000001300743641200142415ustar00rootroot00000000000000orchestrator-0.3.8/.gitignore000066400000000000000000000001071300743641200162270ustar00rootroot00000000000000.DS_Store *.log .tmp node_modules build *.node components *.orig .idea orchestrator-0.3.8/.npmignore000066400000000000000000000001231300743641200162340ustar00rootroot00000000000000.DS_Store *.log node_modules build *.node components *.orig .idea test .travis.yml orchestrator-0.3.8/.travis.yml000066400000000000000000000001211300743641200163440ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "0.12" - "4" - "5" - "6" - "7" orchestrator-0.3.8/LICENSE000066400000000000000000000021131300743641200152430ustar00rootroot00000000000000Copyright (c) 2013 [Richardson & Sons, LLC](http://richardsonandsons.com/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. orchestrator-0.3.8/README.md000066400000000000000000000175551300743641200155350ustar00rootroot00000000000000[![Build Status](https://secure.travis-ci.org/robrich/orchestrator.svg?branch=master)](https://travis-ci.org/robrich/orchestrator) [![Dependency Status](https://david-dm.org/robrich/orchestrator.svg)](https://david-dm.org/robrich/orchestrator) Orchestrator ============ A module for sequencing and executing tasks and dependencies in maximum concurrency Usage ----- ### 1. Get a reference: ```javascript var Orchestrator = require('orchestrator'); var orchestrator = new Orchestrator(); ``` ### 2. Load it up with stuff to do: ```javascript orchestrator.add('thing1', function(){ // do stuff }); orchestrator.add('thing2', function(){ // do stuff }); ``` ### 3. Run the tasks: ```javascript orchestrator.start('thing1', 'thing2', function (err) { // all done }); ``` API --- ### orchestrator.add(name[, deps][, function]); Define a task ```javascript orchestrator.add('thing1', function(){ // do stuff }); ``` #### name Type: `String` The name of the task. #### deps Type: `Array` An array of task names to be executed and completed before your task will run. ```javascript orchestrator.add('mytask', ['array', 'of', 'task', 'names'], function() { // Do stuff }); ``` **Note:** Are your tasks running before the dependencies are complete? Make sure your dependency tasks are correctly using the async run hints: take in a callback or return a promise or event stream. #### fn Type: `function` The function that performs the task's operations. For asynchronous tasks, you need to provide a hint when the task is complete: - Take in a callback - Return a stream or a promise #### examples: **Accept a callback:** ```javascript orchestrator.add('thing2', function(callback){ // do stuff callback(err); }); ``` **Return a promise:** ```javascript var Q = require('q'); orchestrator.add('thing3', function(){ var deferred = Q.defer(); // do async stuff setTimeout(function () { deferred.resolve(); }, 1); return deferred.promise; }); ``` **Return a stream:** (task is marked complete when stream ends) ```javascript var map = require('map-stream'); orchestrator.add('thing4', function(){ var stream = map(function (args, cb) { cb(null, args); }); // do stream stuff return stream; }); ``` **Note:** By default, tasks run with maximum concurrency -- e.g. it launches all the tasks at once and waits for nothing. If you want to create a series where tasks run in a particular order, you need to do two things: - give it a hint to tell it when the task is done, - and give it a hint that a task depends on completion of another. For these examples, let's presume you have two tasks, "one" and "two" that you specifically want to run in this order: 1. In task "one" you add a hint to tell it when the task is done. Either take in a callback and call it when you're done or return a promise or stream that the engine should wait to resolve or end respectively. 2. In task "two" you add a hint telling the engine that it depends on completion of the first task. So this example would look like this: ```javascript var Orchestrator = require('orchestrator'); var orchestrator = new Orchestrator(); // takes in a callback so the engine knows when it'll be done orchestrator.add('one', function (cb) { // do stuff -- async or otherwise cb(err); // if err is not null or undefined, the orchestration will stop, and note that it failed }); // identifies a dependent task must be complete before this one begins orchestrator.add('two', ['one'], function () { // task 'one' is done now }); orchestrator.start('one', 'two'); ``` ### orchestrator.hasTask(name); Have you defined a task with this name? #### name Type: `String` The task name to query ### orchestrator.start(tasks...[, cb]); Start running the tasks #### tasks Type: `String` or `Array` of `String`s Tasks to be executed. You may pass any number of tasks as individual arguments. #### cb Type: `function`: `function (err) {` Callback to call after run completed. Passes single argument: `err`: did the orchestration succeed? **Note:** Tasks run concurrently and therefore may not complete in order. **Note:** Orchestrator uses `sequencify` to resolve dependencies before running, and therefore may not start in order. Listen to orchestration events to watch task running. ```javascript orchestrator.start('thing1', 'thing2', 'thing3', 'thing4', function (err) { // all done }); ``` ```javascript orchestrator.start(['thing1','thing2'], ['thing3','thing4']); ``` **FRAGILE:** Orchestrator catches exceptions on sync runs to pass to your callback but doesn't hook to process.uncaughtException so it can't pass those exceptions to your callback **FRAGILE:** Orchestrator will ensure each task and each dependency is run once during an orchestration run even if you specify it to run more than once. (e.g. `orchestrator.start('thing1', 'thing1')` will only run 'thing1' once.) If you need it to run a task multiple times, wait for the orchestration to end (start's callback) then call start again. (e.g. `orchestrator.start('thing1', function () {orchestrator.start('thing1');})`.) Alternatively create a second orchestrator instance. ### orchestrator.stop() Stop an orchestration run currently in process **Note:** It will call the `start()` callback with an `err` noting the orchestration was aborted ### orchestrator.on(event, cb); Listen to orchestrator internals #### event Type: `String` Event name to listen to: - start: from start() method, shows you the task sequence - stop: from stop() method, the queue finished successfully - err: from stop() method, the queue was aborted due to a task error - task_start: from _runTask() method, task was started - task_stop: from _runTask() method, task completed successfully - task_err: from _runTask() method, task errored - task_not_found: from start() method, you're trying to start a task that doesn't exist - task_recursion: from start() method, there are recursive dependencies in your task list #### cb Type: `function`: `function (e) {` Passes single argument: `e`: event details ```javascript orchestrator.on('task_start', function (e) { // e.message is the log message // e.task is the task name if the message applies to a task else `undefined` // e.err is the error if event is 'err' else `undefined` }); // for task_end and task_err: orchestrator.on('task_stop', function (e) { // e is the same object from task_start // e.message is updated to show how the task ended // e.duration is the task run duration (in seconds) }); ``` **Note:** fires either *stop or *err but not both. ### orchestrator.onAll(cb); Listen to all orchestrator events from one callback #### cb Type: `function`: `function (e) {` Passes single argument: `e`: event details ```javascript orchestrator.onAll(function (e) { // e is the original event args // e.src is event name }); ``` LICENSE ------- (MIT License) Copyright (c) 2013-2015 [Richardson & Sons, LLC](http://richardsonandsons.com/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. orchestrator-0.3.8/index.js000066400000000000000000000200541300743641200157070ustar00rootroot00000000000000/*jshint node:true */ "use strict"; var util = require('util'); var events = require('events'); var EventEmitter = events.EventEmitter; var runTask = require('./lib/runTask'); var Orchestrator = function () { EventEmitter.call(this); this.doneCallback = undefined; // call this when all tasks in the queue are done this.seq = []; // the order to run the tasks this.tasks = {}; // task objects: name, dep (list of names of dependencies), fn (the task to run) this.isRunning = false; // is the orchestrator running tasks? .start() to start, .stop() to stop }; util.inherits(Orchestrator, EventEmitter); Orchestrator.prototype.reset = function () { if (this.isRunning) { this.stop(null); } this.tasks = {}; this.seq = []; this.isRunning = false; this.doneCallback = undefined; return this; }; Orchestrator.prototype.add = function (name, dep, fn) { if (!fn && typeof dep === 'function') { fn = dep; dep = undefined; } dep = dep || []; fn = fn || function () {}; // no-op if (!name) { throw new Error('Task requires a name'); } // validate name is a string, dep is an array of strings, and fn is a function if (typeof name !== 'string') { throw new Error('Task requires a name that is a string'); } if (typeof fn !== 'function') { throw new Error('Task '+name+' requires a function that is a function'); } if (!Array.isArray(dep)) { throw new Error('Task '+name+' can\'t support dependencies that is not an array of strings'); } dep.forEach(function (item) { if (typeof item !== 'string') { throw new Error('Task '+name+' dependency '+item+' is not a string'); } }); this.tasks[name] = { fn: fn, dep: dep, name: name }; return this; }; Orchestrator.prototype.task = function (name, dep, fn) { if (dep || fn) { // alias for add, return nothing rather than this this.add(name, dep, fn); } else { return this.tasks[name]; } }; Orchestrator.prototype.hasTask = function (name) { return !!this.tasks[name]; }; // tasks and optionally a callback Orchestrator.prototype.start = function() { var args, arg, names = [], lastTask, i, seq = []; args = Array.prototype.slice.call(arguments, 0); if (args.length) { lastTask = args[args.length-1]; if (typeof lastTask === 'function') { this.doneCallback = lastTask; args.pop(); } for (i = 0; i < args.length; i++) { arg = args[i]; if (typeof arg === 'string') { names.push(arg); } else if (Array.isArray(arg)) { names = names.concat(arg); // FRAGILE: ASSUME: it's an array of strings } else { throw new Error('pass strings or arrays of strings'); } } } if (this.isRunning) { // reset specified tasks (and dependencies) as not run this._resetSpecificTasks(names); } else { // reset all tasks as not run this._resetAllTasks(); } if (this.isRunning) { // if you call start() again while a previous run is still in play // prepend the new tasks to the existing task queue names = names.concat(this.seq); } if (names.length < 1) { // run all tasks for (i in this.tasks) { if (this.tasks.hasOwnProperty(i)) { names.push(this.tasks[i].name); } } } seq = []; try { this.sequence(this.tasks, names, seq, []); } catch (err) { // Is this a known error? if (err) { if (err.missingTask) { this.emit('task_not_found', {message: err.message, task:err.missingTask, err: err}); } if (err.recursiveTasks) { this.emit('task_recursion', {message: err.message, recursiveTasks:err.recursiveTasks, err: err}); } } this.stop(err); return this; } this.seq = seq; this.emit('start', {message:'seq: '+this.seq.join(',')}); if (!this.isRunning) { this.isRunning = true; } this._runStep(); return this; }; Orchestrator.prototype.stop = function (err, successfulFinish) { this.isRunning = false; if (err) { this.emit('err', {message:'orchestration failed', err:err}); } else if (successfulFinish) { this.emit('stop', {message:'orchestration succeeded'}); } else { // ASSUME err = 'orchestration aborted'; this.emit('err', {message:'orchestration aborted', err: err}); } if (this.doneCallback) { // Avoid calling it multiple times this.doneCallback(err); } else if (err && !this.listeners('err').length) { // No one is listening for the error so speak louder throw err; } }; Orchestrator.prototype.sequence = require('sequencify'); Orchestrator.prototype.allDone = function () { var i, task, allDone = true; // nothing disputed it yet for (i = 0; i < this.seq.length; i++) { task = this.tasks[this.seq[i]]; if (!task.done) { allDone = false; break; } } return allDone; }; Orchestrator.prototype._resetTask = function(task) { if (task) { if (task.done) { task.done = false; } delete task.start; delete task.stop; delete task.duration; delete task.hrDuration; delete task.args; } }; Orchestrator.prototype._resetAllTasks = function() { var task; for (task in this.tasks) { if (this.tasks.hasOwnProperty(task)) { this._resetTask(this.tasks[task]); } } }; Orchestrator.prototype._resetSpecificTasks = function (names) { var i, name, t; if (names && names.length) { for (i = 0; i < names.length; i++) { name = names[i]; t = this.tasks[name]; if (t) { this._resetTask(t); if (t.dep && t.dep.length) { this._resetSpecificTasks(t.dep); // recurse } //} else { // FRAGILE: ignore that the task doesn't exist } } } }; Orchestrator.prototype._runStep = function () { var i, task; if (!this.isRunning) { return; // user aborted, ASSUME: stop called previously } for (i = 0; i < this.seq.length; i++) { task = this.tasks[this.seq[i]]; if (!task.done && !task.running && this._readyToRunTask(task)) { this._runTask(task); } if (!this.isRunning) { return; // task failed or user aborted, ASSUME: stop called previously } } if (this.allDone()) { this.stop(null, true); } }; Orchestrator.prototype._readyToRunTask = function (task) { var ready = true, // no one disproved it yet i, name, t; if (task.dep.length) { for (i = 0; i < task.dep.length; i++) { name = task.dep[i]; t = this.tasks[name]; if (!t) { // FRAGILE: this should never happen this.stop("can't run "+task.name+" because it depends on "+name+" which doesn't exist"); ready = false; break; } if (!t.done) { ready = false; break; } } } return ready; }; Orchestrator.prototype._stopTask = function (task, meta) { task.duration = meta.duration; task.hrDuration = meta.hrDuration; task.running = false; task.done = true; }; Orchestrator.prototype._emitTaskDone = function (task, message, err) { if (!task.args) { task.args = {task:task.name}; } task.args.duration = task.duration; task.args.hrDuration = task.hrDuration; task.args.message = task.name+' '+message; var evt = 'stop'; if (err) { task.args.err = err; evt = 'err'; } // 'task_stop' or 'task_err' this.emit('task_'+evt, task.args); }; Orchestrator.prototype._runTask = function (task) { var that = this; task.args = {task:task.name, message:task.name+' started'}; this.emit('task_start', task.args); task.running = true; runTask(task.fn.bind(this), function (err, meta) { that._stopTask.call(that, task, meta); that._emitTaskDone.call(that, task, meta.runMethod, err); if (err) { return that.stop.call(that, err); } that._runStep.call(that); }); }; // FRAGILE: ASSUME: this list is an exhaustive list of events emitted var events = ['start','stop','err','task_start','task_stop','task_err','task_not_found','task_recursion']; var listenToEvent = function (target, event, callback) { target.on(event, function (e) { e.src = event; callback(e); }); }; Orchestrator.prototype.onAll = function (callback) { var i; if (typeof callback !== 'function') { throw new Error('No callback specified'); } for (i = 0; i < events.length; i++) { listenToEvent(this, events[i], callback); } }; module.exports = Orchestrator; orchestrator-0.3.8/lib/000077500000000000000000000000001300743641200150075ustar00rootroot00000000000000orchestrator-0.3.8/lib/runTask.js000066400000000000000000000027521300743641200170020ustar00rootroot00000000000000/*jshint node:true */ "use strict"; var eos = require('end-of-stream'); var consume = require('stream-consume'); module.exports = function (task, done) { var that = this, finish, cb, isDone = false, start, r; finish = function (err, runMethod) { var hrDuration = process.hrtime(start); if (isDone && !err) { err = new Error('task completion callback called too many times'); } isDone = true; var duration = hrDuration[0] + (hrDuration[1] / 1e9); // seconds done.call(that, err, { duration: duration, // seconds hrDuration: hrDuration, // [seconds,nanoseconds] runMethod: runMethod }); }; cb = function (err) { finish(err, 'callback'); }; try { start = process.hrtime(); r = task(cb); } catch (err) { return finish(err, 'catch'); } if (r && typeof r.then === 'function') { // wait for promise to resolve // FRAGILE: ASSUME: Promises/A+, see http://promises-aplus.github.io/promises-spec/ r.then(function () { finish(null, 'promise'); }, function(err) { finish(err, 'promise'); }); } else if (r && typeof r.pipe === 'function') { // wait for stream to end eos(r, { error: true, readable: r.readable, writable: r.writable && !r.readable }, function(err){ finish(err, 'stream'); }); // Ensure that the stream completes consume(r); } else if (task.length === 0) { // synchronous, function took in args.length parameters, and the callback was extra finish(null, 'sync'); //} else { // FRAGILE: ASSUME: callback } }; orchestrator-0.3.8/package.json000066400000000000000000000015351300743641200165330ustar00rootroot00000000000000{ "name": "orchestrator", "description": "A module for sequencing and executing tasks and dependencies in maximum concurrency", "version": "0.3.8", "homepage": "https://github.com/robrich/orchestrator", "repository": "git://github.com/robrich/orchestrator.git", "author": "Rob Richardson (http://robrich.org/)", "main": "./index.js", "keywords": [ "async", "task", "parallel", "compose" ], "dependencies": { "end-of-stream": "~0.1.5", "sequencify": "~0.0.7", "stream-consume": "~0.1.0" }, "devDependencies": { "event-stream": "~3.3.4", "gulp-uglify": "^2.0.0", "jshint": "^2.9.4", "map-stream": "~0.0.6", "merge-stream": "~1.0.0", "mocha": "~3.1.2", "q": "~1.4.1", "should": "~11.1.1", "vinyl-fs": "~2.4.4" }, "scripts": { "test": "mocha" }, "license": "MIT" } orchestrator-0.3.8/test/000077500000000000000000000000001300743641200152205ustar00rootroot00000000000000orchestrator-0.3.8/test/add.js000066400000000000000000000045351300743641200163150ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('add()', function() { it('should define a task', function(done) { var orchestrator, fn; // Arrange fn = function() {}; // Act orchestrator = new Orchestrator(); orchestrator.add('test', fn); // Assert should.exist(orchestrator.tasks.test); orchestrator.tasks.test.fn.should.equal(fn); done(); }); var failTest = function (one, two, three) { var orchestrator, actualErr; // Arrange orchestrator = new Orchestrator(); // Act try { orchestrator.add(one, two, three); } catch (err) { actualErr = err; } // Assert should.exist(actualErr); should.ok(actualErr.message.indexOf('Task') > -1); }; it('should error if name is not a string', function (done) { var name, fn; // Arrange name = 9; // not a string fn = function () {}; // Act & Assert failTest(name, fn); done(); }); it('should error if dep is not an array', function (done) { var name, dep, fn; // Arrange name = "name"; dep = 9; // not an array fn = function () {}; // Act & Assert failTest(name, dep, fn); done(); }); it('should error if dep contains a non-string', function (done) { var name, dep, fn; // Arrange name = "name"; dep = 9; // not an array fn = function () {}; // Act & Assert failTest(name, dep, fn); done(); }); it('should error if fn is not a function', function (done) { var name, fn; // Arrange name = "name"; fn = 9; // not a function // Act & Assert failTest(name, fn); done(); }); it('should error if fn is not a function and there are dependencies', function (done) { var name, dep, fn; // Arrange name = "name"; dep = ['name']; fn = 9; // not a function // Act & Assert failTest(name, dep, fn); done(); }); it('should accept dependencies with no function', function (done) { var orchestrator, name, dep; // Arrange name = "name"; dep = ['name']; // Act orchestrator = new Orchestrator(); orchestrator.add(name, dep); // Assert should.exist(orchestrator.tasks.name); orchestrator.tasks.name.dep.should.equal(dep); done(); }); }); }); orchestrator-0.3.8/test/doneCallback.js000066400000000000000000000116171300743641200201260ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var Q = require('q'); var fs = require('fs'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('stop() callback', function() { it('should have empty error on succeeding tasks', function(done) { var orchestrator, a; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function() { ++a; }); // Act orchestrator.start('test', function(err) { // Assert a.should.equal(1); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error on fail by callback tasks', function(done) { var orchestrator, a, expectedErr = {message:'the error'}; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function (cb) { ++a; cb(expectedErr); }); // Act orchestrator.start('test', function(err) { // Assert a.should.equal(1); should.exist(err); should.deepEqual(err, expectedErr); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error on sync throw', function(done) { var orchestrator, a, errMess = 'the error message'; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function () { ++a; throw new Error(errMess); }); // Act orchestrator.start('test', function(err) { // Assert a.should.equal(1); should.exist(err); err.message.should.equal(errMess); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error on promise rejected', function(done) { var orchestrator, a, expectedErr = 'the error message'; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function () { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.reject(expectedErr); },1); return deferred.promise; }); // Act orchestrator.start('test', function(actualErr) { // Assert a.should.equal(1); should.exist(actualErr); actualErr.should.equal(expectedErr); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error on stream error', function(done) { var orchestrator; // Arrange orchestrator = new Orchestrator(); orchestrator.add('test', function () { return fs.createReadStream('./index.js') .pipe(fs.createWriteStream('./something/that/does/not/exist')); }); // Act orchestrator.start('test', function(actualErr) { // Assert should.exist(actualErr); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error on missing task', function(done) { var orchestrator, name, a, expectedErr; // Arrange name = 'test'; a = 0; orchestrator = new Orchestrator(); // no 'test' task defined // Act orchestrator.on('task_not_found', function (e) { a++; e.task.should.equal(name); e.message.should.match(/not defined/i, e.message+' should include not defined'); should.exist(e.err); expectedErr = e.err; }); orchestrator.start(name, function(actualErr) { // Assert a.should.equal(1); should.exist(actualErr); actualErr.should.equal(expectedErr); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error on recursive tasks', function(done) { var orchestrator, name, a, expectedErr; // Arrange name = 'test'; a = 0; orchestrator = new Orchestrator(); orchestrator.add(name, [name]); // Act orchestrator.on('task_recursion', function (e) { a++; e.recursiveTasks.length.should.equal(2); e.recursiveTasks[0].should.equal(name); e.recursiveTasks[1].should.equal(name); e.message.should.match(/recursive/i, e.message+' should include recursive'); should.exist(e.err); expectedErr = e.err; }); orchestrator.start(name, function(actualErr) { // Assert a.should.equal(1); should.exist(actualErr); actualErr.should.equal(expectedErr); orchestrator.isRunning.should.equal(false); done(); }); }); it('should have error when calling callback too many times', function(done) { var orchestrator, a, timeout = 30; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function (cb) { cb(null); cb(null); }); // Act orchestrator.start('test', function(err) { // Assert switch (a) { case 0: // first finish should.not.exist(err); break; case 1: // second finish should.exist(err); err.message.indexOf('too many times').should.be.above(-1); break; default: done('finished too many times'); } a++; }); setTimeout(function () { a.should.equal(2); done(); }, timeout); }); }); }); orchestrator-0.3.8/test/emitTaskDone.js000066400000000000000000000051551300743641200201530ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_emitTaskDone()', function() { it('should emit task_stop if no err', function(done) { var orchestrator, taskName, task, message, err; // Arrange taskName = 'test'; message = 'test message'; task = { name: taskName, fn: function() {} }; //err = undefined; // Thing under test orchestrator = new Orchestrator(); orchestrator.on('task_stop', function (/*args*/) { // Assert done(); }); // Act orchestrator._emitTaskDone(task, message, err); }); it('should emit task_err if err', function(done) { var orchestrator, taskName, task, message, err; // Arrange taskName = 'test'; message = 'test message'; task = { name: taskName, fn: function() {} }; err = 'the error'; // Thing under test orchestrator = new Orchestrator(); orchestrator.on('task_err', function (/*args*/) { // Assert done(); }); // Act orchestrator._emitTaskDone(task, message, err); }); it('should pass task, message, duration', function(done) { var orchestrator, taskName, task, message, duration, err; // Arrange taskName = 'test'; message = 'test message'; duration = 1.1; task = { name: taskName, fn: function() {}, duration: duration }; //err = undefined; // Thing under test orchestrator = new Orchestrator(); orchestrator.on('task_stop', function (args) { // Assert args.task.should.equal(taskName); args.duration.should.equal(duration); args.message.should.equal(taskName+' '+message); done(); }); // Act orchestrator._emitTaskDone(task, message, err); }); it('should pass err', function(done) { var orchestrator, taskName, task, message, err; // Arrange taskName = 'test'; message = 'test message'; task = { name: taskName, fn: function() {} }; err = 'the error'; // Thing under test orchestrator = new Orchestrator(); orchestrator.on('task_err', function (args) { // Assert args.err.should.equal(err); done(); }); // Act orchestrator._emitTaskDone(task, message, err); }); it('should die if no task passed', function(done) { // Arrange var orchestrator = new Orchestrator(); var succeed = false; // Act try { orchestrator._emitTaskDone(); succeed = true; } catch (err) { succeed = false; } // Assert succeed.should.equal(false); done(); }); }); }); orchestrator-0.3.8/test/gulpTask.js000066400000000000000000000111101300743641200173420ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ 'use strict'; var Orchestrator = require('../'); var Q = require('q'); var vfs = require('vinyl-fs'); var es = require('event-stream'); var ms = require('merge-stream'); var uglify = require('gulp-uglify'); var should = require('should'); require('mocha'); describe('orchestrator tasks integration tests', function() { var gulp = new Orchestrator(); describe('task()', function() { it('should define a task', function(done) { var fn; fn = function() {}; gulp.task('test', fn); should.exist(gulp.tasks.test); gulp.tasks.test.fn.should.equal(fn); gulp.reset(); done(); }); }); describe('start()', function() { it('should run multiple tasks', function(done) { var a, fn, fn2; a = 0; fn = function() { this.should.equal(gulp); ++a; }; fn2 = function() { this.should.equal(gulp); ++a; }; gulp.task('test', fn); gulp.task('test2', fn2); gulp.start('test', 'test2'); a.should.equal(2); gulp.reset(); done(); }); it('should run all tasks when call start() multiple times', function(done) { var a, fn, fn2; a = 0; fn = function() { this.should.equal(gulp); ++a; }; fn2 = function() { this.should.equal(gulp); ++a; }; gulp.task('test', fn); gulp.task('test2', fn2); gulp.start('test'); gulp.start('test2'); a.should.equal(2); gulp.reset(); done(); }); it('should run all async promise tasks', function(done) { var a, fn, fn2; a = 0; fn = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); },1); return deferred.promise; }; fn2 = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); },1); return deferred.promise; }; gulp.task('test', fn); gulp.task('test2', fn2); gulp.start('test'); gulp.start('test2', function () { gulp.isRunning.should.equal(false); a.should.equal(2); gulp.reset(); done(); }); gulp.isRunning.should.equal(true); }); it('should run all async callback tasks', function(done) { var a, fn, fn2; a = 0; fn = function(cb) { setTimeout(function () { ++a; cb(null); },1); }; fn2 = function(cb) { setTimeout(function () { ++a; cb(null); },1); }; gulp.task('test', fn); gulp.task('test2', fn2); gulp.start('test'); gulp.start('test2', function () { gulp.isRunning.should.equal(false); a.should.equal(2); gulp.reset(); done(); }); gulp.isRunning.should.equal(true); }); it('should run all async stream tasks', function(done){ var a, fn, fn2, fn3, fn4; a = 0; fn = function(cb) { return vfs.src('./index.js') .pipe(uglify()); }; fn2 = function(cb) { var stream1 = vfs.src('./index.js') .pipe(vfs.dest('./test/.tmp')); var stream2 = vfs.src('./index.js') .pipe(vfs.dest('./test/.tmp')); return es.concat(stream1, stream2); }; fn3 = function(cb) { var stream1 = vfs.src('./index.js') .pipe(vfs.dest('./test/.tmp')); var stream2 = vfs.src('./index.js') .pipe(vfs.dest('./test/.tmp')); return ms(stream1, stream2); }; fn4 = function(cb) { return vfs.src('./index.js') .pipe(vfs.dest('./test/.tmp')); }; gulp.task('test', fn); gulp.task('test2', fn2); gulp.task('test3', fn3); gulp.task('test4', fn4); gulp.on('task_stop', function(){ ++a; }); gulp.start('test'); gulp.start('test2'); gulp.start('test3'); gulp.start('test4', function () { gulp.isRunning.should.equal(false); a.should.equal(4); done(); }); }); it('should emit task_not_found and throw an error when task is not defined', function(done) { gulp.reset(); var aErr; gulp.on('task_not_found', function(err){ should.exist(err); should.exist(err.task); err.task.should.equal('test'); gulp.reset(); done(); }); try { gulp.start('test'); } catch (err) { aErr = err; } should.exist(aErr); }); }); }); orchestrator-0.3.8/test/hasTask.js000066400000000000000000000021621300743641200171550ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); require('should'); require('mocha'); describe('orchestrator', function() { describe('hasTask()', function() { it('should return true if there is a task', function(done) { var orchestrator, name, task1, expected, actual; // Arrange name = 'task1'; task1 = { name: name, fn: function() {} }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks[name] = task1; // Act expected = true; actual = orchestrator.hasTask(name); // Assert actual.should.equal(expected); done(); }); it('should return false if there is no such task', function(done) { var orchestrator, name, task1, expected, actual; // Arrange name = 'task1'; task1 = { name: name, fn: function() {} }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks[name] = task1; // Act expected = false; actual = orchestrator.hasTask('not'+name); // Assert actual.should.equal(expected); done(); }); }); }); orchestrator-0.3.8/test/onAll.js000066400000000000000000000017231300743641200166260ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('onAll()', function() { it('should wire up event listener', function(done) { var orchestrator, cb, a = 0; // Arrange cb = function() { ++a; }; orchestrator = new Orchestrator(); // Act orchestrator.onAll(cb); orchestrator.emit('start', {}); // fake do action that would fire event // Assert a.should.equal(1); done(); }); it('should add src to event args', function(done) { var orchestrator, cb, actualE; // Arrange cb = function(e) { actualE = e; }; orchestrator = new Orchestrator(); // Act orchestrator.onAll(cb); orchestrator.emit('stop', {}); // fake do action that would fire event // Assert should.exist(actualE); actualE.src.should.equal('stop'); done(); }); }); }); orchestrator-0.3.8/test/readyToRunTask.js000066400000000000000000000043031300743641200204750ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_readyToRunTask() task is ready when dependencies are resolved', function() { it('should be ready if no dependencies', function(done) { var orchestrator, task, expected, actual; // Arrange expected = true; task = { name: 'a', dep: [] }; // Act orchestrator = new Orchestrator(); orchestrator.tasks = { a: task }; actual = orchestrator._readyToRunTask(task); // Assert actual.should.equal(expected); done(); }); it('should be ready if dependency is done', function(done) { var orchestrator, task, dep, expected, actual; // Arrange expected = true; task = { name: 'a', dep: ['b'] }; dep = { name: 'b', dep: [], done: true }; // Act orchestrator = new Orchestrator(); orchestrator.tasks = { a: task, b: dep }; actual = orchestrator._readyToRunTask(task); // Assert actual.should.equal(expected); done(); }); it('should not be ready if dependency is not done', function(done) { var orchestrator, task, dep, expected, actual; // Arrange expected = false; task = { name: 'a', dep: ['b'] }; dep = { name: 'b', dep: [] //done: lack of var is falsey }; // Act orchestrator = new Orchestrator(); orchestrator.tasks = { a: task, b: dep }; actual = orchestrator._readyToRunTask(task); // Assert actual.should.equal(expected); done(); }); it('should stop() if dependency is missing', function(done) { var orchestrator, task, cb, expected, actual, expectedErr; // Arrange expected = false; task = { name: 'a', dep: ['b'] // which doesn't exist }; cb = function (err) { expectedErr = err; }; // Act orchestrator = new Orchestrator(); orchestrator.tasks = { a: task }; orchestrator.doneCallback = cb; actual = orchestrator._readyToRunTask(task); // Assert should.exist(expectedErr); expectedErr.indexOf('exist').should.be.above(-1); actual.should.equal(expected); done(); }); }); }); orchestrator-0.3.8/test/resetAllTasks.js000066400000000000000000000025101300743641200203350ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_resetAllTasks()', function() { it('should set done = false on all done tasks', function(done) { var orchestrator, task1, task2; // Arrange task1 = { name: 'test1', fn: function() {}, done: true }; task2 = { name: 'test2', fn: function() {}, done: true }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { task1: task1, task2: task2 }; // Act orchestrator._resetAllTasks(); // Assert task1.done.should.equal(false); task2.done.should.equal(false); done(); }); it('should not set done = false if done does not exist', function(done) { var orchestrator, task1, task2; // Arrange task1 = { name: 'test1', fn: function() {} // no done }; task2 = { name: 'test2', fn: function() {} // no done }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { task1: task1, task2: task2 }; // Act orchestrator._resetAllTasks(); // Assert should.not.exist(task1.done); should.not.exist(task2.done); done(); }); }); }); orchestrator-0.3.8/test/resetSpecificTasks.js000066400000000000000000000057471300743641200213710ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_resetSpecificTasks()', function() { it('should set done = false on specified done tasks', function(done) { var orchestrator, task1, task2; // Arrange task1 = { name: 'test1', fn: function() {}, done: true }; task2 = { name: 'test2', fn: function() {}, done: true }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { test1: task1, test2: task2 }; // Act orchestrator._resetSpecificTasks(['test1','test2']); // Assert task1.done.should.equal(false); task2.done.should.equal(false); done(); }); it('should not set done = false if done does not exist', function(done) { var orchestrator, task1, task2; // Arrange task1 = { name: 'test1', fn: function() {} // no done }; task2 = { name: 'test2', fn: function() {} // no done }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { test1: task1, test2: task2 }; // Act orchestrator._resetSpecificTasks(['test1','test2']); // Assert should.not.exist(task1.done); should.not.exist(task2.done); done(); }); it('should set done = false on done dependency', function(done) { var orchestrator, task, dep; // Arrange task = { name: 'test', dep: ['dep'], fn: function() {} // no done though irrelevant to current test }; dep = { name: 'dep', fn: function() {}, done: true }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { test: task, dep: dep }; // Act orchestrator._resetSpecificTasks(['test']); // Assert dep.done.should.equal(false); done(); }); it('should not set done on irrelevant tasks', function(done) { var orchestrator, task, irrelevant; // Arrange task = { name: 'test', fn: function() {} // no done though irrelevant to current test }; irrelevant = { name: 'irrelevant', fn: function() {}, done: true }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { test: task, irrelevant: irrelevant }; // Act orchestrator._resetSpecificTasks(['test']); // Assert irrelevant.done.should.equal(true); done(); }); it('should not die if dependency does not exist', function(done) { var orchestrator, task; // Arrange task = { name: 'test', dep: ['dep'], // dep doesn't exist fn: function() {} // no done though irrelevant to current test }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks = { test: task }; // Act orchestrator._resetSpecificTasks(['test']); // Assert // we're still here so it worked done(); }); }); }); orchestrator-0.3.8/test/resetTask.js000066400000000000000000000050051300743641200175230ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_resetTask()', function() { it('should set done = false on done task', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() {}, done: true }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._resetTask(task); // Assert task.done.should.equal(false); done(); }); it('should not set done = false if done does not exist', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() {} // no done }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._resetTask(task); // Assert should.not.exist(task.done); done(); }); it('should remove start', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() {}, start: new Date() }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._resetTask(task); // Assert should.not.exist(task.start); done(); }); it('should remove stop', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() {}, stop: new Date() }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._resetTask(task); // Assert should.not.exist(task.stop); done(); }); it('should remove duration', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() {}, duration: new Date() }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._resetTask(task); // Assert should.not.exist(task.duration); done(); }); it('should remove args', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() {}, args: {} }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._resetTask(task); // Assert should.not.exist(task.args); done(); }); it('should not die if not passed a task', function(done) { // Arrange var orchestrator = new Orchestrator(); // Act orchestrator._resetTask(); // Assert // we're still here so it worked done(); }); }); }); orchestrator-0.3.8/test/runTask.js000066400000000000000000000036321300743641200172110ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_runTask() tasks execute as expected', function() { it('calls task function', function(done) { var orchestrator, a, task; // Arrange a = 0; task = { name: 'test', fn: function() { ++a; } }; // Act orchestrator = new Orchestrator(); orchestrator._runTask(task); // Assert a.should.equal(1); done(); }); it('sets .running correctly', function(done) { var orchestrator, task; // Arrange task = { name: 'test', fn: function() { should.exist(task.running); task.running.should.equal(true); } }; // Act orchestrator = new Orchestrator(); orchestrator._runTask(task); // Assert should.exist(task.running); task.running.should.equal(false); done(); }); it('logs start', function(done) { var orchestrator, task, a = 0; // Arrange task = { name: 'test', fn: function() { } }; // Act orchestrator = new Orchestrator(); orchestrator.on('task_start', function (e) { should.exist(e.task); e.task.should.equal('test'); ++a; }); orchestrator._runTask(task); // Assert a.should.equal(1); done(); }); it('passes start args to stop event', function(done) { var orchestrator, task, passthrough, a = 0; // Arrange task = { name: 'test', fn: function() { } }; passthrough = 'passthrough'; // Act orchestrator = new Orchestrator(); orchestrator.on('task_start', function (e) { e.passthrough = passthrough; ++a; }); orchestrator.on('task_stop', function (e) { e.passthrough.should.equal(passthrough); ++a; }); orchestrator._runTask(task); // Assert a.should.equal(2); done(); }); }); }); orchestrator-0.3.8/test/start.js000066400000000000000000000153771300743641200167300ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var Q = require('q'); require('should'); require('mocha'); describe('orchestrator', function() { describe('run() tasks', function() { it('should run multiple tasks', function(done) { var orchestrator, a, fn, fn2; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; fn2 = function() { ++a; }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); // Act orchestrator.start('test', 'test2'); // Assert a.should.equal(2); done(); }); it('should run multiple tasks as array', function(done) { var orchestrator, a, fn, fn2, fn3; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; fn2 = function() { ++a; }; fn3 = function() { throw new Error('run wrong task'); }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); orchestrator.add('test3', fn3); // Act orchestrator.start(['test', 'test2']); // Assert a.should.equal(2); done(); }); it('should run all tasks when call run() multiple times', function(done) { var orchestrator, a, fn, fn2; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; fn2 = function() { ++a; }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); // Act orchestrator.start('test'); orchestrator.start('test2'); // Assert a.should.equal(2); done(); }); it('should add new tasks at the front of the queue', function(done) { var orchestrator, a, fn, fn2, fn3, aAtFn2, aAtFn3, test2pos, test3pos; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; orchestrator.start('test3'); }; fn2 = function() { ++a; aAtFn2 = a; }; fn3 = function() { ++a; aAtFn3 = a; }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); orchestrator.add('test3', fn3); // Act orchestrator.start('test', 'test2'); // Assert aAtFn3.should.equal(2); // 1 and 3 ran aAtFn2.should.equal(3); // 1, 3, and 2 ran a.should.equal(3); test2pos = orchestrator.seq.indexOf('test2'); test3pos = orchestrator.seq.indexOf('test3'); test2pos.should.be.above(-1); test3pos.should.be.above(-1); test2pos.should.be.above(test3pos); done(); }); it('should run all tasks when call run() multiple times', function(done) { var orchestrator, a, fn, fn2; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; fn2 = function() { ++a; }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); // Act orchestrator.start('test'); orchestrator.start('test2'); // Assert a.should.equal(2); done(); }); it('should run all tasks when call run() with no arguments', function(done) { var orchestrator, a, fn, fn2; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; fn2 = function() { ++a; }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); // Act orchestrator.start(); // Assert a.should.equal(2); done(); }); it('should run all async promise tasks', function(done) { var orchestrator, a, fn, fn2; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); },1); return deferred.promise; }; fn2 = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); },1); return deferred.promise; }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); // Act orchestrator.start('test'); orchestrator.start('test2', function () { // Assert orchestrator.isRunning.should.equal(false); a.should.equal(2); done(); }); orchestrator.isRunning.should.equal(true); }); it('should run all async callback tasks', function(done) { var orchestrator, a, fn, fn2; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function(cb) { setTimeout(function () { ++a; cb(null); },1); }; fn2 = function(cb) { setTimeout(function () { ++a; cb(null); },1); }; orchestrator.add('test', fn); orchestrator.add('test2', fn2); // Act orchestrator.start('test'); orchestrator.start('test2', function () { // Assert orchestrator.isRunning.should.equal(false); a.should.equal(2); done(); }); orchestrator.isRunning.should.equal(true); }); it('should run task scoped to orchestrator', function(done) { var orchestrator, a, fn; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { this.should.equal(orchestrator); ++a; }; orchestrator.add('test', fn); // Act orchestrator.start('test'); // Assert a.should.equal(1); orchestrator.isRunning.should.equal(false); done(); }); // FRAGILE: It resets task.done at `.start()` so if task isn't finished when you call `start()` again, it won't run again it('should run task multiple times when call run(task) multiple times', function(done) { var orchestrator, a, fn; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; orchestrator.add('test', fn); // Act orchestrator.start('test'); orchestrator.start('test'); // Assert a.should.equal(2); done(); }); it('should run task dependencies multiple times when call run(task) multiple times', function(done) { var orchestrator, a, fn, dep; // Arrange orchestrator = new Orchestrator(); a = 0; dep = function() { ++a; }; fn = function() { }; orchestrator.add('dep', dep); orchestrator.add('test', ['dep'], fn); // Act orchestrator.start('test'); orchestrator.start('test'); // Assert a.should.equal(2); done(); }); it('should run task multiple times when call run() (default) multiple times', function(done) { var orchestrator, a, fn; // Arrange orchestrator = new Orchestrator(); a = 0; fn = function() { ++a; }; orchestrator.add('test', fn); orchestrator.add('default', ['test'], function () {}); // Act orchestrator.start(function () { // Finished first run, now run a second time orchestrator.start(function () { // Assert a.should.equal(2); done(); }); }); }); it('should run no-op task', function(done) { var orchestrator; // Arrange orchestrator = new Orchestrator(); orchestrator.add('test'); // Act orchestrator.start(function () { // Assert done(); }); }); }); }); orchestrator-0.3.8/test/stop.js000066400000000000000000000056071300743641200165530ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('stop()', function() { it('should call doneCallback', function(done) { var orchestrator, a = 0; // Arrange orchestrator = new Orchestrator(); orchestrator.doneCallback = function (/*err*/) { a++; }; // Act orchestrator.stop(null); // Assert a.should.equal(1); done(); }); it('should pass error to doneCallback', function(done) { var orchestrator, actualError, expectedError = 'This is a test error'; // Arrange orchestrator = new Orchestrator(); orchestrator.doneCallback = function (err) { actualError = err; }; // Act orchestrator.stop(expectedError); // Assert should.exist(actualError); actualError.should.equal(expectedError); done(); }); it('should set isRunning to false', function(done) { var orchestrator; // Arrange orchestrator = new Orchestrator(); orchestrator.isRunning = true; // Act orchestrator.stop(null, true); // true means success // Assert orchestrator.isRunning.should.equal(false); done(); }); it('should log success', function (done) { var orchestrator, actualLog; // Arrange orchestrator = new Orchestrator(); orchestrator.on('stop', function (e) { actualLog = e; }); // Act orchestrator.stop(null, true); // true means success // Assert should.exist(actualLog); should.not.exist(actualLog.task); actualLog.message.indexOf('succeed').should.be.above(-1); done(); }); it('should log failure', function (done) { var orchestrator, actualLog; // Arrange orchestrator = new Orchestrator(); orchestrator.on('err', function (e) { actualLog = e; }); // Act orchestrator.stop(null, false); // false means aborted // Assert should.exist(actualLog); should.not.exist(actualLog.task); actualLog.message.indexOf('abort').should.be.above(-1); done(); }); it('should log exception', function (done) { var orchestrator, actualErr = 'the error', actualLog; // Arrange orchestrator = new Orchestrator(); orchestrator.on('err', function (e) { actualLog = e; }); // Act orchestrator.stop(actualErr); // false means aborted // Assert should.exist(actualLog); should.not.exist(actualLog.task); actualLog.message.indexOf('fail').should.be.above(-1); actualLog.err.should.equal(actualErr); done(); }); it('should throw if no callback and no err handler', function (done) { var orchestrator, expectedErr = 'the error', actualErr; // Arrange orchestrator = new Orchestrator(); // Act try { orchestrator.stop(expectedErr); // Assert } catch (err) { actualErr = err; } actualErr.should.equal(expectedErr); done(); }); }); }); orchestrator-0.3.8/test/stopTask.js000066400000000000000000000035151300743641200173720ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); require('should'); require('mocha'); describe('orchestrator', function() { describe('_stopTask()', function() { it('should set done = true', function(done) { var orchestrator, task, meta; // Arrange task = { name: 'test', fn: function() {} }; meta = { duration: 2, hrDuration: [2,2] }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._stopTask(task, meta); // Assert task.done.should.equal(true); done(); }); it('should set running = false', function(done) { var orchestrator, task, meta; // Arrange task = { name: 'test', fn: function() {}, running: true }; meta = { duration: 2, hrDuration: [2,2] }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._stopTask(task, meta); // Assert task.running.should.equal(false); done(); }); it('should set task.duration', function(done) { var orchestrator, duration, task, meta; // Arrange duration = 2; task = { name: 'test', fn: function() {}, start: new Date() }; meta = { duration: duration, hrDuration: [2,2] }; // the thing under test orchestrator = new Orchestrator(); // Act orchestrator._stopTask(task, meta); // Assert (typeof task.duration).should.equal('number'); task.duration.should.equal(duration); done(); }); it('should die if not passed a task', function(done) { // Arrange var orchestrator = new Orchestrator(); var succeed = false; // Act try { orchestrator._stopTask(); succeed = true; } catch (err) { succeed = false; } // Assert succeed.should.equal(false); done(); }); }); }); orchestrator-0.3.8/test/streamConsume.js000066400000000000000000000206411300743641200204060ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var Stream = require('stream'); var Readable = Stream.Readable; var Writable = Stream.Writable; var Duplex = Stream.Duplex; var Q = require('q'); var fs = require('fs'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('when given a stream', function() { it('should consume a Readable stream to relieve backpressure, in objectMode', function(done) { var orchestrator, a; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function() { // Create a Readable stream with a small buffer... var rs = Readable({objectMode: true, highWaterMark: 2}); rs._read = function() { // ...and generate more chunks than fit in that buffer if (a++ < 100) { rs.push(a); } else { rs.push(null); } }; return rs; }); // Act orchestrator.start('test', function(err) { // Assert // Simple completion of the task is the main criterion here, but check a few things: a.should.be.above(99); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should consume a Readable stream to relieve backpressure', function(done) { var orchestrator, a; // Arrange orchestrator = new Orchestrator(); a = 0; orchestrator.add('test', function() { // Create a Readable stream with a small buffer... var rs = Readable({highWaterMark: 2}); rs._read = function() { // ...and generate more chunks than fit in that buffer if (a++ < 100) { rs.push("."); } else { rs.push(null); } }; return rs; }); // Act orchestrator.start('test', function(err) { // Assert // Simple completion of the task is the main criterion here, but check a few things: a.should.be.above(99); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should detect completion of a Writable stream', function(done) { var orchestrator, a, lengthRead; // Arrange orchestrator = new Orchestrator(); a = 0; lengthRead = 0; orchestrator.add('test', function() { // Create a Readable stream... var rs = Readable({highWaterMark: 2}); rs._read = function() { if (a++ < 100) { rs.push("."); } else { rs.push(null); } }; // ...and consume it var ws = Writable(); ws._write = function(chunk, enc, next) { lengthRead += chunk.length; next(); }; rs.pipe(ws); // Return the Writable return ws; }); // Act orchestrator.start('test', function(err) { // Assert a.should.be.above(99); lengthRead.should.equal(100); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should detect completion of a Writable stream, in objectMode', function(done) { var orchestrator, a, lengthRead; // Arrange orchestrator = new Orchestrator(); a = 0; lengthRead = 0; orchestrator.add('test', function() { // Create a Readable stream... var rs = Readable({objectMode: true, highWaterMark: 2}); rs._read = function() { if (a++ < 100) { rs.push(a); } else { rs.push(null); } }; // ...and consume it var ws = Writable({objectMode: true}); ws._write = function(chunk, enc, next) { lengthRead++; next(); }; rs.pipe(ws); // Return the Writable return ws; }); // Act orchestrator.start('test', function(err) { // Assert a.should.be.above(99); lengthRead.should.equal(100); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should handle an intermediate Readable stream being returned', function(done) { var orchestrator, a, lengthRead; // Arrange orchestrator = new Orchestrator(); a = 0; lengthRead = 0; orchestrator.add('test', function() { // Create a Readable stream... var rs = Readable({highWaterMark: 2}); rs._read = function() { if (a++ < 100) { rs.push("."); } else { rs.push(null); } }; // ...and consume it var ws = Writable(); ws._write = function(chunk, enc, next) { lengthRead += chunk.length; next(); }; rs.pipe(ws); // Return the Readable return rs; }); // Act orchestrator.start('test', function(err) { // Assert a.should.be.above(99); // Ensure all data was received by the Writable lengthRead.should.equal(100); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should handle an intermediate Readable stream being returned, in objectMode', function(done) { var orchestrator, a, lengthRead; // Arrange orchestrator = new Orchestrator(); a = 0; lengthRead = 0; orchestrator.add('test', function() { // Create a Readable stream... var rs = Readable({objectMode: true, highWaterMark: 2}); rs._read = function() { if (a++ < 100) { rs.push(a); } else { rs.push(null); } }; // ...and consume it var ws = Writable({objectMode: true}); ws._write = function(chunk, enc, next) { lengthRead++; next(); }; rs.pipe(ws); // Return the Readable return rs; }); // Act orchestrator.start('test', function(err) { // Assert a.should.be.above(99); // Ensure all data was received by the Writable lengthRead.should.equal(100); should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should require the Readable side of a Duplex stream to be closed to trigger completion', function(done) { var orchestrator; var readableClosed = false; var readCalled = false; var writableClosed = false; // Arrange orchestrator = new Orchestrator(); orchestrator.add('test', function() { var ds = Duplex(); ds._write = function(chunk, enc, next) { next(); }; function closeReadable() { // Delay closing the Readable side setTimeout(function() { readableClosed = true; ds.push(null); }, 1); } ds.on("finish", function() { writableClosed = true; if (readCalled) closeReadable(); }); ds._read = function() { readCalled = true; // Only close the Readable if the Writable has already been closed if (writableClosed) closeReadable(); } // Close the Writable side - after returning the stream, so that orchestrator sees the close setTimeout(function() { ds.end(); }, 1); // Return the Duplex return ds; }); // Act orchestrator.start('test', function(err) { // Assert readableClosed.should.be.true; should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should handle a classic stream that is not piped anywhere', function(done) { var orchestrator; var readableClosed = false; var readCalled = false; var writableClosed = false; var i; // Arrange orchestrator = new Orchestrator(); orchestrator.add('test', function() { var rs = new Stream(); process.nextTick(function() { for (i = 1; i <= 100; i++) { rs.emit("data", i); } rs.emit("end"); }); // Return the Readable return rs; }); // Act orchestrator.start('test', function(err) { // Assert should.not.exist(err); orchestrator.isRunning.should.equal(false); done(); }); }); it('should handle a classic stream that is piped somewhere', function(done) { var orchestrator; var readableClosed = false; var readCalled = false; var writableClosed = false; var lengthRead = 0; var i; // Arrange orchestrator = new Orchestrator(); orchestrator.add('test', function() { var rs = new Stream(); process.nextTick(function() { for (i = 0; i < 100; i++) { rs.emit("data", i); } rs.emit("end"); }); var ws = new Writable({objectMode: true, highWaterMark: 5}); ws._write = function(chunk, enc, next) { lengthRead++; next(); }; rs.pipe(ws); // Return the Readable return rs; }); // Act orchestrator.start('test', function(err) { // Assert should.not.exist(err); lengthRead.should.equal(100); orchestrator.isRunning.should.equal(false); done(); }); }); }); }); orchestrator-0.3.8/test/task.js000066400000000000000000000027521300743641200165260ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('task()', function() { it('should return task if there is a task', function(done) { var orchestrator, name, task1, actual; // Arrange name = 'task1'; task1 = { name: name, fn: function() {} }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks[name] = task1; // Act actual = orchestrator.task(name); // Assert actual.should.equal(task1); done(); }); it('should return false if there is no such task', function(done) { var orchestrator, name, task1, actual; // Arrange name = 'task1'; task1 = { name: name, fn: function() {} }; // the thing under test orchestrator = new Orchestrator(); orchestrator.tasks[name] = task1; // Act actual = orchestrator.task('not'+name); // Assert should.not.exist(actual); done(); }); it('should create a task if passed a second arg', function(done) { var orchestrator, name, fn, actual; // Arrange name = 'task1'; fn = function () {}; // the thing under test orchestrator = new Orchestrator(); // Act actual = orchestrator.task(name, fn); // Assert should.not.exist(actual); should.exist(orchestrator.tasks[name]); orchestrator.tasks[name].name.should.equal(name); done(); }); }); }); orchestrator-0.3.8/test/taskDependencies.js000066400000000000000000000100451300743641200210270ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var Q = require('q'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('run() task dependencies', function() { // Technically these are duplicated from require('sequencify'), // but those are unit tests and these are integration tests it('should run tasks in specified order if no dependencies', function(done) { var orchestrator, a, fn, fn2; // Arrange a = 0; fn = function() { a.should.equal(1); ++a; }; fn2 = function() { a.should.equal(2); ++a; }; // Act orchestrator = new Orchestrator(); orchestrator.on('start', function (e) { should.exist(e); a.should.equal(0); ++a; e.message.should.startWith('seq: '); // Order is not deterministic, but event should still happen }); orchestrator.add('test1', fn); orchestrator.add('test2', fn2); orchestrator.start('test1', 'test2', function (err) { // Assert a.should.equal(3); should.not.exist(err); done(); }); }); it('should run dependency then specified task', function(done) { var orchestrator, a, fn, fn2; // Arrange a = 0; fn = function() { a.should.equal(1); ++a; }; fn2 = function() { a.should.equal(2); ++a; }; // Act orchestrator = new Orchestrator(); orchestrator.on('start', function (e) { should.exist(e); a.should.equal(0); ++a; e.message.should.equal('seq: dep,test'); }); orchestrator.add('dep', fn); orchestrator.add('test', ['dep'], fn2); orchestrator.start('test'); // Assert a.should.equal(3); done(); }); it('should run asynchronous dependency then specified task', function(done) { var orchestrator, a, fn, fn2; // Arrange a = 0; fn = function() { var deferred = Q.defer(); setTimeout(function () { a.should.equal(1); ++a; deferred.resolve(); },1); return deferred.promise; }; fn2 = function() { var deferred = Q.defer(); setTimeout(function () { a.should.equal(2); ++a; deferred.resolve(); },1); return deferred.promise; }; // Act orchestrator = new Orchestrator(); orchestrator.on('start', function (e) { should.exist(e); a.should.equal(0); ++a; e.message.should.equal('seq: dep,test'); }); orchestrator.add('dep', fn); orchestrator.add('test', ['dep'], fn2); orchestrator.start('test', function () { // Assert orchestrator.isRunning.should.equal(false); a.should.equal(3); done(); }); orchestrator.isRunning.should.equal(true); }); it('should run all tasks of complex dependency chain', function(done) { var orchestrator, a, fn1, fn2, fn3, fn4, timeout = 2; // Arrange a = 0; // fn1 is a long-running task, fn2 and 3 run quickly, fn4 is synchronous // If shorter tasks mark it done before the longer task finishes that's wrong fn1 = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); }, timeout*5); return deferred.promise; }; fn2 = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); }, timeout); return deferred.promise; }; fn3 = function() { var deferred = Q.defer(); setTimeout(function () { ++a; deferred.resolve(); }, timeout); return deferred.promise; }; fn4 = function() { ++a; }; // Act orchestrator = new Orchestrator(); orchestrator.on('start', function (e) { should.exist(e); a.should.equal(0); ++a; e.message.should.equal('seq: fn1,fn2,fn3,fn4'); }); orchestrator.add('fn1', fn1); orchestrator.add('fn2', fn2); orchestrator.add('fn3', ['fn1', 'fn2'], fn3); orchestrator.add('fn4', ['fn3'], fn4); orchestrator.start('fn4', function () { // Assert orchestrator.isRunning.should.equal(false); a.should.equal(5); done(); }); orchestrator.isRunning.should.equal(true); }); }); }); orchestrator-0.3.8/test/taskTimings.js000066400000000000000000000016751300743641200200640ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_runTask() task timings', function() { it('should set duration to 1 when task takes 1 second', function(done) { var orchestrator, a, fn, timeout; // Arrange timeout = 0.01; // seconds a = 0; fn = function(cb) { setTimeout(function () { cb(); }, timeout*1000); // milliseconds }; // The thing under test orchestrator = new Orchestrator(); orchestrator.add('test', fn); orchestrator.on('task_stop', function (args) { // Assert args.duration.should.be.approximately(timeout, 0.02); args.duration.should.be.above(0); ++a; }); // Act orchestrator.start('test', function (err) { // Assert a.should.equal(1); should.not.exist(err); done(); }); }); }); }); orchestrator-0.3.8/test/taskWaiting.js000066400000000000000000000136271300743641200200540ustar00rootroot00000000000000/*jshint node:true */ /*global describe:false, it:false */ "use strict"; var Orchestrator = require('../'); var Q = require('q'); var map = require('map-stream'); var es = require('event-stream'); var should = require('should'); require('mocha'); describe('orchestrator', function() { describe('_runTask() waits for done correctly', function() { it('sync task sets done after calling function', function(done) { var orchestrator, task, a; // Arrange a = 0; task = { name: 'test', fn: function() { should.not.exist(task.done); } }; // Act orchestrator = new Orchestrator(); orchestrator._runStep = function () { a.should.equal(0); a++; }; orchestrator._runTask(task); // Assert should.exist(task.done); task.done.should.equal(true); a.should.equal(1); done(); }); it('sync task logs finish after calling function', function(done) { var orchestrator, task, a; // Arrange a = 0; task = { name: 'test', fn: function() { a.should.equal(0); } }; // the thing under test orchestrator = new Orchestrator(); orchestrator.on('task_stop', function (e) { should.exist(e.task); e.task.should.equal('test'); if (e.message.indexOf('finish')) { ++a; } }); orchestrator._runStep = function () {}; // fake // Act orchestrator._runTask(task); // Assert a.should.equal(1); done(); }); it('async promise task sets done after task resolves', function(done) { var orchestrator, task, timeout = 5, a; // Arrange a = 0; task = { name: 'test', fn: function() { var deferred = Q.defer(); setTimeout(function () { should.not.exist(task.done); deferred.resolve(); }, timeout); return deferred.promise; } }; // Act orchestrator = new Orchestrator(); orchestrator._runStep = function () { a.should.equal(0); a++; }; orchestrator._runTask(task); // Assert should.not.exist(task.done); setTimeout(function () { should.exist(task.done); task.done.should.equal(true); a.should.equal(1); done(); }, timeout*2); }); it('async promise task logs finish after task resolves', function(done) { var orchestrator, task, timeout = 5, a; // Arrange a = 0; task = { name: 'test', fn: function() { var deferred = Q.defer(); setTimeout(function () { a.should.equal(0); deferred.resolve(); }, timeout); return deferred.promise; } }; // the thing under test orchestrator = new Orchestrator(); orchestrator.on('task_stop', function (e) { should.exist(e.task); e.task.should.equal('test'); if (e.message.indexOf('promise') > -1) { ++a; } }); orchestrator._runStep = function () {}; // Act orchestrator._runTask(task); // Assert setTimeout(function () { a.should.equal(1); done(); }, timeout*2); }); it('async callback task sets done after task resolves', function(done) { var orchestrator, task, timeout = 5, a; // Arrange a = 0; task = { name: 'test', fn: function(cb) { setTimeout(function () { should.not.exist(task.done); cb(null); }, timeout); } }; // Act orchestrator = new Orchestrator(); orchestrator._runStep = function () { a.should.equal(0); a++; }; orchestrator._runTask(task); // Assert should.not.exist(task.done); setTimeout(function () { should.exist(task.done); task.done.should.equal(true); a.should.equal(1); done(); }, timeout*2); }); it('async callback task logs finish after task resolves', function(done) { var orchestrator, task, timeout = 5, a; // Arrange a = 0; task = { name: 'test', fn: function(cb) { setTimeout(function () { should.not.exist(task.done); cb(null); }, timeout); } }; // the thing under test orchestrator = new Orchestrator(); orchestrator.on('task_stop', function (e) { should.exist(e.task); e.task.should.equal('test'); if (e.message.indexOf('callback') > -1) { ++a; } }); orchestrator._runStep = function () {}; // Act orchestrator._runTask(task); // Assert setTimeout(function () { a.should.equal(1); done(); }, timeout*2); }); it('async stream task sets done after task resolves', function(done) { var orchestrator, task, timeout = 5, a; // Arrange a = 0; task = { name: 'test', fn: function() { return es.readable(function(/*count, callback*/) { this.emit('data', {a:'rgs'}); this.emit('end'); }).pipe(map(function (f, cb) { setTimeout(function () { cb(null, f); }, timeout); })); } }; should.not.exist(task.done); // Act orchestrator = new Orchestrator(); orchestrator._runStep = function () { a.should.equal(0); a++; }; orchestrator._runTask(task); // Assert setTimeout(function () { should.exist(task.done); task.done.should.equal(true); a.should.equal(1); done(); }, timeout*2); }); it('async stream task logs finish after task resolves', function(done) { var orchestrator, task, timeout = 5, a; // Arrange a = 0; task = { name: 'test', fn: function() { return es.readable(function(/*count, callback*/) { this.emit('data', {a:'rgs'}); this.emit('end'); }).pipe(es.map(function (f, cb) { setTimeout(function () { cb(null, f); }, timeout); })); } }; // the thing under test orchestrator = new Orchestrator(); orchestrator.on('task_stop', function (e) { should.exist(e.task); e.task.should.equal('test'); if (e.message.indexOf('stream') > -1) { ++a; } }); orchestrator._runStep = function () {}; // Act orchestrator._runTask(task); // Assert setTimeout(function () { a.should.equal(1); done(); }, timeout*2); }); }); });