mirror of
https://github.com/bluxmit/alnoda-workspaces.git
synced 2024-09-19 19:27:10 +12:00
1559 lines
52 KiB
JavaScript
Executable file
1559 lines
52 KiB
JavaScript
Executable file
// Unit tests for Cronicle (run using `npm test`)
|
|
// Copyright (c) 2016 - 2017 Joseph Huckaby
|
|
// Released under the MIT License
|
|
|
|
var cp = require('child_process');
|
|
var fs = require('fs');
|
|
var async = require('async');
|
|
var glob = require('glob');
|
|
var moment = require('moment-timezone');
|
|
|
|
var Tools = require('pixl-tools');
|
|
var PixlServer = require("pixl-server");
|
|
|
|
// we need a few config files
|
|
var config = require('../sample_conf/config.json');
|
|
var setup = require('../sample_conf/setup.json');
|
|
|
|
// override things for the unit tests
|
|
config.debug = true;
|
|
config.echo = false;
|
|
config.color = false;
|
|
config.master = false;
|
|
|
|
config.WebServer.http_port = 4012;
|
|
config.base_app_url = "http://localhost:4012";
|
|
config.udp_broadcast_port = 4014;
|
|
|
|
config.email_from = "test@localhost";
|
|
config.smtp_hostname = "localhost";
|
|
config.secret_key = "UNIT_TEST";
|
|
config.log_filename = "unit.log";
|
|
config.pid_file = "logs/unit.pid";
|
|
config.debug_level = 10;
|
|
config.scheduler_startup_grace = 0;
|
|
config.job_startup_grace = 1;
|
|
config.Storage.Filesystem.base_dir = "data/unittest";
|
|
config.web_hook_config_keys = ["base_app_url", "something_custom"];
|
|
config.something_custom = "nonstandard property";
|
|
config.track_manual_jobs = true;
|
|
|
|
// chdir to the proper server root dir
|
|
process.chdir( require('path').dirname( __dirname ) );
|
|
|
|
// global refs
|
|
var server = null;
|
|
var storage = null;
|
|
var cronicle = null;
|
|
var request = null;
|
|
var api_url = '';
|
|
var session_id = '';
|
|
|
|
module.exports = {
|
|
logDebug: function(level, msg, data) {
|
|
// proxy request to system logger with correct component
|
|
if (cronicle && cronicle.logger) {
|
|
cronicle.logger.set( 'component', 'UnitTest' );
|
|
cronicle.logger.debug( level, msg, data );
|
|
}
|
|
},
|
|
|
|
setUp: function (callback) {
|
|
// always called before tests start
|
|
var self = this;
|
|
|
|
// make sure another unit test isn't running
|
|
var pid = false;
|
|
try { pid = fs.readFileSync('logs/unit.pid', { encoding: 'utf8' }); }
|
|
catch (e) {;}
|
|
if (pid) {
|
|
var alive = true;
|
|
try { process.kill(parseInt(pid), 0); }
|
|
catch (e) { alive = false; }
|
|
if (alive) {
|
|
console.warn("Another unit test is already running (PID " + pid + "). Exiting.");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// clean out data from last time
|
|
try { cp.execSync('rm -rf logs/unit.pid data/unittest'); }
|
|
catch (e) {;}
|
|
|
|
// construct server object
|
|
server = new PixlServer({
|
|
|
|
__name: 'Cronicle',
|
|
__version: require('../package.json').version,
|
|
|
|
config: config,
|
|
|
|
components: [
|
|
require('pixl-server-storage'),
|
|
require('pixl-server-web'),
|
|
require('pixl-server-api'),
|
|
require('pixl-server-user'),
|
|
require('./engine.js')
|
|
]
|
|
|
|
});
|
|
|
|
server.startup( function() {
|
|
// server startup complete
|
|
storage = server.Storage;
|
|
cronicle = server.Cronicle;
|
|
|
|
// prepare to make api calls
|
|
request = cronicle.request;
|
|
api_url = server.config.get('base_app_url') + server.API.config.get('base_uri');
|
|
|
|
// cancel auto ticks, so we can send our own later
|
|
clearTimeout( server.tickTimer );
|
|
delete server.tickTimer;
|
|
|
|
// bootstrap storage with initial records
|
|
async.eachSeries( setup.storage,
|
|
function(params, callback) {
|
|
var func = params.shift();
|
|
params.push( callback );
|
|
|
|
// massage a few params
|
|
if (typeof(params[1]) == 'object') {
|
|
var obj = params[1];
|
|
if (obj.created) obj.created = Tools.timeNow(true);
|
|
if (obj.modified) obj.modified = Tools.timeNow(true);
|
|
if (obj.regexp && (obj.regexp == '_HOSTNAME_')) obj.regexp = '^(' + Tools.escapeRegExp( server.hostname ) + ')$';
|
|
if (obj.hostname && (obj.hostname == '_HOSTNAME_')) obj.hostname = server.hostname;
|
|
if (obj.ip && (obj.ip == '_IP_')) obj.ip = server.ip;
|
|
}
|
|
|
|
// call storage directly
|
|
storage[func].apply( storage, params );
|
|
},
|
|
function(err) {
|
|
if (err) throw err;
|
|
|
|
// begin unit tests
|
|
callback();
|
|
}
|
|
); // async.eachSeries
|
|
} ); // server.startup
|
|
}, // setUp
|
|
|
|
beforeEach: function(test) {
|
|
// called just before each test
|
|
this.logDebug(10, "Starting unit test: " + test.name );
|
|
},
|
|
|
|
afterEach: function(test) {
|
|
// called after each test completes
|
|
this.logDebug(10, "Unit test complete: " + test.name );
|
|
},
|
|
|
|
//
|
|
// Tests Array:
|
|
//
|
|
|
|
tests: [
|
|
|
|
function testServerStarted(test) {
|
|
test.ok( server.started > 0, 'Cronicle started up successfully');
|
|
test.done();
|
|
},
|
|
|
|
function testStorage(test) {
|
|
storage.get( 'users/admin', function(err, user) {
|
|
test.ok( !err, "No error fetching admin user" );
|
|
test.ok( !!user, "User record is non-null" );
|
|
test.ok( user.username == "admin", "Username is correct" );
|
|
test.ok( user.created > 0, "User creation date is non-zero" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testcheckMasterEligibility(test) {
|
|
cronicle.checkMasterEligibility( function() {
|
|
test.ok( cronicle.multi.cluster == true, "Server found in cluster" );
|
|
test.ok( cronicle.multi.eligible == true, "Server is eligible for master" );
|
|
test.ok( cronicle.multi.master == false, "Server is not yet master" );
|
|
test.ok( cronicle.multi.slave == false, "Server is not a slave" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testGoMaster(test) {
|
|
cronicle.goMaster();
|
|
|
|
test.ok( cronicle.multi.master == true, "Server became master" );
|
|
test.ok( cronicle.multi.slave == false, "Server is not a slave" );
|
|
test.ok( cronicle.multi.cluster == true, "Server is still found in cluster" );
|
|
test.ok( cronicle.multi.masterHostname == server.hostname, "Server masterHostname is self" );
|
|
test.ok( !!cronicle.multi.lastPingSent, "Server lastPingSent is non-zero" );
|
|
test.ok( !!cronicle.tz, "Server has a timezone set" );
|
|
|
|
// need a rest here, so async sub-components can start up
|
|
setTimeout( function() { test.done(); }, 500 );
|
|
},
|
|
|
|
function testAPIPing(test) {
|
|
// make basic REST API call, check response
|
|
request.json( api_url + '/app/ping', {}, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting ping API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from ping API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testAPILoginBadUsername(test) {
|
|
// login with unknown username
|
|
var params = {
|
|
username: "nobody",
|
|
password: "foo"
|
|
};
|
|
request.json( api_url + '/user/login', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting user/login API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from user/login API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code != 0, "Code is non-zero (we expect an error)" );
|
|
test.ok( !data.session_id, "No session_id in response" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testAPILoginBadPassword(test) {
|
|
// login with good user but bad password
|
|
var params = {
|
|
username: "admin",
|
|
password: "adminnnnnnn"
|
|
};
|
|
request.json( api_url + '/user/login', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting user/login API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from user/login API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code != 0, "Code is non-zero (we expect an error)" );
|
|
test.ok( !data.session_id, "No session_id in response" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testAPIUserLogin(test) {
|
|
// login as admin (successfully), save session id for downstream tests
|
|
var params = {
|
|
username: "admin",
|
|
password: "admin"
|
|
};
|
|
request.json( api_url + '/user/login', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting user/login API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from user/login API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.session_id, "Found session_id in response" );
|
|
|
|
// save session_id for later
|
|
session_id = data.session_id;
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testAPIConfig(test) {
|
|
// test app/config api
|
|
var params = {};
|
|
request.json( api_url + '/app/config', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.config, "Found config in response data" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
// app/create_plugin
|
|
|
|
function testAPICreatePlugin(test) {
|
|
// test app/create_plugin api
|
|
var self = this;
|
|
var params = {"params":[{"type":"textarea","id":"script","title":"Script Source","rows":10,"value":"#!/bin/sh\n\n# Enter your shell script code here"}],"title":"Copy of Shell Script","command":"bin/shell-plugin.js","enabled":1,"session_id":session_id};
|
|
|
|
request.json( api_url + '/app/create_plugin', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.id, "Found new id in data" );
|
|
|
|
// save plugin id for later
|
|
self.plugin_id = data.id;
|
|
|
|
// check to see that plugin actually got saved to storage
|
|
storage.listFind( 'global/plugins', { id: data.id }, function(err, plugin) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!plugin, "Data record record is non-null" );
|
|
test.ok( plugin.username == "admin", "Username is correct" );
|
|
test.ok( plugin.created > 0, "Record creation date is non-zero" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/update_plugin
|
|
|
|
function testAPIUpdatePlugin(test) {
|
|
// test app/update_plugin api
|
|
var self = this;
|
|
var params = {"id":this.plugin_id, "title":"Updated Plugin Title","session_id":session_id};
|
|
|
|
request.json( api_url + '/app/update_plugin', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that plugin actually got saved to storage
|
|
storage.listFind( 'global/plugins', { id: self.plugin_id }, function(err, plugin) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!plugin, "Data record is non-null" );
|
|
test.ok( plugin.username == "admin", "Username is correct" );
|
|
test.ok( plugin.created > 0, "Record creation date is non-zero" );
|
|
test.ok( plugin.title == "Updated Plugin Title", "Title was updated correctly" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/delete_plugin
|
|
|
|
function testAPIDeletePlugin(test) {
|
|
// test app/delete_plugin api
|
|
var self = this;
|
|
var params = {"id":this.plugin_id, "session_id":session_id};
|
|
|
|
request.json( api_url + '/app/delete_plugin', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that plugin actually got deleted from storage
|
|
storage.listFind( 'global/plugins', { id: self.plugin_id }, function(err, plugin) {
|
|
test.ok( !err, "No error expected for missing data" );
|
|
test.ok( !plugin, "Data record should be null (deleted)" );
|
|
|
|
delete self.plugin_id;
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/create_category
|
|
|
|
function testAPICreateCategory(test) {
|
|
// test app/create_category api
|
|
var self = this;
|
|
var params = {"title":"test will del cat","description":"yo","max_children":0,"enabled":1,"notify_success":"","notify_fail":"","web_hook":"","cpu_limit":0,"cpu_sustain":0,"memory_limit":0,"memory_sustain":0,"log_max_size":0,"session_id":session_id};
|
|
|
|
request.json( api_url + '/app/create_category', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.id, "Found new id in data" );
|
|
|
|
// save cat id for later
|
|
self.cat_id = data.id;
|
|
|
|
// check to see that cat actually got saved to storage
|
|
storage.listFind( 'global/categories', { id: data.id }, function(err, cat) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!cat, "Data record record is non-null" );
|
|
test.ok( cat.username == "admin", "Username is correct" );
|
|
test.ok( cat.created > 0, "Record creation date is non-zero" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/update_category
|
|
|
|
function testAPIUpdateCategory(test) {
|
|
// test app/update_category api
|
|
var self = this;
|
|
var params = {"id":this.cat_id, "title":"Updated Category Title","session_id":session_id};
|
|
|
|
request.json( api_url + '/app/update_category', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that cat actually got saved to storage
|
|
storage.listFind( 'global/categories', { id: self.cat_id }, function(err, cat) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!cat, "Data record is non-null" );
|
|
test.ok( cat.username == "admin", "Username is correct" );
|
|
test.ok( cat.created > 0, "Record creation date is non-zero" );
|
|
test.ok( cat.title == "Updated Category Title", "Title was updated correctly" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/delete_category
|
|
|
|
function testAPIDeleteCategory(test) {
|
|
// test app/delete_category api
|
|
var self = this;
|
|
var params = {"id":this.cat_id, "session_id":session_id};
|
|
|
|
request.json( api_url + '/app/delete_category', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that cat actually got deleted from storage
|
|
storage.listFind( 'global/categories', { id: self.cat_id }, function(err, cat) {
|
|
test.ok( !err, "No error expected for missing data" );
|
|
test.ok( !cat, "Data record should be null (deleted)" );
|
|
|
|
delete self.cat_id;
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/create_server_group
|
|
|
|
function testAPICreateServerGroup(test) {
|
|
// test app/create_server_group api
|
|
var self = this;
|
|
var params = {"title":"del gap","regexp":"dasds","master":0,"session_id":session_id};
|
|
|
|
request.json( api_url + '/app/create_server_group', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.id, "Found new id in data" );
|
|
|
|
// save group id for later
|
|
self.group_id = data.id;
|
|
|
|
// check to see that group actually got saved to storage
|
|
storage.listFind( 'global/server_groups', { id: data.id }, function(err, group) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!group, "Data record record is non-null" );
|
|
test.ok( group.title == "del gap", "Title is correct" );
|
|
test.ok( group.regexp == "dasds", "Regexp is correct" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/update_server_group
|
|
|
|
function testAPIUpdateServerGroup(test) {
|
|
// test app/update_server_group api
|
|
var self = this;
|
|
var params = {"id":this.group_id, "title":"Updated Group Title","session_id":session_id};
|
|
|
|
request.json( api_url + '/app/update_server_group', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that group actually got saved to storage
|
|
storage.listFind( 'global/server_groups', { id: self.group_id }, function(err, group) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!group, "Data record is non-null" );
|
|
test.ok( group.title == "Updated Group Title", "Title was updated correctly" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/delete_server_group
|
|
|
|
function testAPIDeleteServerGroup(test) {
|
|
// test app/delete_server_group api
|
|
var self = this;
|
|
var params = {"id":this.group_id, "session_id":session_id};
|
|
|
|
request.json( api_url + '/app/delete_server_group', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that group actually got deleted from storage
|
|
storage.listFind( 'global/server_groups', { id: self.group_id }, function(err, group) {
|
|
test.ok( !err, "No error expected for missing data" );
|
|
test.ok( !group, "Data record should be null (deleted)" );
|
|
|
|
delete self.group_id;
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/create_api_key
|
|
|
|
function testAPICreateAPIKey(test) {
|
|
// test app/create_api_key api
|
|
var self = this;
|
|
var params = {"key":"35b60c12892dd4503cf3a8dbf22d3354","privileges":{"admin":0,"create_events":0,"edit_events":0,"delete_events":1,"run_events":0,"abort_events":0,"state_update":0},"active":"1","title":"test will delete","description":"dshfdwsfs","session_id":session_id};
|
|
|
|
request.json( api_url + '/app/create_api_key', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.id, "Found new id in data" );
|
|
test.ok( !!data.key, "Found new api key in data" );
|
|
|
|
// save api key id for later
|
|
self.apikey_id = data.id;
|
|
self.apikey_key = data.key;
|
|
|
|
// check to see that api key actually got saved to storage
|
|
storage.listFind( 'global/api_keys', { id: data.id }, function(err, api_key) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!api_key, "Data record is non-null" );
|
|
test.ok( api_key.username == "admin", "Username is correct" );
|
|
test.ok( api_key.created > 0, "Record creation date is non-zero" );
|
|
test.ok( !!api_key.key, "API Key record has key" );
|
|
test.ok( api_key.key == "35b60c12892dd4503cf3a8dbf22d3354", "API Key is correct" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
function testAPIKeyUsage(test) {
|
|
// try to hit an API using the API Key as auth (not a user session id)
|
|
var self = this;
|
|
var params = { "api_key": this.apikey_key, offset: 0, limit: 100 };
|
|
|
|
request.json( api_url + '/app/get_schedule', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testAPIKeyUnauthorized(test) {
|
|
// try to access an API that is unauthorized for an API Key
|
|
// an error is expected here
|
|
var self = this;
|
|
var params = {"title":"this should fail","description":"yo key","max_children":0,"enabled":1,"notify_success":"","notify_fail":"","web_hook":"","cpu_limit":0,"cpu_sustain":0,"memory_limit":0,"memory_sustain":0,"api_key":this.apikey_key};
|
|
|
|
request.json( api_url + '/app/create_category', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API", err );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API", resp.statusCode );
|
|
test.ok( "code" in data, "Found code prop in JSON response", data );
|
|
test.ok( data.code != 0, "Code is non-zero (error is expected)", data );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
// app/update_api_key
|
|
|
|
function testAPIUpdateAPIKey(test) {
|
|
// test app/update_api_key api
|
|
var self = this;
|
|
var params = {"id":this.apikey_id, "title":"Updated API Key Title","session_id":session_id};
|
|
|
|
request.json( api_url + '/app/update_api_key', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that api key actually got saved to storage
|
|
storage.listFind( 'global/api_keys', { id: self.apikey_id }, function(err, api_key) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!api_key, "Data record is non-null" );
|
|
test.ok( api_key.username == "admin", "Username is correct" );
|
|
test.ok( api_key.created > 0, "Record creation date is non-zero" );
|
|
test.ok( api_key.title == "Updated API Key Title", "Title was updated correctly" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/get_api_keys
|
|
|
|
function testAPIGetAPIKeys(test) {
|
|
// test app/get_api_keys api
|
|
var self = this;
|
|
var params = { "session_id": session_id, offset: 0, limit: 100 };
|
|
|
|
request.json( api_url + '/app/get_api_keys', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.rows, "Found rows in response" );
|
|
test.ok( !!data.rows.length, "Rows has length" );
|
|
|
|
var api_key = Tools.findObject( data.rows, { id: self.apikey_id } );
|
|
test.ok( !!api_key, "Found our API Key in rows" );
|
|
test.ok( api_key.id == self.apikey_id, "API Key ID matches our query" );
|
|
test.ok( api_key.username == "admin", "Username is correct" );
|
|
test.ok( api_key.created > 0, "Record creation date is non-zero" );
|
|
test.ok( !!api_key.key, "API Key record has key" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/get_api_key
|
|
|
|
function testAPIGetAPIKey(test) {
|
|
// test app/get_api_key api
|
|
var self = this;
|
|
var params = { "session_id": session_id, id: this.apikey_id };
|
|
|
|
request.json( api_url + '/app/get_api_key', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
var api_key = data.api_key;
|
|
test.ok( !!api_key, "Found our API Key in data" );
|
|
test.ok( api_key.id == self.apikey_id, "API Key ID matches our query" );
|
|
test.ok( api_key.username == "admin", "Username is correct" );
|
|
test.ok( api_key.created > 0, "Record creation date is non-zero" );
|
|
test.ok( !!api_key.key, "API Key record has key" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/delete_api_key
|
|
|
|
function testAPIDeleteAPIKey(test) {
|
|
// test app/delete_api_key api
|
|
var self = this;
|
|
var params = {"id":this.apikey_id, "session_id":session_id};
|
|
|
|
request.json( api_url + '/app/delete_api_key', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that api key actually got deleted from storage
|
|
storage.listFind( 'global/api_keys', { id: self.apikey_id }, function(err, api_key) {
|
|
test.ok( !err, "No error expected for missing data" );
|
|
test.ok( !api_key, "Data record should be null (deleted)" );
|
|
|
|
delete self.apikey_id;
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/create_event
|
|
|
|
function testAPICreateEvent(test) {
|
|
// test app/create_event api
|
|
var self = this;
|
|
var params = {
|
|
"enabled": 1,
|
|
"params": {
|
|
"duration": "10",
|
|
"progress": 1,
|
|
"action": "Success",
|
|
"secret": "foo"
|
|
},
|
|
"timing": {
|
|
"years": [2001], // we'll run it manually first
|
|
"minutes": [0]
|
|
},
|
|
"max_children": 1,
|
|
"timeout": 300,
|
|
"catch_up": 0,
|
|
"timezone": cronicle.tz,
|
|
"plugin": "testplug",
|
|
"title": "Well Test!",
|
|
"category": "general",
|
|
"target": "maingrp",
|
|
"multiplex": 0,
|
|
"retries": 0,
|
|
"retry_delay": 0,
|
|
"detached": 0,
|
|
"notify_success": "",
|
|
"notify_fail": "",
|
|
"web_hook": "",
|
|
"cpu_limit": 0,
|
|
"cpu_sustain": 0,
|
|
"memory_limit": 0,
|
|
"memory_sustain": 0,
|
|
"notes": "",
|
|
"session_id": session_id
|
|
};
|
|
|
|
request.json( api_url + '/app/create_event', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.id, "Found new id in data" );
|
|
|
|
// save event id for later
|
|
self.event_id = data.id;
|
|
|
|
// check to see that event actually got saved to storage
|
|
storage.listFind( 'global/schedule', { id: data.id }, function(err, event) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!event, "Data record record is non-null" );
|
|
test.ok( event.username == "admin", "Username is correct" );
|
|
test.ok( event.created > 0, "Record creation date is non-zero" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/update_event
|
|
|
|
function testAPIUpdateEvent(test) {
|
|
// test app/update_event api
|
|
var self = this;
|
|
var params = {
|
|
"id": this.event_id,
|
|
"title": "Updated Event Title",
|
|
"session_id": session_id
|
|
};
|
|
|
|
request.json( api_url + '/app/update_event', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that event actually got saved to storage
|
|
storage.listFind( 'global/schedule', { id: self.event_id }, function(err, event) {
|
|
test.ok( !err, "No error fetching data" );
|
|
test.ok( !!event, "Data record record is non-null" );
|
|
test.ok( event.username == "admin", "Username is correct" );
|
|
test.ok( event.created > 0, "Record creation date is non-zero" );
|
|
test.ok( event.title == "Updated Event Title", "New title is correct" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
// app/get_schedule
|
|
|
|
function testAPIGetSchedule(test) {
|
|
// test app/get_schedule api
|
|
var self = this;
|
|
var params = { "session_id": session_id, offset: 0, limit: 100 };
|
|
|
|
request.json( api_url + '/app/get_schedule', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.rows, "Found rows in response" );
|
|
test.ok( !!data.rows.length, "Rows has length" );
|
|
|
|
var event = Tools.findObject( data.rows, { id: self.event_id } );
|
|
test.ok( !!event, "Found our event in rows" );
|
|
test.ok( event.id == self.event_id, "Event ID matches our query" );
|
|
test.ok( event.username == "admin", "Username is correct" );
|
|
test.ok( event.created > 0, "Record creation date is non-zero" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/get_event
|
|
|
|
function testAPIGetEvent(test) {
|
|
// test app/get_event api
|
|
var self = this;
|
|
var params = { "session_id": session_id, id: this.event_id };
|
|
|
|
request.json( api_url + '/app/get_event', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
var event = data.event;
|
|
test.ok( !!event, "Found our event in data" );
|
|
test.ok( event.id == self.event_id, "Event ID matches our query" );
|
|
test.ok( event.username == "admin", "Username is correct" );
|
|
test.ok( event.created > 0, "Record creation date is non-zero" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/run_event
|
|
|
|
function testAPIRunEvent(test) {
|
|
// test app/run_event api
|
|
// run event manually, specify an override
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
id: this.event_id,
|
|
notify_fail: 'test@test.com'
|
|
};
|
|
|
|
request.json( api_url + '/app/run_event', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.ids, "Found ids in response" );
|
|
test.ok( data.ids.length == 1, "Data ids has length of 1" );
|
|
test.ok( !!data.ids[0], "Found Job ID in response data" );
|
|
|
|
var job_id = data.ids[0];
|
|
self.job_id = job_id;
|
|
|
|
// wait a few seconds here for job to start and get to around 50%
|
|
setTimeout( function() {
|
|
test.done();
|
|
}, 1000 * 5 );
|
|
|
|
} );
|
|
},
|
|
|
|
function testJobInProgress(test) {
|
|
// make sure job is in progress
|
|
var self = this;
|
|
var all_jobs = cronicle.getAllActiveJobs();
|
|
var job = all_jobs[ this.job_id ];
|
|
|
|
test.ok( !!job, "Found our job in active list" );
|
|
test.ok( job.event == this.event_id, "Job has correct Event ID" );
|
|
test.ok( job.progress > 0, "Job has positive progress" );
|
|
test.ok( job.notify_fail == "test@test.com", "Our notify_fail override made it in" );
|
|
test.ok( !!job.pid, "Job has a PID" );
|
|
|
|
// try to ping pid
|
|
var ping = false;
|
|
try { ping = process.kill( job.pid, 0 ); }
|
|
catch (e) {;}
|
|
test.ok( !!ping, "Job PID was successfully pinged" );
|
|
|
|
// force cronicle to measure mem/cpu
|
|
cronicle.monitorServerResources( function(err) {
|
|
test.ok( !err, "No error calling monitorServerResources", err );
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
// app/get_live_job_log
|
|
|
|
function testAPIGetLiveJobLog(test) {
|
|
// test get_live_job_log API (raw HTTP get, not a JSON API)
|
|
var self = this;
|
|
|
|
request.get( api_url + '/app/get_live_job_log?id=' + this.job_id, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( !!data, "Got data buffer" );
|
|
test.ok( data.length > 0, "Data buffer has length" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/get_job_status
|
|
|
|
function testAPIGetJobStatus(test) {
|
|
// test app/get_job_status api
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
id: this.job_id
|
|
};
|
|
|
|
request.json( api_url + '/app/get_job_status', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
test.ok( !!data.job, "Found job in data" );
|
|
|
|
var job = data.job;
|
|
test.ok( job.id == self.job_id, "Job ID matches" );
|
|
test.ok( job.progress > 0, "Job progress is still non-zero" );
|
|
|
|
test.ok( !!job.cpu, "Job has CPU metrics" );
|
|
test.ok( job.cpu.count > 0, "Job CPU count is non-zero" );
|
|
// test.ok( job.cpu.current > 0, "Job CPU current is non-zero" );
|
|
|
|
test.ok( !!job.mem, "Job has memory metrics" );
|
|
test.ok( job.mem.count > 0, "Job memory count is non-zero" );
|
|
// test.ok( job.mem.current > 0, "Job memory current is non-zero" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/update_job
|
|
|
|
function testAPIUpdateJob(test) {
|
|
// test app/update_job api
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
id: this.job_id,
|
|
notify_fail: 'test2@test.com'
|
|
};
|
|
|
|
request.json( api_url + '/app/update_job', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
var all_jobs = cronicle.getAllActiveJobs();
|
|
var job = all_jobs[ self.job_id ];
|
|
|
|
test.ok( !!job, "Found our job in active list" );
|
|
test.ok( job.event == self.event_id, "Job has correct Event ID" );
|
|
test.ok( job.notify_fail == "test2@test.com", "Our notify_fail update was applied" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// wait for job to complete
|
|
|
|
function testWaitJobComplete(test) {
|
|
// go into wait loop while job is still in progress
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
id: this.job_id,
|
|
need_log: 1
|
|
};
|
|
var details = { code: 1 };
|
|
var count = 0;
|
|
|
|
async.doWhilst(
|
|
function (callback) {
|
|
// poll get_job_details API
|
|
request.json( api_url + '/app/get_job_details', params, function(err, resp, data) {
|
|
if (err) return callback(err);
|
|
if (resp.statusCode != 200) return callback(new Error("HTTP " + resp.statusCode + " " + resp.statusMessage));
|
|
|
|
// e-brake to prevent infinite loop
|
|
if (count++ > 100) return callback(new Error("Too many loop iterations polling get_job_details API"));
|
|
|
|
details = data;
|
|
setTimeout( callback, 500 );
|
|
} );
|
|
},
|
|
function () { return (details.code != 0); },
|
|
function (err) {
|
|
// job is complete
|
|
var job = details.job;
|
|
|
|
test.ok( !!job, "Got job details in response" );
|
|
test.ok( job.id == self.job_id, "Job ID matches" );
|
|
test.ok( !!job.complete, "Job is marked as complete" );
|
|
test.ok( job.code == 0, "Job is not marked as an error" );
|
|
test.ok( !!job.perf, "Job has perf metrics" );
|
|
test.ok( !!job.pid, "Job record still has a pid" );
|
|
|
|
// job pid should be dead at this point
|
|
var ping = false;
|
|
try { ping = process.kill( job.pid, 0 ); }
|
|
catch (e) {;}
|
|
test.ok( !ping, "Job PID is dead" );
|
|
|
|
test.done();
|
|
}
|
|
);
|
|
},
|
|
|
|
// app/get_job_log
|
|
|
|
function testAPIGetJobLog(test) {
|
|
// test get_job_log API (raw HTTP get, not a JSON API)
|
|
var self = this;
|
|
|
|
request.get( api_url + '/app/get_job_log?id=' + this.job_id, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( !!data, "Got data buffer" );
|
|
test.ok( data.length > 0, "Data buffer has length" );
|
|
test.ok( data.toString().match(/success/i), "Log buffer contains expected string" );
|
|
|
|
test.done();
|
|
|
|
} );
|
|
},
|
|
|
|
// app/get_event_history
|
|
|
|
function testAPIGetEventHistory(test) {
|
|
// go into wait loop while event history is being written
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
id: this.event_id,
|
|
offset: 0,
|
|
limit: 100
|
|
};
|
|
var details = { rows: [] };
|
|
var count = 0;
|
|
|
|
async.doWhilst(
|
|
function (callback) {
|
|
// poll get_event_history API
|
|
request.json( api_url + '/app/get_event_history', params, function(err, resp, data) {
|
|
if (err) return callback(err);
|
|
if (resp.statusCode != 200) return callback(new Error("HTTP " + resp.statusCode + " " + resp.statusMessage));
|
|
|
|
// e-brake to prevent infinite loop
|
|
if (count++ > 10) return callback(new Error("Too many loop iterations polling get_event_history API"));
|
|
|
|
details = data;
|
|
setTimeout( callback, 500 );
|
|
} );
|
|
},
|
|
function () { return ( !details.rows || !details.rows.length ); },
|
|
function (err) {
|
|
// history is written
|
|
var stub = details.rows[0];
|
|
|
|
test.ok( !!stub, "Got event history in response" );
|
|
test.ok( stub.id == self.job_id, "History ID matches Job ID" );
|
|
test.ok( stub.code == 0, "Correct code in history item" );
|
|
test.ok( stub.event == self.event_id, "History item Event ID matching Event ID" );
|
|
test.ok( stub.elapsed > 0, "History item has non-zero elapsed time" );
|
|
test.ok( stub.action == "job_complete", "History item has correct action" );
|
|
|
|
test.done();
|
|
}
|
|
);
|
|
},
|
|
|
|
// app/get_history
|
|
|
|
function testAPIGetHistory(test) {
|
|
// go into wait loop while history is being written
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
offset: 0,
|
|
limit: 100
|
|
};
|
|
var details = { rows: [] };
|
|
var count = 0;
|
|
|
|
async.doWhilst(
|
|
function (callback) {
|
|
// poll get_history API
|
|
request.json( api_url + '/app/get_history', params, function(err, resp, data) {
|
|
if (err) return callback(err);
|
|
if (resp.statusCode != 200) return callback(new Error("HTTP " + resp.statusCode + " " + resp.statusMessage));
|
|
|
|
// e-brake to prevent infinite loop
|
|
if (count++ > 10) return callback(new Error("Too many loop iterations polling get_history API"));
|
|
|
|
details = data;
|
|
setTimeout( callback, 500 );
|
|
} );
|
|
},
|
|
function () { return ( !details.rows || !details.rows.length ); },
|
|
function (err) {
|
|
// history is written
|
|
var stub = details.rows[0];
|
|
|
|
test.ok( !!stub, "Got event history in response" );
|
|
test.ok( stub.id == self.job_id, "History ID matches Job ID" );
|
|
test.ok( stub.code == 0, "Correct code in history item" );
|
|
test.ok( stub.event == self.event_id, "History item Event ID matching Event ID" );
|
|
test.ok( stub.elapsed > 0, "History item has non-zero elapsed time" );
|
|
test.ok( stub.action == "job_complete", "History item has correct action" );
|
|
|
|
test.done();
|
|
}
|
|
);
|
|
},
|
|
|
|
// app/get_activity
|
|
|
|
function testAPIGetActivity(test) {
|
|
// go into wait loop while activity log is being written
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
offset: 0,
|
|
limit: 100
|
|
};
|
|
var details = { rows: [] };
|
|
var count = 0;
|
|
|
|
async.doWhilst(
|
|
function (callback) {
|
|
// poll get_activity API
|
|
request.json( api_url + '/app/get_activity', params, function(err, resp, data) {
|
|
if (err) return callback(err);
|
|
if (resp.statusCode != 200) return callback(new Error("HTTP " + resp.statusCode + " " + resp.statusMessage));
|
|
|
|
// e-brake to prevent infinite loop
|
|
if (count++ > 10) return callback(new Error("Too many loop iterations polling get_activity API"));
|
|
|
|
details = data;
|
|
setTimeout( callback, 500 );
|
|
} );
|
|
},
|
|
function () { return ( !details.rows || !details.rows.length || (details.rows[0].id != self.job_id) ); },
|
|
function (err) {
|
|
// activity is written
|
|
// test.debug("Activity response:", details);
|
|
|
|
var stub = details.rows[0];
|
|
test.debug("Activity first item:", stub);
|
|
|
|
test.ok( !!stub, "Got activity in response" );
|
|
test.ok( stub.id == self.job_id, "Activity ID matches Job ID" );
|
|
test.ok( stub.event == self.event_id, "Activity item Event ID matches Event ID" );
|
|
test.ok( stub.action == "job_run", "Activity item has correct action" );
|
|
|
|
test.done();
|
|
}
|
|
);
|
|
},
|
|
|
|
function testSchedulerEventTiming(test) {
|
|
// test various formats of event timing
|
|
|
|
// timestamp for testing: Epoch 1454797620
|
|
// Sat Feb 6 14:27:00 2016 (PST)
|
|
|
|
var cursor = 1454797620;
|
|
var tz = "America/Los_Angeles";
|
|
|
|
test.ok( !!cronicle.checkEventTiming( {}, cursor, tz ), "Every minute should run" );
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { minutes: [27] }, cursor, tz ), "Hourly should run" );
|
|
test.ok( !cronicle.checkEventTiming( { minutes: [28] }, cursor, tz ), "Hourly should not run" );
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { hours: [14], minutes: [27] }, cursor, tz ), "Daily should run" );
|
|
test.ok( !!cronicle.checkEventTiming( { hours: [14] }, cursor, tz ), "Daily every minute should run" );
|
|
test.ok( !cronicle.checkEventTiming( { hours: [17], minutes: [27] }, cursor, tz ), "Daily should not run" );
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { weekdays: [6], hours: [14], minutes: [27] }, cursor, tz ), "Weekly should run" );
|
|
test.ok( !!cronicle.checkEventTiming( { weekdays: [6], minutes: [27] }, cursor, tz ), "Weekly hourly should run" );
|
|
test.ok( !cronicle.checkEventTiming( { weekdays: [0], hours: [14], minutes: [27] }, cursor, tz ), "Weekly should not run" );
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { days: [6], hours: [14], minutes: [27] }, cursor, tz ), "Monthly should run" );
|
|
test.ok( !!cronicle.checkEventTiming( { days: [6], minutes: [27] }, cursor, tz ), "Monthly hourly should run" );
|
|
test.ok( !cronicle.checkEventTiming( { days: [5], hours: [14], minutes: [27] }, cursor, tz ), "Monthly should not run" );
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { months: [2], days: [6], hours: [14], minutes: [27] }, cursor, tz ), "Yearly should run" );
|
|
test.ok( !!cronicle.checkEventTiming( { months: [2], minutes: [27] }, cursor, tz ), "Yearly hourly should run" );
|
|
test.ok( !cronicle.checkEventTiming( { months: [12], days: [6], hours: [14], minutes: [27] }, cursor, tz ), "Yearly should not run" );
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { years: [2016], months: [2], days: [6], hours: [14], minutes: [27] }, cursor, tz ), "Single should run" );
|
|
test.ok( !cronicle.checkEventTiming( { years: [2015], months: [2], days: [6], hours: [14], minutes: [27] }, cursor, tz ), "Single should not run" );
|
|
|
|
// now test same timestamp in a different timezone
|
|
tz = "America/New_York";
|
|
|
|
test.ok( !!cronicle.checkEventTiming( { hours: [17], minutes: [27] }, cursor, tz ), "New York should run" );
|
|
test.ok( !cronicle.checkEventTiming( { hours: [14], minutes: [27] }, cursor, tz ), "New York should not run" );
|
|
|
|
test.done();
|
|
},
|
|
|
|
function testUpdateEventForSchedule(test) {
|
|
// update event with hourly timing and a simple shell command
|
|
var self = this;
|
|
|
|
var params = {
|
|
"params": {
|
|
"script": "#!/bin/sh\n\necho \"UNIT TEST STRING\""
|
|
},
|
|
"timing": {
|
|
"minutes": [25] // hourly on the 25th minute
|
|
},
|
|
"plugin": "shellplug",
|
|
"web_hook": api_url + '/app/unit_test_web_hook'
|
|
};
|
|
|
|
storage.listFindUpdate( 'global/schedule', { id: this.event_id }, params, function(err) {
|
|
test.ok( !err, "Failed to update event: " + err );
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
function testSchedulerTick(test) {
|
|
// tick scheduler with false time, which should start our job
|
|
var self = this;
|
|
|
|
test.ok( !!cronicle.state.enabled, "Scheduler state is currently enabled" );
|
|
|
|
// add API handler for testing web hooks
|
|
cronicle.api_unit_test_web_hook = function(args, callback) {
|
|
// hello
|
|
var params = args.params || {};
|
|
|
|
if (self.expect_web_hook && self.current_test && (params.action == 'job_complete')) {
|
|
var test = self.current_test;
|
|
delete self.current_test;
|
|
|
|
self.web_hook_data = params;
|
|
test.ok( !!params, "Got web hook data" );
|
|
test.done();
|
|
}
|
|
|
|
callback({ code: 0 });
|
|
}; // web hook handler
|
|
|
|
// set props for api callback to detect
|
|
self.expect_web_hook = true;
|
|
self.web_hook_data = null;
|
|
self.current_test = test;
|
|
|
|
// setup our fake timestamp to match event timing settings
|
|
var dargs = Tools.getDateArgs( Tools.timeNow(true) );
|
|
dargs.min = 25; // match our event timing
|
|
|
|
// tick the scheduler
|
|
cronicle.schedulerMinuteTick( dargs );
|
|
},
|
|
|
|
function testWebHookData(test) {
|
|
// web hook should have got us here, so let's examine the data
|
|
var job = this.web_hook_data;
|
|
test.debug("Web hook data:", job);
|
|
|
|
delete this.web_hook_data;
|
|
delete this.expect_web_hook;
|
|
|
|
test.ok( !!job, "Got web hook data" );
|
|
test.ok( !!job.id, "Job has an ID", job );
|
|
test.ok( job.id != this.job_id, "Job ID does not match previous job", job );
|
|
test.ok( job.code == 0, "Job is not marked as an error", job );
|
|
test.ok( job.event == this.event_id, "Job Event ID matches", job );
|
|
test.ok( job.category == "general", "Job has correct category", job );
|
|
test.ok( job.plugin == "shellplug", "Job has correct Plugin", job );
|
|
test.ok( !!job.base_app_url, "Job has correct key pulled from config via web hook", job );
|
|
test.ok( job.something_custom == "nonstandard property", "Job has correct custom web hook property", job );
|
|
test.ok( !job.smtp_hostname, "Job does not have config key not in the web hook key list", job );
|
|
|
|
test.done();
|
|
},
|
|
|
|
function testRunFailedEvent(test) {
|
|
// run an event that fails
|
|
var self = this;
|
|
|
|
// set props for api callback to detect
|
|
this.expect_web_hook = true;
|
|
this.web_hook_data = null;
|
|
this.current_test = test;
|
|
|
|
storage.listFind( 'global/schedule', { id: this.event_id }, function(err, event) {
|
|
test.ok( !err, "No error locating event in schedule" );
|
|
test.ok( !!event, "Found event in schedule" );
|
|
|
|
var job = Tools.copyHash( event, true );
|
|
job.params.script = "#!/bin/sh\n\necho \"UNIT TEST DELIBERATE FAILURE\"\nexit 1\n";
|
|
|
|
cronicle.launchJob( job, function(err, jobs) {
|
|
// not doing anything here, as web hook should fire automatically and finish the test
|
|
} );
|
|
} );
|
|
},
|
|
|
|
function testRunFailedResults(test) {
|
|
// make sure failed event really failed
|
|
var job = this.web_hook_data;
|
|
test.debug( "Web hook data: ", job );
|
|
|
|
delete this.web_hook_data;
|
|
delete this.expect_web_hook;
|
|
|
|
test.ok( !!job, "Got web hook data" );
|
|
test.ok( !!job.id, "Job has an ID" );
|
|
test.ok( job.code != 0, "Job is marked as an error" );
|
|
test.ok( !!job.description, "Job has an error description" );
|
|
test.ok( job.event == this.event_id, "Job Event ID matches" );
|
|
test.ok( job.category == "general", "Job has correct category" );
|
|
test.ok( job.plugin == "shellplug", "Job has correct Plugin" );
|
|
|
|
// need rest here, for async logs to finish inserting
|
|
setTimeout( function() {
|
|
test.done();
|
|
}, 500 );
|
|
},
|
|
|
|
function testRunDetachedEvent(test) {
|
|
// run event in detached mode
|
|
var self = this;
|
|
|
|
storage.listFind( 'global/schedule', { id: this.event_id }, function(err, event) {
|
|
test.ok( !err, "No error locating event in schedule" );
|
|
test.ok( !!event, "Found event in schedule" );
|
|
|
|
var job = Tools.copyHash( event, true );
|
|
job.detached = 1;
|
|
|
|
cronicle.launchJob( job, function(err, jobs) {
|
|
test.ok( !err, "No error launching job" );
|
|
test.ok( !!jobs, "Got array of launched jobs" );
|
|
test.ok( jobs.length == 1, "Launched exactly one job" );
|
|
test.ok( jobs[0].id, "Got Job ID" );
|
|
|
|
// save new job id
|
|
self.detached_job_id = jobs[0].id;
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
function testWaitForDetachedQueue(test) {
|
|
// monitor queue directory until finished file shows up
|
|
var self = this;
|
|
var file_spec = server.config.get('queue_dir') + '/*.json';
|
|
var files_found = false;
|
|
|
|
async.doWhilst(
|
|
function (callback) {
|
|
// poll queue dir
|
|
glob(file_spec, {}, function (err, files) {
|
|
// got task files
|
|
if (files && files.length) {
|
|
files_found = true;
|
|
}
|
|
setTimeout( callback, 250 );
|
|
} );
|
|
},
|
|
function () { return (!files_found); },
|
|
function (err) {
|
|
// got files, we're done
|
|
test.done();
|
|
}
|
|
);
|
|
},
|
|
|
|
function testFinishDetachedEvent(test) {
|
|
// force external queue to run to process finished event
|
|
|
|
// set props for api callback to detect
|
|
this.expect_web_hook = true;
|
|
this.web_hook_data = null;
|
|
this.current_test = test;
|
|
|
|
cronicle.monitorExternalQueue();
|
|
// not calling test.done() as it should fire via web hook
|
|
},
|
|
|
|
function testDetachedWebHookData(test) {
|
|
// web hook should have got us here, so let's examine the data
|
|
var job = this.web_hook_data;
|
|
test.debug( "Detached web hook data: ", job );
|
|
|
|
delete this.web_hook_data;
|
|
delete this.expect_web_hook;
|
|
|
|
test.ok( !!job, "Got web hook data" );
|
|
test.ok( !!job.id, "Job has an ID" );
|
|
test.ok( job.id == this.detached_job_id, "Job ID matches our detached job" );
|
|
test.ok( job.code == 0, "Job is not marked as an error" );
|
|
test.ok( job.event == this.event_id, "Job Event ID matches" );
|
|
test.ok( job.category == "general", "Job has correct category" );
|
|
test.ok( job.plugin == "shellplug", "Job has correct Plugin" );
|
|
|
|
// need rest here, for async logs to finish inserting,
|
|
// before we delete the associated event (which also deletes logs!)
|
|
setTimeout( function() {
|
|
test.done();
|
|
}, 500 );
|
|
},
|
|
|
|
// app/delete_event
|
|
|
|
function testAPIDeleteEvent(test) {
|
|
// test app/delete_event api
|
|
var self = this;
|
|
var params = {
|
|
"id": this.event_id,
|
|
"session_id": session_id
|
|
};
|
|
|
|
request.json( api_url + '/app/delete_event', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that event actually got deleted from storage
|
|
storage.listFind( 'global/schedule', { id: self.event_id }, function(err, event) {
|
|
|
|
test.ok( !err, "No error expected for missing data" );
|
|
test.ok( !event, "Data record should be null (deleted)" );
|
|
|
|
delete self.event_id;
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
|
|
|
|
// TODO: app/abort_job
|
|
// TODO: app/abort_jobs
|
|
|
|
// TODO: catch-up event
|
|
|
|
|
|
|
|
// app/update_master_state
|
|
|
|
function testAPIUpdateMasterState(test) {
|
|
// test app/update_master_state api
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id,
|
|
"enabled": 0
|
|
};
|
|
|
|
// pre-check that state is currently enabled
|
|
test.ok( !!cronicle.state.enabled, "Scheduler state is currently enabled" );
|
|
|
|
// disable it via API
|
|
request.json( api_url + '/app/update_master_state', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that change took effect
|
|
test.ok( !cronicle.state.enabled, "State is actually disabled" );
|
|
|
|
test.done();
|
|
} );
|
|
},
|
|
|
|
// user/logout
|
|
|
|
function testAPIUserLogout(test) {
|
|
// test user/logout api
|
|
var self = this;
|
|
var params = {
|
|
"session_id": session_id
|
|
};
|
|
|
|
request.json( api_url + '/user/logout', params, function(err, resp, data) {
|
|
|
|
test.ok( !err, "No error requesting API" );
|
|
test.ok( resp.statusCode == 200, "HTTP 200 from API" );
|
|
test.ok( "code" in data, "Found code prop in JSON response" );
|
|
test.ok( data.code == 0, "Code is zero (no error)" );
|
|
|
|
// check to see that session actually got deleted from storage
|
|
storage.get('sessions/' + session_id, function(err, data) {
|
|
|
|
test.ok( !!err, "Error expected for missing session" );
|
|
test.ok( !data, "Data record should be null (deleted)" );
|
|
|
|
test.done();
|
|
} );
|
|
} );
|
|
}
|
|
|
|
], // tests array
|
|
|
|
tearDown: function (callback) {
|
|
// always called right before shutdown
|
|
this.logDebug(1, "Running tearDown");
|
|
|
|
// add some delays here so async storage ops can complete
|
|
setTimeout( function() {
|
|
server.shutdown( function() {
|
|
// delete our mess after a short rest (just so no errors are logged)
|
|
setTimeout( function() {
|
|
try { cp.execSync('rm -rf data/unittest'); }
|
|
catch (e) {;}
|
|
|
|
callback();
|
|
}, 500 );
|
|
} );
|
|
}, 500 );
|
|
}
|
|
};
|