diff --git a/.gitignore b/.gitignore index 560f1313..a0f07a89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -node_modules +/node_modules *.log *.log *.pid diff --git a/test/fixtures/module-fixture/node_modules/pmx/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/.npmignore new file mode 100644 index 00000000..3fcc6400 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/.npmignore @@ -0,0 +1,7 @@ +node_modules +*.log +*.log +test/child +*.iml +.idea/** +sample/pmx-server-stats diff --git a/test/fixtures/module-fixture/node_modules/pmx/.travis.yml b/test/fixtures/module-fixture/node_modules/pmx/.travis.yml new file mode 100644 index 00000000..7e7d37b1 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +branches: + only: + - master + - development +node_js: + - "iojs" + - "0.12" + - "0.11" + - "0.10" diff --git a/test/fixtures/module-fixture/node_modules/pmx/CHANGELOG.md b/test/fixtures/module-fixture/node_modules/pmx/CHANGELOG.md new file mode 100644 index 00000000..e89f1868 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/CHANGELOG.md @@ -0,0 +1,18 @@ + +# 0.2.27 + +- Remove debug message +- Rename module +- Auto instanciation + +# 0.2.25 + +- Add ip address on each transaction + +# 0.2.24 + +- Add unit option for Histogram and Meter + +# 0.2.23 + +- Include Counter, Meter, Metric and Histogram diff --git a/test/fixtures/module-fixture/node_modules/pmx/README.md b/test/fixtures/module-fixture/node_modules/pmx/README.md new file mode 100644 index 00000000..a06ae2e5 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/README.md @@ -0,0 +1,303 @@ + +# Driver for Keymetrics + +![Keymetrics](https://keymetrics.io/assets/images/application-demo.png) + +PMX is a module that allows you to create advanced interactions with Keymetrics. + +With it you can: +- Trigger remote actions / functions +- Analyze custom metrics / variables (with utilities like Histogram/Counter/Metric/Meters) +- Report errors (uncaught exceptions and custom errors) +- Emit events +- Analyze HTTP latency + +# Installation + +![Build Status](https://api.travis-ci.org/keymetrics/pmx.png?branch=master) + +Install PMX and add it to your package.json via: + +```bash +$ npm install pmx --save +``` + +Then init the module to monitor HTTP, Errors and diverse metrics. +```javascript +var pmx = require('pmx').init(); // By default everything is enabled and ignore_routes is empty +``` +Or choose what to monitor. +```javascript +var pmx = require('pmx').init({ + http : true, // HTTP routes logging (default: true) + ignore_routes : [/socket\.io/, /notFound/], // Ignore http routes with this pattern (Default: []) + errors : true, // Exceptions loggin (default: true) + custom_probes : true, // Custom probes (default: true) + network : true, // Traffic usage monitoring (default: false) + ports : true // Shows which ports your app is listening on (default: false) +}); +``` + +# Custom monitoring + +## Emit Events + +Emit events and get historical and statistics: + +```javascript +var pmx = require('pmx'); + +pmx.emit('user:register', { + user : 'Alex registered', + email : 'thorustor@gmail.com' +}); +``` + +## Custom Action + +Trigger function from Keymetrics + +### Long running + +```javascript +var pmx = require('pmx'); + +pmx.action('db:clean', { comment : 'Description for this action' }, function(reply) { + clean.db(function() { + /** + * reply() must be called at the end of the action + */ + reply({success : true}); + }); +}); +``` + +## Errors + +Catch uncaught exceptions: +```javascript +var pmx = require('pmx').init(); +``` + +Attach more data from errors that happens in Express: +```javascript +var pmx = require('pmx'); + +app.get('/' ...); +app.post(...); + +app.use(pmx.expressErrorHandler()); +``` + +Trigger custom errors: +```javascript +var pmx = require('pmx'); + +pmx.notify({ success : false }); + +pmx.notify('This is an error'); + +pmx.notify(new Error('This is an error')); +``` + +## TCP network usage monitoring + +If you enable the flag `network: true` when you init pmx it will show network usage datas (download and upload) in realtime. + +If you enable the flag `ports: true` when you init pmx it will show which ports your app is listenting on. + + +## HTTP latency analysis + +Monitor routes, latency and codes. REST compliant. + +```javascript +pmx.http(); // You must do this BEFORE any require('http') +``` +Ignore some routes by passing a list of regular expressions. +```javascript +pmx.http({ + http : true, // (Default: true) + ignore_routes : [/socket\.io/, /notFound/] // Ignore http routes with this pattern (Default: []) +}); +``` +This can also be done via pmx.init() +```javascript +pmx.init({ + http : true, // (Default: true) + ignore_routes : [/socket\.io/, /notFound/] // Ignore http routes with this pattern (Default: []) +}); +``` + +**This module is enabled by default if you called pmx with the init() function.** + +## Measure + +Measure critical segments of you code thanks to 4 kind of probes: + +- Simple metrics: Values that can be read instantly + - Monitor variable value +- Counter: Things that increment or decrement + - Downloads being processed, user connected +- Meter: Things that are measured as events / interval + - Request per minute for a http server +- Histogram: Keeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution + - Monitor the mean of execution of a query into database + +#### Common options + +- `name` : The probe name as is will be displayed on the **Keymetrics** dashboard +- `agg_type` : This param is optionnal, it can be `sum`, `max`, `min`, `avg` (default) or `none`. It will impact the way the probe data are aggregated within the **Keymetrics** backend. Use `none` if this is irrelevant (eg: constant or string value). + + +### Metric + +Values that can be read instantly. + +```javascript +var probe = pmx.probe(); + +var metric = probe.metric({ + name : 'Realtime user', + agg_type: 'max', + value : function() { + return Object.keys(users).length; + } +}); +``` + +### Counter + +Things that increment or decrement. + +```javascript +var probe = pmx.probe(); + +var counter = probe.counter({ + name : 'Downloads', + agg_type: 'sum' +}); + +http.createServer(function(req, res) { + counter.inc(); + req.on('end', function() { + counter.dec(); + }); +}); +``` + +### Meter + +Things that are measured as events / interval. + +```javascript +var probe = pmx.probe(); + +var meter = probe.meter({ + name : 'req/sec', + samples : 1, + timeframe : 60 +}); + +http.createServer(function(req, res) { + meter.mark(); + res.end({success:true}); +}); +``` +#### Options + +**samples** option is the rate unit. Defaults to **1** sec. + +**timeframe** option is the timeframe over which events will be analyzed. Defaults to **60** sec. + +### Histogram + +Keeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution. + +```javascript +var probe = pmx.probe(); + +var histogram = probe.histogram({ + name : 'latency', + measurement : 'mean' +}); + +var latency = 0; + +setInterval(function() { + latency = Math.round(Math.random() * 100); + histogram.update(latency); +}, 100); +``` + +#### Options + +**measurement** option can be: + +- min: The lowest observed value. +- max: The highest observed value. +- sum: The sum of all observed values. +- variance: The variance of all observed values. +- mean: The average of all observed values. +- stddev: The stddev of all observed values. +- count: The number of observed values. +- median: 50% of all values in the resevoir are at or below this value. +- p75: See median, 75% percentile. +- p95: See median, 95% percentile. +- p99: See median, 99% percentile. +- p999: See median, 99.9% percentile. + +## Expose data (JSON object) + +```javascript +pmx.transpose('variable name', function() { return my_data }); + +// or + +pmx.tranpose({ + name : 'variable name', + value : function() { return my_data; } +}); +``` + +## Modules + +### Simple app + +``` +process.env.MODULE_DEBUG = true; + +var pmx = require('pmx'); + +var conf = pmx.initModule(); +``` + +# Beta + +### Long running with data emitter (scoped action) + +A scoped action is an action that can emit logs related to this action. + +```javascript +var pmx = require('pmx'); + +pmx.scopedAction('scoped:action', function(options, res) { + var i = setInterval(function() { + // Emit progress data + if (error) + res.error('oops'); + else + res.send('this is a chunk of data'); + }, 1000); + + setTimeout(function() { + clearInterval(i); + return res.end(); + }, 8000); +}); +``` + + +# License + +MIT diff --git a/test/fixtures/module-fixture/node_modules/pmx/examples/package.json b/test/fixtures/module-fixture/node_modules/pmx/examples/package.json new file mode 100644 index 00000000..acac5aa1 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/examples/package.json @@ -0,0 +1,21 @@ +{ + "name": "example-module", + "version": "0.3.21", + "description": "Keymetrics++ and PM2 adapter", + "main": "scoped-action.js", + "dependencies": { + }, + "scripts": { + "test": "DEBUG='axm:*' mocha test/*.mocha.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/keymetrics/pmx.git" + }, + "config" : { + "aconfig-var" : true, + "var2" : false + }, + "author": "Keymetrics I/O", + "license": "MIT" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/examples/scoped-action.js b/test/fixtures/module-fixture/node_modules/pmx/examples/scoped-action.js new file mode 100644 index 00000000..c6280c1b --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/examples/scoped-action.js @@ -0,0 +1,70 @@ + +var pmx = require('..'); + + +var conf = pmx.initModule({ + + widget : { + type : 'generic', + logo : 'https://app.keymetrics.io/img/logo/keymetrics-300.png', + + // 0 = main element + // 1 = secondary + // 2 = main border + // 3 = secondary border + theme : ['#141A1F', '#222222', '#3ff', '#3ff'], + + el : { + probes : true, + actions : true + }, + + block : { + actions : true, + issues : true, + meta : true + } + + // Status + // Green / Yellow / Red + } +}); + + +pmx.scopedAction('testo', function(data, emitter) { + var i = setInterval(function() { + emitter.send('datard'); + }, 100); + + setTimeout(function() { + + emitter.end('end'); + clearInterval(i); + }, 3000); +}); + +var spawn = require('child_process').spawn; + +pmx.scopedAction('long running lsof', function(data, res) { + var child = spawn('lsof', []); + + child.stdout.on('data', function(chunk) { + chunk.toString().split('\n').forEach(function(line) { + res.send(line); + }); + }); + + child.stdout.on('end', function(chunk) { + res.end('end'); + }); + +}); + + +pmx.action('simple action', function(reply) { + return reply({success:true}); +}); + +pmx.action('simple with arg', function(opts,reply) { + return reply(opts); +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/index.js b/test/fixtures/module-fixture/node_modules/pmx/index.js new file mode 100644 index 00000000..055544a9 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/index.js @@ -0,0 +1,2 @@ + +module.exports = exports = require("./lib"); diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/Probe.js b/test/fixtures/module-fixture/node_modules/pmx/lib/Probe.js new file mode 100644 index 00000000..d5f73c4f --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/Probe.js @@ -0,0 +1,180 @@ + +var Counter = require('./utils/probes/Counter.js'); +var Histogram = require('./utils/probes/Histogram.js'); +var Meter = require('./utils/probes/Meter.js'); + +var Transport = require('./utils/transport.js'); + +var debug = require('debug')('axm:probe'); +var Probe = {}; + +Probe._started = false; +Probe._var = {}; + +Probe.AVAILABLE_AGG_TYPES = ['avg', 'min', 'max', 'sum', 'none']; +Probe.AVAILABLE_MEASUREMENTS = [ + 'min', + 'max', + 'sum', + 'count', + 'variance', + 'mean', + 'stddev', + 'median', + 'p75', + 'p95', + 'p99', + 'p999' +]; +Probe.default_aggregation = 'avg'; + +function cookData(data) { + var cooked_data = {}; + + Object.keys(data).forEach(function(probe_name) { + var value = data[probe_name].value; + + if (typeof(value) == 'function') + value = value(); + else + value = value; + + cooked_data[probe_name] = { + value: value + }; + + if (data[probe_name].agg_type && + data[probe_name].agg_type != 'none') + cooked_data[probe_name].agg_type = data[probe_name].agg_type; + + }); + return cooked_data; +}; + +Probe.probe = function() { + + if (Probe._started == false) { + Probe._started = true; + + setInterval(function() { + Transport.send({ + type : 'axm:monitor', + data : cookData(Probe._var) + }); + }, 990); + } + + return { + /** + * This reflect data to keymetrics + * pmx.transpose('prop name', fn) + * + * or + * + * pmx.transpose({ + * name : 'variable name', + * data : function() { return value } + * }); + */ + transpose : function(variable_name, reporter) { + if (typeof variable_name === 'object') { + reporter = variable_name.data; + variable_name = variable_name.name; + } + + if (typeof reporter !== 'function') { + return console.error('[PMX] reporter is not a function'); + } + + Probe._var[variable_name] = { + value: reporter + }; + }, + metric : function(opts) { + var agg_type = opts.agg_type || Probe.default_aggregation; + + if (!opts.name) + return console.error('[Probe][Metric] Name not defined'); + if (typeof(opts.value) === 'undefined') + return console.error('[Probe][Metric] Value not defined'); + if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) + return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); + + if (opts.value) + Probe._var[opts.name] = { + value: opts.value, + agg_type: agg_type + }; + + return { + val : function() { + var value = Probe._var[opts.name].value; + + if (typeof(value) == 'function') + value = value(); + + return value; + }, + set : function(dt) { Probe._var[opts.name].value = dt } + } + }, + histogram : function(opts) { + if (!opts.name) + return console.error('[Probe][Histogram] Name not defined'); + opts.measurement = opts.measurement || 'mean'; + opts.unit = opts.unit || ''; + var agg_type = opts.agg_type || Probe.default_aggregation; + + if (Probe.AVAILABLE_MEASUREMENTS.indexOf(opts.measurement) == -1) + return console.error('[Probe][Histogram] Measure type %s does not exists', opts.measurement); + if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) + return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); + + var histogram = new Histogram(opts); + + Probe._var[opts.name] = { + value: function() { return (Math.round(histogram.val() * 100) / 100) + '' + opts.unit }, + agg_type: agg_type + }; + + return histogram; + }, + meter : function(opts) { + var agg_type = opts.agg_type || Probe.default_aggregation; + + if (!opts.name) + return console.error('[Probe][Meter] Name not defined'); + if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) + return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); + + opts.unit = opts.unit || ''; + + var meter = new Meter(opts); + + Probe._var[opts.name] = { + value: function() { return meter.val() + '' + opts.unit }, + agg_type: agg_type + }; + + return meter; + }, + counter : function(opts) { + var agg_type = opts.agg_type || Probe.default_aggregation; + + if (!opts.name) + return console.error('[Probe][Counter] Name not defined'); + if (Probe.AVAILABLE_AGG_TYPES.indexOf(agg_type) == -1) + return console.error("[Probe][Metric] Unknown agg_type: %s", agg_type); + + var counter = new Counter(); + + Probe._var[opts.name] = { + value: function() { return counter.val() }, + agg_type: agg_type + }; + return counter; + }, + } +}; + +module.exports = Probe; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/actions.js b/test/fixtures/module-fixture/node_modules/pmx/lib/actions.js new file mode 100644 index 00000000..dd807d58 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/actions.js @@ -0,0 +1,147 @@ +var domain = require('domain'); +var debug = require('debug')('axm:events'); +var Common = require('./common.js'); +var Transport = require('./utils/transport.js'); + +var Actions = {}; + +Actions.action = function(action_name, opts, fn) { + if (!fn) { + fn = opts; + opts = null; + } + + if (!action_name) + return console.error('[PMX] action.action_name is missing'); + if (!fn) + return console.error('[PMX] emit.data is mission'); + + if (!process.send) { + debug('Process not running within PM2'); + return false; + } + + // Notify the action + Transport.send({ + type : 'axm:action', + data : { + action_name : action_name, + opts : opts, + arity : fn.length + } + }); + + function reply(data) { + Transport.send({ + type : 'axm:reply', + data : { + return : data, + action_name : action_name + } + }); + } + + process.on('message', function(data) { + if (!data) return false; + + // In case 2 arguments has been set but no options has been transmitted + if (fn.length === 2 && typeof(data) === 'string' && data === action_name) + return fn({}, reply); + + // In case 1 arguments has been set but options has been transmitted + if (fn.length === 1 && typeof(data) === 'object' && data.msg === action_name) + return fn(reply); + + /** + * Classical call + */ + if (typeof(data) === 'string' && data === action_name) + return fn(reply); + + /** + * If data is an object == v2 protocol + * Pass the opts as first argument + */ + if (typeof(data) === 'object' && data.msg === action_name) + return fn(data.opts, reply); + }); +}; + +Actions.scopedAction = function(action_name, fn) { + + if (!action_name) + return console.error('[PMX] action.action_name is missing'); + if (!fn) + return console.error('[PMX] callback is missing'); + + if (!process.send) { + debug('Process not running within PM2'); + return false; + } + + // Notify the action + Transport.send({ + type : 'axm:action', + data : { + action_name : action_name, + action_type : 'scoped' + } + }); + + process.on('message', function(data) { + if (!data + || data.uuid === undefined + || data.action_name === undefined) + return false; + + if (data.action_name === action_name) { + var res = { + send : function(dt) { + Transport.send({ + type : 'axm:scoped_action:stream', + data : { + data : dt, + uuid : data.uuid, + action_name : action_name + } + }); + }, + error : function(dt) { + Transport.send({ + type : 'axm:scoped_action:error', + data : { + data : dt, + uuid : data.uuid, + action_name : action_name + } + }); + }, + end : function(dt) { + Transport.send({ + type : 'axm:scoped_action:end', + data : { + data : dt, + uuid : data.uuid, + action_name : action_name + } + }); + } + }; + + var d = domain.create(); + + d.on('error', function(err) { + res.error({error : err}); + setTimeout(function() { + process.exit(1); + }, 300); + }); + + d.run(function() { + fn(data.opts || null, res); + }); + } + }); +}; + +module.exports = Actions; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/common.js b/test/fixtures/module-fixture/node_modules/pmx/lib/common.js new file mode 100644 index 00000000..c1bce910 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/common.js @@ -0,0 +1,6 @@ + +var Common = module.exports = {}; + +Common.getDate = function getDate() { + return Math.round(Date.now() / 1000); +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/events.js b/test/fixtures/module-fixture/node_modules/pmx/lib/events.js new file mode 100644 index 00000000..f09496bc --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/events.js @@ -0,0 +1,26 @@ + +var debug = require('debug')('axm:events'); +var Transport = require('./utils/transport.js'); +var Common = require('./common.js'); +var stringify = require('json-stringify-safe'); + +var Events = {}; + +Events.emit = function(name, data) { + if (!name) + return console.error('[AXM] emit.name is missing'); + if (!data) + return console.error('[AXM] emit.data is missing'); + + var inflight_obj = JSON.parse(stringify(data)); + + inflight_obj.__name = name; + + Transport.send({ + type : 'human:event', + data : inflight_obj + }, true); + return false; +}; + +module.exports = Events; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/index.js b/test/fixtures/module-fixture/node_modules/pmx/lib/index.js new file mode 100644 index 00000000..55803e4a --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/index.js @@ -0,0 +1,66 @@ + +var Events = require('./events.js'); +var Actions = require('./actions.js'); +var Notify = require('./notify.js'); +var Transaction = require('./transaction.js'); +var Network = require('./network.js'); +var Monitor = require('./monitor.js'); +var Profiling = require('./probes/profiling.js'); +var Probe = require('./Probe.js'); + +var Pm2Module = require('./pm2_module.js'); + +var util = require('util'); + +var Export = {}; + +/** + * Flatten API + */ +util._extend(Export, Events); +util._extend(Export, Actions); +util._extend(Export, Notify); +util._extend(Export, Monitor); +util._extend(Export, Pm2Module); +util._extend(Export, Probe); +util._extend(Export, Transaction); +util._extend(Export, Network); +util._extend(Export, Profiling); + +Export.init = function(opts) { + if (!opts) opts = {}; + + opts = util._extend({ + http : true, + http_latency : 200, + http_code : 500, + ignore_routes : [], + profiling : true, + errors : true, + custom_probes : true, + network : false, + ports : false + }, opts); + + if (opts.ports) + Export.catchPorts(); + if (opts.network) + Export.catchTraffic(); + Export.http(opts); + Export.catchAll(opts); + + if (opts.profiling) + Profiling.v8Profiling(Export); + + if (opts.custom_probes) { + // Event loop monitoring + require('./probes/pacemaker.js')(Export); + } + return this; +}; + +/** + * Export + */ + +module.exports = Export; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/monitor.js b/test/fixtures/module-fixture/node_modules/pmx/lib/monitor.js new file mode 100644 index 00000000..7c519506 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/monitor.js @@ -0,0 +1,49 @@ + +var Transport = require('./utils/transport.js'); +var debug = require('debug')('axm:monitor'); + +var Monitor = {}; + +function cookData(data) { + var cooked_data = {}; + + Object.keys(data).forEach(function(probe_name) { + if (typeof(data[probe_name]) == 'function') + cooked_data[probe_name] = data[probe_name](); + else + cooked_data[probe_name] = data[probe_name]; + }); + return cooked_data; +}; + +function enableProbes(custom_namespace) { + if (!custom_namespace) + custom_namespace = 'axm'; + + if (!global[custom_namespace]) + global[custom_namespace] = {}; + + if (this.interval) + return global[custom_namespace]; + + this.interval = setInterval(function() { + Transport.send({ + type : 'axm:monitor', + data : cookData(global[custom_namespace]) + }); + }, 990); + + return global[custom_namespace]; +}; + +function stopProbing() { + clearInterval(this.interval); +} + +Monitor.enableProbes = enableProbes; +Monitor.enableProbe = enableProbes; + +Monitor.stopProbe = stopProbing; +Monitor.stopProbes = stopProbing; + +module.exports = Monitor; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/network.js b/test/fixtures/module-fixture/node_modules/pmx/lib/network.js new file mode 100644 index 00000000..c83e513e --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/network.js @@ -0,0 +1,116 @@ +var net_module = require('net'); +var Probe = require('./Probe.js'); + +var Network = module.exports = {}; + +Network.catchPorts = function() { + var ports_list = []; + var opened_ports = 'N/A'; + + Probe.probe().metric({ + name : 'Open ports', + value : function() { return opened_ports; } + }); + + var original_listen = net_module.Server.prototype.listen; + + net_module.Server.prototype.listen = function() { + var port = parseInt(arguments[0]); + + if (!isNaN(port) && ports_list.indexOf(port) === -1) { + ports_list.push(port); + opened_ports = ports_list.sort().join(); + } + + this.once('close', function() { + if (ports_list.indexOf(port) > -1) { + ports_list.splice(ports_list.indexOf(port), 1); + opened_ports = ports_list.sort().join(); + } + }); + + return original_listen.apply(this, arguments); + }; +}; + +Network.catchTraffic = function() { + var download = 0; + var upload = 0; + var up = '0 B/sec'; + var down = '0 B/sec'; + + var filter = function(bytes) { + var to_fixed = 0; + + if (bytes === 0) + ; + else if (bytes < 1024) + to_fixed = 6; + else if (bytes < (1024 * 1024)) + to_fixed = 3; + else + to_fixed = 2; + + bytes = (bytes / (1024 * 1024)).toFixed(to_fixed); + + var cut_zeros = 0; + + for (var i = (bytes.length - 1); i > 0; --i) { + if (bytes[i] === '.') { + ++cut_zeros; + break; + } + if (bytes[i] !== '0') + break; + ++cut_zeros; + } + + if (cut_zeros > 0) + bytes = bytes.slice(0, -(cut_zeros)); + + return (bytes + ' MB/s'); + }; + + setInterval(function() { + up = filter(upload); + down = filter(download); + upload = 0; + download = 0; + }, 999); + + Probe.probe().metric({ + name : 'Network Download', + agg_type : 'sum', + value : function() { return down; } + }); + + Probe.probe().metric({ + name : 'Network Upload', + agg_type : 'sum', + value : function() { return up; } + }); + + var original_write = net_module.Socket.prototype.write; + + net_module.Socket.prototype.write = function(data) { + if (data.length) + upload += data.length; + return original_write.apply(this, arguments); + }; + + var original_read = net_module.Socket.prototype.read; + + net_module.Socket.prototype.read = function() { + + if (!this.monitored) { + this.monitored = true; + + this.on('data', function(data) { + if (data.length) + download += data.length; + }); + } + + return original_read.apply(this, arguments); + }; +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/notify.js b/test/fixtures/module-fixture/node_modules/pmx/lib/notify.js new file mode 100644 index 00000000..49bd5a78 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/notify.js @@ -0,0 +1,117 @@ + +var debug = require('debug')('axm:notify'); +var util = require('util'); +var Common = require('./common.js'); + +var Options = require('./pm2_module.js'); + +var Transport = require('./utils/transport.js'); + +var Notify = {}; + + +var jsonize = function(err, filter, space) { + if (typeof(err) != 'object') + return err; + + var plainObject = {}; + + Object.getOwnPropertyNames(err).forEach(function(key) { + plainObject[key] = err[key]; + }); + return plainObject; +}; + +Notify.catchAll = function(opts) { + + if (opts === undefined) + opts = { errors : true }; + + Options.configureModule({ + error : opts.errors + }); + + if (process.env.exec_mode == 'cluster_mode') + return false; + + var catchException = function(err) { + //debug(err.stack || err); + Transport.send({ + type : 'process:exception', + data : jsonize(err) + }, true); + console.error(err.stack || err); + process.exit(255); + }; + + if (opts.errors === true + && util.inspect(process.listeners('uncaughtException')).length === 2) { + process.once('uncaughtException', catchException); + } + else if (opts.errors === false + && util.inspect(process.listeners('uncaughtException')).length !== 2) { + process.removeAllListeners('uncaughtException'); + } +}; + +Notify._interpretError = function(err) { + var s_err = {}; + + if (typeof(err) === 'string') { + // Simple string processing + s_err = new Error(err); + } + else if (!(err instanceof Error) && typeof(err) === 'object') { + // JSON processing + s_err = new Error(JSON.stringify(err)); + s_err.data = err; + } + else if (err instanceof Error) { + // Error object type processing + s_err = err; + } + + return jsonize(s_err); +}; + +Notify.notify = function(err) { + var ret_err = this._interpretError(err); + + // full_err + //debug(ret_err); + + Transport.send({ + type : 'process:exception', + data : ret_err + }, true); + + return ret_err; +}; + +Notify.expressErrorHandler = function() { + var self = this; + + Options.configureModule({ + error : true + }); + + return function errorHandler(err, req, res, next) { + if (res.statusCode < 400) res.statusCode = 500; + + //debug(err.stack || err); + + err.url = req.url; + err.component = req.url; + err.action = req.method; + err.params = req.body; + err.session = req.session; + + Transport.send({ + type : 'process:exception', + data : jsonize(err) + }, true); + return next(err); + }; +}; + +module.exports = Notify; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/pm2_module.js b/test/fixtures/module-fixture/node_modules/pmx/lib/pm2_module.js new file mode 100644 index 00000000..7b3b8aae --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/pm2_module.js @@ -0,0 +1,110 @@ + +var debug = require('debug')('axm:events'); +var Transport = require('./utils/transport.js'); +var path = require('path'); +var fs = require('fs'); +var util = require('util'); + +var Options = {}; + +Options.configureModule = function(opts) { + if (!this.running) { + this.running = true; + /* Avoid automatic exit of the script */ + + setInterval(function() {}, 1000); + } + + Transport.send({ + type : 'axm:option:configuration', + data : opts + }, false); +}; + +/** + * Load config and merge with data from package.json + */ +Options.loadConfig = Options.initModule = function(conf) { + var package_filepath = path.resolve(path.dirname(require.main.filename), 'package.json'); + + if (!conf) + conf = {}; + + if (!conf.module_conf) + conf.module_conf = {}; + + conf = util._extend(conf, { + errors : false, + latency : false, + versioning : false, + show_module_meta : false + }); + + /** + * Merge package.json metadata + */ + try { + var package_json = require(package_filepath); + + conf.module_version = package_json.version; + conf.module_name = package_json.name; + conf.description = package_json.description; + + if (package_json.config) { + conf = util._extend(conf, package_json.config); + conf.module_conf = package_json.config; + } + } catch(e) { + throw new Error('[PMX] package.json problem (not found or mal formated', e); + } + + /** + * If custom variables has been set, merge with returned configuration + */ + try { + if (process.env[package_json.name]) { + conf = util._extend(conf, JSON.parse(process.env[package_json.name])); + conf.module_conf = util._extend(conf.module_conf, JSON.parse(process.env[package_json.name])); + } + } catch(e) { + console.error(e); + console.error('Ezssadrror while parsing configuration in environment (%s)', package_json.name); + } + + Options.configureModule(conf); + + return conf; +}; + +Options.getPID = function(file) { + if (typeof(file) === 'number') + return file; + return parseInt(fs.readFileSync(file).toString()); +}; + +Options.resolvePidPaths = function(filepaths) { + if (typeof(filepaths) === 'number') + return filepaths; + + function detect(filepaths) { + var content = ''; + + filepaths.some(function(filepath) { + try { + content = fs.readFileSync(filepath); + } catch(e) { + return false; + } + return true; + }); + + return content.toString().trim(); + } + + var ret = parseInt(detect(filepaths)); + + return isNaN(ret) ? null : ret; +}; + + +module.exports = Options; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/probes/pacemaker.js b/test/fixtures/module-fixture/node_modules/pmx/lib/probes/pacemaker.js new file mode 100644 index 00000000..f16ceda6 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/probes/pacemaker.js @@ -0,0 +1,19 @@ + +module.exports = function(pmx) { + var TIME_INTERVAL = 1000; + + var oldTime = process.hrtime(); + + var histogram = pmx.probe().histogram({ + name : 'Loop delay', + measurement : 'mean', + unit : 'ms' + }); + + setInterval(function() { + var newTime = process.hrtime(); + var delay = (newTime[0] - oldTime[0]) * 1e3 + (newTime[1] - oldTime[1]) / 1e6 - TIME_INTERVAL; + oldTime = newTime; + histogram.update(delay); + }, TIME_INTERVAL); +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/probes/profiling.js b/test/fixtures/module-fixture/node_modules/pmx/lib/probes/profiling.js new file mode 100644 index 00000000..806aad00 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/probes/profiling.js @@ -0,0 +1,121 @@ + +var debug = require('debug')('axm:profiling'); +var os = require('os'); +var path = require('path'); +var fs = require('fs'); + +var Options = require('../pm2_module.js'); + +var Profiling = module.exports = {}; + +Profiling.exposeProfiling = function(pmx, profiler_path) { + try { + var profiler = require(profiler_path); + } catch(e) { + debug('v8-profiler module not installed', e); + return false; + } + + debug('v8-profiler sucessfully enabled'); + + /** + * Tell Keymetrics that profiling is possible + * (flag available in axm_options object) + */ + Options.configureModule({ + heapdump : true + }); + + /** + * Heap snapshot + */ + pmx.action('km:heapdump', function(reply) { + var dump_file = path.join(os.tmpDir(), Date.now() + '.heapsnapshot'); + + var snapshot = profiler.takeSnapshot('km-heap-snapshot'); + var buffer = ''; + + snapshot.serialize( + function iterator(data, length) { + buffer += data; + }, function complete() { + fs.writeFile(dump_file, buffer, function (err) { + debug('Heap dump file flushed (e=', err); + + if (err) { + return reply({ + success : false, + err : err + }); + } + return reply({ + success : true, + heapdump : true, + dump_file : dump_file + }); + }); + } + ); + }); + + /** + * CPU profiling snapshot + */ + var ns_cpu_profiling = 'km-cpu-profiling'; + var cpu_dump_file = path.join(os.tmpDir(), Date.now() + '.cpuprofile'); + + pmx.action('km:cpu:profiling:start', function(reply) { + profiler.startProfiling(ns_cpu_profiling); + return reply({ success : true }); + }); + + pmx.action('km:cpu:profiling:stop', function(reply) { + var cpu = profiler.stopProfiling(ns_cpu_profiling); + + fs.writeFile(cpu_dump_file, JSON.stringify(cpu), function(err) { + if (err) { + return reply({ + success : false, + err : err + }); + } + return reply({ + success : true, + cpuprofile : true, + dump_file : cpu_dump_file + }); + }); + }); + +}; + +/** + * Discover v8-profiler + */ +Profiling.detectV8Profiler = function(cb) { + var require_paths = require.main.paths; + + (function look_for_profiler(require_paths) { + if (!require_paths[0]) + return cb(new Error('Module not found')); + + var profiler_path = path.join(require_paths[0], 'v8-profiler'); + + fs.exists(profiler_path, function(exist) { + if (exist) + return cb(null, profiler_path); + + require_paths.shift(); + return look_for_profiler(require_paths); + }); + return false; + })(require_paths); +}; + +Profiling.v8Profiling = function(pmx) { + Profiling.detectV8Profiler(function(err, profiler_path) { + if (err) + return false; + return Profiling.exposeProfiling(pmx, profiler_path); + }); +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/transaction.js b/test/fixtures/module-fixture/node_modules/pmx/lib/transaction.js new file mode 100644 index 00000000..d076f154 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/transaction.js @@ -0,0 +1,84 @@ + +var util = require('util'); +var Proxy = require('./utils/proxy.js'); +var SimpleHttpWrap = require('./wrapper/simple_http.js'); +var Options = require('./pm2_module.js'); + +var debug = require('debug')('axm:patch'); + +var Transaction = module.exports = {}; + +Transaction.http = function(opts) { + var Module = require('module'); + + debug('Wrapping HTTP routes'); + + if (Array.isArray(opts)) { + var routes = JSON.parse(JSON.stringify(opts)); + opts = { + http : true, + http_latency : 200, + http_code : 500, + ignore_routes : routes + }; + } + opts = util._extend({ + http : true, + http_latency : 200, + http_code : 500, + ignore_routes : [], + }, opts); + + Proxy.wrap(Module, '_load', function(load) { + if (load.__axm_original) { + debug('HTTP routes have already been wrapped before'); + + Options.configureModule({ + latency : opts.http + }); + + if (opts.http === false) { + return function(file) { return load.__axm_original.apply(this, arguments) }; + } else { + return function(file) { + if (file === 'http' || file === 'https') + return SimpleHttpWrap(opts, load.__axm_original.apply(this, arguments)); + else + return load.__axm_original.apply(this, arguments); + }; + } + } + + return function(file) { + if (opts.http && + (file === 'http' || file === 'https')) { + debug('http module being required'); + Options.configureModule({ + latency : true + }); + return SimpleHttpWrap(opts, load.apply(this, arguments)); + } + else + return load.apply(this, arguments); + }; + }); +}; + +// Transaction.patch = function() { +// var Module = require('module'); + +// debug('Patching'); + +// Proxy.wrap(Module, '_load', function(load) { +// return function(file) { +// if (file == 'redis') { +// return RedisWrap(load.apply(this, arguments)); +// } +// else if (file == 'http') { +// return HttpWrap(load.apply(this, arguments)); +// } +// else +// return load.apply(this, arguments); +// }; +// }); +// }; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/BinaryHeap.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/BinaryHeap.js new file mode 100644 index 00000000..384464b6 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/BinaryHeap.js @@ -0,0 +1,135 @@ + +// Hacked https://github.com/felixge/node-measured + +// Based on http://en.wikipedia.org/wiki/Binary_Heap +// as well as http://eloquentjavascript.net/appendix2.html +module.exports = BinaryHeap; +function BinaryHeap(options) { + options = options || {}; + + this._elements = options.elements || []; + this._score = options.score || this._score; +} + +BinaryHeap.prototype.add = function(/* elements */) { + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + + this._elements.push(element); + this._bubble(this._elements.length - 1); + } +}; + +BinaryHeap.prototype.first = function() { + return this._elements[0]; +}; + +BinaryHeap.prototype.removeFirst = function() { + var root = this._elements[0]; + var last = this._elements.pop(); + + if (this._elements.length > 0) { + this._elements[0] = last; + this._sink(0); + } + + return root; +}; + +BinaryHeap.prototype.clone = function() { + return new BinaryHeap({ + elements: this.toArray(), + score: this._score, + }); +}; + +BinaryHeap.prototype.toSortedArray = function() { + var array = []; + var clone = this.clone(); + + while (true) { + var element = clone.removeFirst(); + if (element === undefined) break; + + array.push(element); + } + + return array; +}; + +BinaryHeap.prototype.toArray = function() { + return [].concat(this._elements); +}; + +BinaryHeap.prototype.size = function() { + return this._elements.length; +}; + +BinaryHeap.prototype._bubble = function(bubbleIndex) { + var bubbleElement = this._elements[bubbleIndex]; + var bubbleScore = this._score(bubbleElement); + + while (bubbleIndex > 0) { + var parentIndex = this._parentIndex(bubbleIndex); + var parentElement = this._elements[parentIndex]; + var parentScore = this._score(parentElement); + + if (bubbleScore <= parentScore) break; + + this._elements[parentIndex] = bubbleElement; + this._elements[bubbleIndex] = parentElement; + bubbleIndex = parentIndex; + } +}; + +BinaryHeap.prototype._sink = function(sinkIndex) { + var sinkElement = this._elements[sinkIndex]; + var sinkScore = this._score(sinkElement); + var length = this._elements.length; + + while (true) { + var swapIndex = null; + var swapScore = null; + var swapElement = null; + var childIndexes = this._childIndexes(sinkIndex); + + for (var i = 0; i < childIndexes.length; i++) { + var childIndex = childIndexes[i]; + + if (childIndex >= length) break; + + var childElement = this._elements[childIndex]; + var childScore = this._score(childElement); + + if (childScore > sinkScore) { + if (swapScore === null || swapScore < childScore) { + swapIndex = childIndex; + swapScore = childScore; + swapElement = childElement; + } + } + } + + if (swapIndex === null) break; + + this._elements[swapIndex] = sinkElement; + this._elements[sinkIndex] = swapElement; + sinkIndex = swapIndex; + } +}; + +BinaryHeap.prototype._parentIndex = function(index) { + return Math.floor((index - 1) / 2); +}; + +BinaryHeap.prototype._childIndexes = function(index) { + return [ + 2 * index + 1, + 2 * index + 2, + ]; + return ; +}; + +BinaryHeap.prototype._score = function(element) { + return element.valueOf(); +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EDS.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EDS.js new file mode 100644 index 00000000..4389e27c --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EDS.js @@ -0,0 +1,101 @@ + +// Hacked https://github.com/felixge/node-measured + +var BinaryHeap = require('./BinaryHeap'); +var units = require('./units'); + +module.exports = ExponentiallyDecayingSample; +function ExponentiallyDecayingSample(options) { + options = options || {}; + + this._elements = new BinaryHeap({ + score: function(element) { + return -element.priority; + } + }); + + this._rescaleInterval = options.rescaleInterval || ExponentiallyDecayingSample.RESCALE_INTERVAL; + this._alpha = options.alpha || ExponentiallyDecayingSample.ALPHA; + this._size = options.size || ExponentiallyDecayingSample.SIZE; + this._random = options.random || this._random; + this._landmark = null; + this._nextRescale = null; +} + +ExponentiallyDecayingSample.RESCALE_INTERVAL = 1 * units.HOURS; +ExponentiallyDecayingSample.ALPHA = 0.015; +ExponentiallyDecayingSample.SIZE = 1028; + +ExponentiallyDecayingSample.prototype.update = function(value, timestamp) { + var now = Date.now(); + if (!this._landmark) { + this._landmark = now; + this._nextRescale = this._landmark + this._rescaleInterval; + } + + timestamp = timestamp || now; + + var newSize = this._elements.size() + 1; + + var element = { + priority: this._priority(timestamp - this._landmark), + value: value + }; + + if (newSize <= this._size) { + this._elements.add(element); + } else if (element.priority > this._elements.first().priority) { + this._elements.removeFirst(); + this._elements.add(element); + } + + if (now >= this._nextRescale) this._rescale(now); +}; + +ExponentiallyDecayingSample.prototype.toSortedArray = function() { + return this._elements + .toSortedArray() + .map(function(element) { + return element.value; + }); +}; + + +ExponentiallyDecayingSample.prototype.toArray = function() { + return this._elements + .toArray() + .map(function(element) { + return element.value; + }); +}; + +ExponentiallyDecayingSample.prototype._weight = function(age) { + // We divide by 1000 to not run into huge numbers before reaching a + // rescale event. + return Math.exp(this._alpha * (age / 1000)); +}; + +ExponentiallyDecayingSample.prototype._priority = function(age) { + return this._weight(age) / this._random(); +}; + +ExponentiallyDecayingSample.prototype._random = function() { + return Math.random(); +}; + +ExponentiallyDecayingSample.prototype._rescale = function(now) { + now = now || Date.now(); + + var self = this; + var oldLandmark = this._landmark; + this._landmark = now || Date.now(); + this._nextRescale = now + this._rescaleInterval; + + var factor = self._priority(-(self._landmark - oldLandmark)); + + this._elements + .toArray() + .forEach(function(element) { + element.priority *= factor; + }); +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EWMA.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EWMA.js new file mode 100644 index 00000000..dec164e9 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/EWMA.js @@ -0,0 +1,31 @@ + +// Hacked https://github.com/felixge/node-measured + +var units = require('./units'); + +module.exports = ExponentiallyWeightedMovingAverage; + +function ExponentiallyWeightedMovingAverage(timePeriod, tickInterval) { + this._timePeriod = timePeriod || 1 * units.MINUTE; + this._tickInterval = tickInterval || ExponentiallyWeightedMovingAverage.TICK_INTERVAL; + this._alpha = 1 - Math.exp(-this._tickInterval / this._timePeriod); + this._count = 0; + this._rate = 0; +}; + +ExponentiallyWeightedMovingAverage.TICK_INTERVAL = 5 * units.SECONDS; + +ExponentiallyWeightedMovingAverage.prototype.update = function(n) { + this._count += n; +}; + +ExponentiallyWeightedMovingAverage.prototype.tick = function() { + var instantRate = this._count / this._tickInterval; + this._count = 0; + + this._rate += (this._alpha * (instantRate - this._rate)); +}; + +ExponentiallyWeightedMovingAverage.prototype.rate = function(timeUnit) { + return (this._rate || 0) * timeUnit; +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/fixedQueue.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/fixedQueue.js new file mode 100644 index 00000000..86059a5f --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/fixedQueue.js @@ -0,0 +1,115 @@ +function FixedQueue( size, initialValues ){ + + // If there are no initial arguments, default it to + // an empty value so we can call the constructor in + // a uniform way. + initialValues = (initialValues || []); + + // Create the fixed queue array value. + var queue = Array.apply( null, initialValues ); + + // Store the fixed size in the queue. + queue.fixedSize = size; + + // Add the class methods to the queue. Some of these have + // to override the native Array methods in order to make + // sure the queue lenght is maintained. + queue.push = FixedQueue.push; + queue.splice = FixedQueue.splice; + queue.unshift = FixedQueue.unshift; + + // Trim any initial excess from the queue. + FixedQueue.trimTail.call( queue ); + + // Return the new queue. + return( queue ); + +} + + +// I trim the queue down to the appropriate size, removing +// items from the beginning of the internal array. +FixedQueue.trimHead = function(){ + + // Check to see if any trimming needs to be performed. + if (this.length <= this.fixedSize){ + + // No trimming, return out. + return; + + } + + // Trim whatever is beyond the fixed size. + Array.prototype.splice.call( + this, + 0, + (this.length - this.fixedSize) + ); + +}; + + +// I trim the queue down to the appropriate size, removing +// items from the end of the internal array. +FixedQueue.trimTail = function(){ + + // Check to see if any trimming needs to be performed. + if (this.length <= this.fixedSize){ + + // No trimming, return out. + return; + + } + + // Trim whatever is beyond the fixed size. + Array.prototype.splice.call( + this, + this.fixedSize, + (this.length - this.fixedSize) + ); + +}; + + +// I synthesize wrapper methods that call the native Array +// methods followed by a trimming method. +FixedQueue.wrapMethod = function( methodName, trimMethod ){ + + // Create a wrapper that calls the given method. + var wrapper = function(){ + + // Get the native Array method. + var method = Array.prototype[ methodName ]; + + // Call the native method first. + var result = method.apply( this, arguments ); + + // Trim the queue now that it's been augmented. + trimMethod.call( this ); + + // Return the original value. + return( result ); + + }; + + // Return the wrapper method. + return( wrapper ); + +}; + + +// Wrap the native methods. +FixedQueue.push = FixedQueue.wrapMethod( + "push", + FixedQueue.trimHead +); + +FixedQueue.splice = FixedQueue.wrapMethod( + "splice", + FixedQueue.trimTail +); + +FixedQueue.unshift = FixedQueue.wrapMethod( + "unshift", + FixedQueue.trimTail +); diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Counter.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Counter.js new file mode 100644 index 00000000..b7e9e7b3 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Counter.js @@ -0,0 +1,26 @@ + +// Hacked from https://github.com/felixge/node-measured + +module.exports = Counter; + +function Counter(opts) { + opts = opts || {}; + + this._count = opts.count || 0; +} + +Counter.prototype.val = function() { + return this._count; +}; + +Counter.prototype.inc = function(n) { + this._count += (n || 1); +}; + +Counter.prototype.dec = function(n) { + this._count -= (n || 1); +}; + +Counter.prototype.reset = function(count) { + this._count = count || 0; +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Histogram.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Histogram.js new file mode 100644 index 00000000..affba16a --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Histogram.js @@ -0,0 +1,185 @@ + +// Hacked from https://github.com/felixge/node-measured + +var EDS = require('../EDS.js'); + +function Histogram(opts) { + var self = this; + + opts = opts || {}; + + this._measurement = opts.measurement; + this._call_fn = null; + + var methods = { + min : this.getMin, + max : this.getMax, + sum : this.getSum, + count : this.getCount, + variance : this._calculateVariance, + mean : this._calculateMean, + stddev : this._calculateStddev + }; + + if (methods[this._measurement]) + this._call_fn = methods[this._measurement]; + else { + this._call_fn = function() { + var percentiles = this.percentiles([0.5, 0.75, 0.95, 0.99, 0.999]); + + var medians = { + median : percentiles[0.5], + p75 : percentiles[0.75], + p95 : percentiles[0.95], + p99 : percentiles[0.99], + p999 : percentiles[0.999] + }; + + return medians[this._measurement]; + } + } + this._sample = new EDS(); + this._min = null; + this._max = null; + this._count = 0; + this._sum = 0; + + // These are for the Welford algorithm for calculating running variance + // without floating-point doom. + this._varianceM = 0; + this._varianceS = 0; +} + +Histogram.prototype.update = function(value) { + this._count++; + this._sum += value; + + this._sample.update(value); + this._updateMin(value); + this._updateMax(value); + this._updateVariance(value); +}; + +Histogram.prototype.percentiles = function(percentiles) { + var values = this._sample + .toArray() + .sort(function(a, b) { + return (a === b) + ? 0 + : a - b; + }); + + var results = {}; + + for (var i = 0; i < percentiles.length; i++) { + var percentile = percentiles[i]; + if (!values.length) { + results[percentile] = null; + continue; + } + + var pos = percentile * (values.length + 1); + + if (pos < 1) { + results[percentile] = values[0]; + } else if (pos >= values.length) { + results[percentile] = values[values.length - 1]; + } else { + var lower = values[Math.floor(pos) - 1]; + var upper = values[Math.ceil(pos) - 1]; + + results[percentile] = lower + (pos - Math.floor(pos)) * (upper - lower); + } + } + + return results; +}; + +Histogram.prototype.reset = function() { + this.constructor.call(this); +}; + +Histogram.prototype.val = function() { + if (typeof(this._call_fn) === 'function') + return this._call_fn(); + else + return this._call_fn; +}; + +Histogram.prototype.getMin = function() { + return this._min; +}; + +Histogram.prototype.getMax = function() { + return this._max; +}; + +Histogram.prototype.getSum = function() { + return this._sum; +}; + +Histogram.prototype.getCount = function() { + return this._count; +}; + + +Histogram.prototype.fullResults = function() { + var percentiles = this.percentiles([0.5, 0.75, 0.95, 0.99, 0.999]); + + return { + min : this._min, + max : this._max, + sum : this._sum, + variance : this._calculateVariance(), + mean : this._calculateMean(), + stddev : this._calculateStddev(), + count : this._count, + median : percentiles[0.5], + p75 : percentiles[0.75], + p95 : percentiles[0.95], + p99 : percentiles[0.99], + p999 : percentiles[0.999] + }; +}; + +Histogram.prototype._updateMin = function(value) { + if (this._min === null || value < this._min) { + this._min = value; + //console.log(value); + } +}; + +Histogram.prototype._updateMax = function(value) { + if (this._max === null || value > this._max) { + this._max = value; + } +}; + +Histogram.prototype._updateVariance = function(value) { + if (this._count === 1) return this._varianceM = value; + + var oldM = this._varianceM; + + this._varianceM += ((value - oldM) / this._count); + this._varianceS += ((value - oldM) * (value - this._varianceM)); +}; + +Histogram.prototype._calculateMean = function() { + return (this._count === 0) + ? 0 + : this._sum / this._count; +}; + +Histogram.prototype._calculateVariance = function() { + return (this._count <= 1) + ? null + : this._varianceS / (this._count - 1); +}; + +Histogram.prototype._calculateStddev = function() { + return (this._count < 1) + ? null + : Math.sqrt(this._calculateVariance()); +}; + +module.exports = Histogram; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Meter.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Meter.js new file mode 100644 index 00000000..f436af40 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/probes/Meter.js @@ -0,0 +1,36 @@ + +// Hacked from https://github.com/felixge/node-measured + +var units = require('../units'); +var EWMA = require('../EWMA'); + +function Meter(opts) { + var self = this; + + this._tickInterval = 5 * units.SECONDS; + this._samples = opts.samples || 1; + this._timeframe = opts.timeframe || 60; + + this._rate = new EWMA(this._timeframe * units.SECONDS, this._tickInterval); + + if (opts.debug && opts.debug == true) + return false; + + this._interval = setInterval(function() { + self._rate.tick(); + }, this._tickInterval); +} + +Meter.RATE_UNIT = units.SECONDS; + +Meter.prototype.mark = function(n) { + n = n || 1; + + this._rate.update(n); +}; + +Meter.prototype.val = function() { + return Math.round(this._rate.rate(this._samples * Meter.RATE_UNIT) * 100 ) / 100; +}; + +module.exports = Meter; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/proxy.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/proxy.js new file mode 100644 index 00000000..830a30fe --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/proxy.js @@ -0,0 +1,34 @@ + +var debug = require('debug')('axm:proxy'); + +// var cls = require('continuation-local-storage'); +// var ns = cls.createNamespace('namespace'); + +var Proxy = module.exports = { + wrap : function(object, methods, hook) { + var self = this; + + if (!Array.isArray(methods)) methods = [methods]; + + for (var i = 0; i < methods.length; ++i) { + debug('Wrapping method:', methods[i]); + var original = object[methods[i]]; + if (!original) return debug('Method %s unknown', methods[i]); + if (original.__axm_original) { + debug('Already wrapped', methods[i]); + if (methods[i] != '_load') + return; + } + var hooked = hook(original); + + if (original.__axm_original) { + hooked.__axm_original = original.__axm_original; + } + else { + hooked.__axm_original = original; + } + object[methods[i]] = hooked; + //debug('Method proxified'); + } + } +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/transport.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/transport.js new file mode 100644 index 00000000..b7485e4b --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/transport.js @@ -0,0 +1,34 @@ + +var debug = require('debug')('axm:transport'); +var stringify = require('json-stringify-safe'); + +var Transport = module.exports = {}; + +function ipcSend(args, print) { + /** + * For debug purpose + */ + if (process.env.MODULE_DEBUG) + console.log(args); + + if (!process.send) { + var output = args.data; + delete output.__name; + return false; + } + + + try { + process.send(JSON.parse(stringify(args))); + } catch(e) { + console.error('Process disconnected from parent !'); + console.error(e.stack || e); + process.exit(1); + } +}; + +Transport.send = function(args, print) { + if (!print) print = false; + + ipcSend(args, print); +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/utils/units.js b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/units.js new file mode 100644 index 00000000..b1e16a24 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/utils/units.js @@ -0,0 +1,9 @@ +// Time units, as found in Java: +// see: http://download.oracle.com/javase/6/docs/api/java/util/concurrent/TimeUnit.html +exports.NANOSECONDS = 1 / (1000 * 1000); +exports.MICROSECONDS = 1 / 1000; +exports.MILLISECONDS = 1; +exports.SECONDS = 1000 * exports.MILLISECONDS; +exports.MINUTES = 60 * exports.SECONDS; +exports.HOURS = 60 * exports.MINUTES; +exports.DAYS = 24 * exports.HOURS; diff --git a/test/fixtures/module-fixture/node_modules/pmx/lib/wrapper/simple_http.js b/test/fixtures/module-fixture/node_modules/pmx/lib/wrapper/simple_http.js new file mode 100644 index 00000000..ca6d65a9 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/lib/wrapper/simple_http.js @@ -0,0 +1,98 @@ + +var Proxy = require('../utils/proxy.js'); +var Transport = require('../utils/transport.js'); +var Probe = require('../Probe.js'); + +var gl_meter, gl_latency; + +var HttpWrap = module.exports = function(opts, http) { + + gl_meter = Probe.probe().meter({ + name : 'HTTP', + seconds : 60, + unit : 'req/s' + }); + + gl_latency = Probe.probe().histogram({ + measurement : 'mean', + name : 'pmx:http:latency', + unit : 'ms' + }); + + var ignoreRoutes = function(url) { + for (var i = 0; i < opts.ignore_routes.length; ++i) { + if (url.match(opts.ignore_routes[i]) != null) { + return true; + } + } + return false; + }; + + Proxy.wrap(http.Server.prototype, ['on', 'addListener'], function(addListener) { + return function(event, listener) { + var self = this; + + var overloaded_function = function(request, response) { + gl_meter.mark(); + + var http_start = { + url : request.url, + method : request.method, + start : Date.now(), + ip : request.headers['x-forwarded-for'] || + (request.connection ? request.connection.remoteAddress : false) || + (request.socket ? request.socket.remoteAddress : false) || + ((request.connection && request.connection.socket) ? request.connection.socket.remoteAddress : false) || '' + }; + + response.once('finish', function() { + + if (!ignoreRoutes(http_start.url)) + gl_latency.update(Date.now() - http_start.start); + + if (((Date.now() - http_start.start) >= opts.http_latency + || response.statusCode >= opts.http_code) + && !ignoreRoutes(http_start.url)) { + + Transport.send({ + type : 'http:transaction', + data : { + url : http_start.url, + method : http_start.method, + time : Date.now() - http_start.start, + code : response.statusCode, + ip : http_start.ip, + size : response.getHeader('Content-Length') || null + } + }); + } + + http_start = null; + }); + }; + + if (!(event === 'request' && typeof listener === 'function')) + return addListener.apply(self, arguments); + + if (self.__overloaded !== true) { + + self.on('removeListener', function onRemoveListener() { + setTimeout(function() { + if (self.listeners('request').length === 1) { + self.removeListener('request', overloaded_function); + self.removeListener('removeListener', onRemoveListener); + self.__overloaded = false; + } + }, 200); + }); + + addListener.call(self, event, overloaded_function); + + self.__overloaded = true; + } + + return addListener.apply(self, arguments); + }; + }); + return http; +}; diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.jshintrc b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.jshintrc new file mode 100644 index 00000000..299877f2 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.jshintrc @@ -0,0 +1,3 @@ +{ + "laxbreak": true +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.npmignore new file mode 100644 index 00000000..7e6163db --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/.npmignore @@ -0,0 +1,6 @@ +support +test +examples +example +*.sock +dist diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/History.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/History.md new file mode 100644 index 00000000..854c9711 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/History.md @@ -0,0 +1,195 @@ + +2.2.0 / 2015-05-09 +================== + + * package: update "ms" to v0.7.1 (#202, @dougwilson) + * README: add logging to file example (#193, @DanielOchoa) + * README: fixed a typo (#191, @amir-s) + * browser: expose `storage` (#190, @stephenmathieson) + * Makefile: add a `distclean` target (#189, @stephenmathieson) + +2.1.3 / 2015-03-13 +================== + + * Updated stdout/stderr example (#186) + * Updated example/stdout.js to match debug current behaviour + * Renamed example/stderr.js to stdout.js + * Update Readme.md (#184) + * replace high intensity foreground color for bold (#182, #183) + +2.1.2 / 2015-03-01 +================== + + * dist: recompile + * update "ms" to v0.7.0 + * package: update "browserify" to v9.0.3 + * component: fix "ms.js" repo location + * changed bower package name + * updated documentation about using debug in a browser + * fix: security error on safari (#167, #168, @yields) + +2.1.1 / 2014-12-29 +================== + + * browser: use `typeof` to check for `console` existence + * browser: check for `console.log` truthiness (fix IE 8/9) + * browser: add support for Chrome apps + * Readme: added Windows usage remarks + * Add `bower.json` to properly support bower install + +2.1.0 / 2014-10-15 +================== + + * node: implement `DEBUG_FD` env variable support + * package: update "browserify" to v6.1.0 + * package: add "license" field to package.json (#135, @panuhorsmalahti) + +2.0.0 / 2014-09-01 +================== + + * package: update "browserify" to v5.11.0 + * node: use stderr rather than stdout for logging (#29, @stephenmathieson) + +1.0.4 / 2014-07-15 +================== + + * dist: recompile + * example: remove `console.info()` log usage + * example: add "Content-Type" UTF-8 header to browser example + * browser: place %c marker after the space character + * browser: reset the "content" color via `color: inherit` + * browser: add colors support for Firefox >= v31 + * debug: prefer an instance `log()` function over the global one (#119) + * Readme: update documentation about styled console logs for FF v31 (#116, @wryk) + +1.0.3 / 2014-07-09 +================== + + * Add support for multiple wildcards in namespaces (#122, @seegno) + * browser: fix lint + +1.0.2 / 2014-06-10 +================== + + * browser: update color palette (#113, @gscottolson) + * common: make console logging function configurable (#108, @timoxley) + * node: fix %o colors on old node <= 0.8.x + * Makefile: find node path using shell/which (#109, @timoxley) + +1.0.1 / 2014-06-06 +================== + + * browser: use `removeItem()` to clear localStorage + * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777) + * package: add "contributors" section + * node: fix comment typo + * README: list authors + +1.0.0 / 2014-06-04 +================== + + * make ms diff be global, not be scope + * debug: ignore empty strings in enable() + * node: make DEBUG_COLORS able to disable coloring + * *: export the `colors` array + * npmignore: don't publish the `dist` dir + * Makefile: refactor to use browserify + * package: add "browserify" as a dev dependency + * Readme: add Web Inspector Colors section + * node: reset terminal color for the debug content + * node: map "%o" to `util.inspect()` + * browser: map "%j" to `JSON.stringify()` + * debug: add custom "formatters" + * debug: use "ms" module for humanizing the diff + * Readme: add "bash" syntax highlighting + * browser: add Firebug color support + * browser: add colors for WebKit browsers + * node: apply log to `console` + * rewrite: abstract common logic for Node & browsers + * add .jshintrc file + +0.8.1 / 2014-04-14 +================== + + * package: re-add the "component" section + +0.8.0 / 2014-03-30 +================== + + * add `enable()` method for nodejs. Closes #27 + * change from stderr to stdout + * remove unnecessary index.js file + +0.7.4 / 2013-11-13 +================== + + * remove "browserify" key from package.json (fixes something in browserify) + +0.7.3 / 2013-10-30 +================== + + * fix: catch localStorage security error when cookies are blocked (Chrome) + * add debug(err) support. Closes #46 + * add .browser prop to package.json. Closes #42 + +0.7.2 / 2013-02-06 +================== + + * fix package.json + * fix: Mobile Safari (private mode) is broken with debug + * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript + +0.7.1 / 2013-02-05 +================== + + * add repository URL to package.json + * add DEBUG_COLORED to force colored output + * add browserify support + * fix component. Closes #24 + +0.7.0 / 2012-05-04 +================== + + * Added .component to package.json + * Added debug.component.js build + +0.6.0 / 2012-03-16 +================== + + * Added support for "-" prefix in DEBUG [Vinay Pulim] + * Added `.enabled` flag to the node version [TooTallNate] + +0.5.0 / 2012-02-02 +================== + + * Added: humanize diffs. Closes #8 + * Added `debug.disable()` to the CS variant + * Removed padding. Closes #10 + * Fixed: persist client-side variant again. Closes #9 + +0.4.0 / 2012-02-01 +================== + + * Added browser variant support for older browsers [TooTallNate] + * Added `debug.enable('project:*')` to browser variant [TooTallNate] + * Added padding to diff (moved it to the right) + +0.3.0 / 2012-01-26 +================== + + * Added millisecond diff when isatty, otherwise UTC string + +0.2.0 / 2012-01-22 +================== + + * Added wildcard support + +0.1.0 / 2011-12-02 +================== + + * Added: remove colors unless stderr isatty [TooTallNate] + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Makefile b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Makefile new file mode 100644 index 00000000..5cf4a596 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Makefile @@ -0,0 +1,36 @@ + +# get Makefile directory name: http://stackoverflow.com/a/5982798/376773 +THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) +THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) + +# BIN directory +BIN := $(THIS_DIR)/node_modules/.bin + +# applications +NODE ?= $(shell which node) +NPM ?= $(NODE) $(shell which npm) +BROWSERIFY ?= $(NODE) $(BIN)/browserify + +all: dist/debug.js + +install: node_modules + +clean: + @rm -rf dist + +dist: + @mkdir -p $@ + +dist/debug.js: node_modules browser.js debug.js dist + @$(BROWSERIFY) \ + --standalone debug \ + . > $@ + +distclean: clean + @rm -rf node_modules + +node_modules: package.json + @NODE_ENV= $(NPM) install + @touch node_modules + +.PHONY: all install clean distclean diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Readme.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Readme.md new file mode 100644 index 00000000..b4f45e3c --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/Readme.md @@ -0,0 +1,188 @@ +# debug + + tiny node.js debugging utility modelled after node core's debugging technique. + +## Installation + +```bash +$ npm install debug +``` + +## Usage + + With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. + +Example _app.js_: + +```js +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; + +// fake app + +debug('booting %s', name); + +http.createServer(function(req, res){ + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, function(){ + debug('listening'); +}); + +// fake worker of some kind + +require('./worker'); +``` + +Example _worker.js_: + +```js +var debug = require('debug')('worker'); + +setInterval(function(){ + debug('doing some work'); +}, 1000); +``` + + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: + + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) + + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) + +#### Windows note + + On Windows the environment variable is set using the `set` command. + + ```cmd + set DEBUG=*,-not_this + ``` + +Then, run the program to be debugged as usual. + +## Millisecond diff + + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. + + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) + + When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: + + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) + +## Conventions + + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". + +## Wildcards + + The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. + + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:". + +## Browser support + + Debug works in the browser as well, currently persisted by `localStorage`. Consider the situation shown below where you have `worker:a` and `worker:b`, and wish to debug both. Somewhere in the code on your page, include: + +```js +window.myDebug = require("debug"); +``` + + ("debug" is a global object in the browser so we give this object a different name.) When your page is open in the browser, type the following in the console: + +```js +myDebug.enable("worker:*") +``` + + Refresh the page. Debug output will continue to be sent to the console until it is disabled by typing `myDebug.disable()` in the console. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + b('doing some work'); +}, 1200); +``` + +#### Web Inspector Colors + + Colors are also enabled on "Web Inspectors" that understand the `%c` formatting + option. These are WebKit web inspectors, Firefox ([since version + 31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/)) + and the Firebug plugin for Firefox (any version). + + Colored output looks something like: + + ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png) + +### stderr vs stdout + +You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally: + +Example _stdout.js_: + +```js +var debug = require('debug'); +var error = debug('app:error'); + +// by default stderr is used +error('goes to stderr!'); + +var log = debug('app:log'); +// set this namespace to log via console.log +log.log = console.log.bind(console); // don't forget to bind to console! +log('goes to stdout'); +error('still goes to stderr!'); + +// set all output to go via console.info +// overrides all per-namespace log settings +debug.log = console.info.bind(console); +error('now goes to stdout via console.info'); +log('still goes to stdout, but via console.info now'); +``` + +### Save debug output to a file + +You can save all debug statements to a file by piping them. + +Example: + +```bash +$ DEBUG_FD=3 node your-app.js 3> whatever.log +``` + +## Authors + + - TJ Holowaychuk + - Nathan Rajlich + +## License + +(The MIT License) + +Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca> + +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. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/bower.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/bower.json new file mode 100644 index 00000000..6af573ff --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/bower.json @@ -0,0 +1,28 @@ +{ + "name": "visionmedia-debug", + "main": "dist/debug.js", + "version": "2.2.0", + "homepage": "https://github.com/visionmedia/debug", + "authors": [ + "TJ Holowaychuk " + ], + "description": "visionmedia-debug", + "moduleType": [ + "amd", + "es6", + "globals", + "node" + ], + "keywords": [ + "visionmedia", + "debug" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/browser.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/browser.js new file mode 100644 index 00000000..7c764522 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/browser.js @@ -0,0 +1,168 @@ + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage(){ + try { + return window.localStorage; + } catch (e) {} +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/component.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/component.json new file mode 100644 index 00000000..ca106372 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/component.json @@ -0,0 +1,19 @@ +{ + "name": "debug", + "repo": "visionmedia/debug", + "description": "small debugging utility", + "version": "2.2.0", + "keywords": [ + "debug", + "log", + "debugger" + ], + "main": "browser.js", + "scripts": [ + "browser.js", + "debug.js" + ], + "dependencies": { + "rauchg/ms.js": "0.7.1" + } +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/debug.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/debug.js new file mode 100644 index 00000000..7571a860 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/debug.js @@ -0,0 +1,197 @@ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = require('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + +exports.formatters = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node.js new file mode 100644 index 00000000..1d392a81 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node.js @@ -0,0 +1,209 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); +var util = require('util'); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: + * + * $ DEBUG_FD=3 node script.js 3>debug.log + */ + +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase(); + if (0 === debugColors.length) { + return tty.isatty(fd); + } else { + return '0' !== debugColors + && 'no' !== debugColors + && 'false' !== debugColors + && 'disabled' !== debugColors; + } +} + +/** + * Map %o to `util.inspect()`, since Node doesn't do that out of the box. + */ + +var inspect = (4 === util.inspect.length ? + // node <= 0.8.x + function (v, colors) { + return util.inspect(v, void 0, void 0, colors); + } : + // node > 0.8.x + function (v, colors) { + return util.inspect(v, { colors: colors }); + } +); + +exports.formatters.o = function(v) { + return inspect(v, this.useColors) + .replace(/\s*\n\s*/g, ' '); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + var name = this.namespace; + + if (useColors) { + var c = this.color; + + args[0] = ' \u001b[3' + c + ';1m' + name + ' ' + + '\u001b[0m' + + args[0] + '\u001b[3' + c + 'm' + + ' +' + exports.humanize(this.diff) + '\u001b[0m'; + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } + return args; +} + +/** + * Invokes `console.error()` with the specified arguments. + */ + +function log() { + return stream.write(util.format.apply(this, arguments) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. + */ + +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + case 'FILE': + var fs = require('fs'); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = require('net'); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; + + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/.npmignore new file mode 100644 index 00000000..d1aa0ce4 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/.npmignore @@ -0,0 +1,5 @@ +node_modules +test +History.md +Makefile +component.json diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/History.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/History.md new file mode 100644 index 00000000..32fdfc17 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/History.md @@ -0,0 +1,66 @@ + +0.7.1 / 2015-04-20 +================== + + * prevent extraordinary long inputs (@evilpacket) + * Fixed broken readme link + +0.7.0 / 2014-11-24 +================== + + * add time abbreviations, updated tests and readme for the new units + * fix example in the readme. + * add LICENSE file + +0.6.2 / 2013-12-05 +================== + + * Adding repository section to package.json to suppress warning from NPM. + +0.6.1 / 2013-05-10 +================== + + * fix singularization [visionmedia] + +0.6.0 / 2013-03-15 +================== + + * fix minutes + +0.5.1 / 2013-02-24 +================== + + * add component namespace + +0.5.0 / 2012-11-09 +================== + + * add short formatting as default and .long option + * add .license property to component.json + * add version to component.json + +0.4.0 / 2012-10-22 +================== + + * add rounding to fix crazy decimals + +0.3.0 / 2012-09-07 +================== + + * fix `ms()` [visionmedia] + +0.2.0 / 2012-09-03 +================== + + * add component.json [visionmedia] + * add days support [visionmedia] + * add hours support [visionmedia] + * add minutes support [visionmedia] + * add seconds support [visionmedia] + * add ms string support [visionmedia] + * refactor tests to facilitate ms(number) [visionmedia] + +0.1.0 / 2012-03-07 +================== + + * Initial release diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/LICENSE b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/LICENSE new file mode 100644 index 00000000..6c07561b --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2014 Guillermo Rauch + +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. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/README.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/README.md new file mode 100644 index 00000000..9b4fd035 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/README.md @@ -0,0 +1,35 @@ +# ms.js: miliseconds conversion utility + +```js +ms('2 days') // 172800000 +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2.5 hrs') // 9000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5s') // 5000 +ms('100') // 100 +``` + +```js +ms(60000) // "1m" +ms(2 * 60000) // "2m" +ms(ms('10 hours')) // "10h" +``` + +```js +ms(60000, { long: true }) // "1 minute" +ms(2 * 60000, { long: true }) // "2 minutes" +ms(ms('10 hours'), { long: true }) // "10 hours" +``` + +- Node/Browser compatible. Published as [`ms`](https://www.npmjs.org/package/ms) in [NPM](http://nodejs.org/download). +- If a number is supplied to `ms`, a string with a unit is returned. +- If a string that contains the number is supplied, it returns it as +a number (e.g: it returns `100` for `'100'`). +- If you pass a string with a number and a valid unit, the number of +equivalent ms is returned. + +## License + +MIT diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/index.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/index.js new file mode 100644 index 00000000..4f927716 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/index.js @@ -0,0 +1,125 @@ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = '' + str; + if (str.length > 10000) return; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/package.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/package.json new file mode 100644 index 00000000..253335e6 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/node_modules/ms/package.json @@ -0,0 +1,48 @@ +{ + "name": "ms", + "version": "0.7.1", + "description": "Tiny ms conversion utility", + "repository": { + "type": "git", + "url": "git://github.com/guille/ms.js.git" + }, + "main": "./index", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "component": { + "scripts": { + "ms/index.js": "index.js" + } + }, + "gitHead": "713dcf26d9e6fd9dbc95affe7eff9783b7f1b909", + "bugs": { + "url": "https://github.com/guille/ms.js/issues" + }, + "homepage": "https://github.com/guille/ms.js", + "_id": "ms@0.7.1", + "scripts": {}, + "_shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "_from": "ms@0.7.1", + "_npmVersion": "2.7.5", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "rauchg", + "email": "rauchg@gmail.com" + }, + "maintainers": [ + { + "name": "rauchg", + "email": "rauchg@gmail.com" + } + ], + "dist": { + "shasum": "9cd13c03adbff25b65effde7ce864ee952017098", + "tarball": "http://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/package.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/package.json new file mode 100644 index 00000000..7e6d9fc5 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/debug/package.json @@ -0,0 +1,73 @@ +{ + "name": "debug", + "version": "2.2.0", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + } + ], + "license": "MIT", + "dependencies": { + "ms": "0.7.1" + }, + "devDependencies": { + "browserify": "9.0.3", + "mocha": "*" + }, + "main": "./node.js", + "browser": "./browser.js", + "component": { + "scripts": { + "debug/index.js": "browser.js", + "debug/debug.js": "debug.js" + } + }, + "gitHead": "b38458422b5aa8aa6d286b10dfe427e8a67e2b35", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug", + "_id": "debug@2.2.0", + "scripts": {}, + "_shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "_from": "debug@*", + "_npmVersion": "2.7.4", + "_nodeVersion": "0.12.2", + "_npmUser": { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + }, + "maintainers": [ + { + "name": "tjholowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "tootallnate", + "email": "nathan@tootallnate.net" + } + ], + "dist": { + "shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da", + "tarball": "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/.npmignore b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/.npmignore new file mode 100644 index 00000000..17d6b367 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/.npmignore @@ -0,0 +1 @@ +/*.tgz diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/CHANGELOG.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/CHANGELOG.md new file mode 100644 index 00000000..42bcb60a --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/CHANGELOG.md @@ -0,0 +1,14 @@ +## Unreleased +- Fixes stringify to only take ancestors into account when checking + circularity. + It previously assumed every visited object was circular which led to [false + positives][issue9]. + Uses the tiny serializer I wrote for [Must.js][must] a year and a half ago. +- Fixes calling the `replacer` function in the proper context (`thisArg`). +- Fixes calling the `cycleReplacer` function in the proper context (`thisArg`). +- Speeds serializing by a factor of + Big-O(h-my-god-it-linearly-searched-every-object) it had ever seen. Searching + only the ancestors for a circular references speeds up things considerably. + +[must]: https://github.com/moll/js-must +[issue9]: https://github.com/isaacs/json-stringify-safe/issues/9 diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/LICENSE b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/LICENSE new file mode 100644 index 00000000..19129e31 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/Makefile b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/Makefile new file mode 100644 index 00000000..36088c72 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/Makefile @@ -0,0 +1,35 @@ +NODE_OPTS = +TEST_OPTS = + +love: + @echo "Feel like makin' love." + +test: + @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot $(TEST_OPTS) + +spec: + @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec $(TEST_OPTS) + +autotest: + @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot --watch $(TEST_OPTS) + +autospec: + @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec --watch $(TEST_OPTS) + +pack: + @file=$$(npm pack); echo "$$file"; tar tf "$$file" + +publish: + npm publish + +tag: + git tag "v$$(node -e 'console.log(require("./package").version)')" + +clean: + rm -f *.tgz + npm prune --production + +.PHONY: love +.PHONY: test spec autotest autospec +.PHONY: pack publish tag +.PHONY: clean diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/README.md b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/README.md new file mode 100644 index 00000000..a11f302a --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/README.md @@ -0,0 +1,52 @@ +# json-stringify-safe + +Like JSON.stringify, but doesn't throw on circular references. + +## Usage + +Takes the same arguments as `JSON.stringify`. + +```javascript +var stringify = require('json-stringify-safe'); +var circularObj = {}; +circularObj.circularRef = circularObj; +circularObj.list = [ circularObj, circularObj ]; +console.log(stringify(circularObj, null, 2)); +``` + +Output: + +```json +{ + "circularRef": "[Circular]", + "list": [ + "[Circular]", + "[Circular]" + ] +} +``` + +## Details + +``` +stringify(obj, serializer, indent, decycler) +``` + +The first three arguments are the same as to JSON.stringify. The last +is an argument that's only used when the object has been seen already. + +The default `decycler` function returns the string `'[Circular]'`. +If, for example, you pass in `function(k,v){}` (return nothing) then it +will prune cycles. If you pass in `function(k,v){ return {foo: 'bar'}}`, +then cyclical objects will always be represented as `{"foo":"bar"}` in +the result. + +``` +stringify.getSerialize(serializer, decycler) +``` + +Returns a serializer that can be used elsewhere. This is the actual +function that's passed to JSON.stringify. + +**Note** that the function returned from `getSerialize` is stateful for now, so +do **not** use it more than once. diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/package.json b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/package.json new file mode 100644 index 00000000..7a97649f --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/package.json @@ -0,0 +1,68 @@ +{ + "name": "json-stringify-safe", + "version": "5.0.1", + "description": "Like JSON.stringify, but doesn't blow up on circular refs.", + "keywords": [ + "json", + "stringify", + "circular", + "safe" + ], + "homepage": "https://github.com/isaacs/json-stringify-safe", + "bugs": { + "url": "https://github.com/isaacs/json-stringify-safe/issues" + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" + }, + "contributors": [ + { + "name": "Andri Möll", + "email": "andri@dot.ee", + "url": "http://themoll.com" + } + ], + "license": "ISC", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/json-stringify-safe.git" + }, + "main": "stringify.js", + "scripts": { + "test": "node test.js" + }, + "devDependencies": { + "mocha": ">= 2.1.0 < 3", + "must": ">= 0.12 < 0.13", + "sinon": ">= 1.12.2 < 2" + }, + "gitHead": "3890dceab3ad14f8701e38ca74f38276abc76de5", + "_id": "json-stringify-safe@5.0.1", + "_shasum": "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb", + "_from": "json-stringify-safe@*", + "_npmVersion": "2.10.0", + "_nodeVersion": "2.0.1", + "_npmUser": { + "name": "isaacs", + "email": "isaacs@npmjs.com" + }, + "dist": { + "shasum": "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb", + "tarball": "http://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "maintainers": [ + { + "name": "isaacs", + "email": "i@izs.me" + }, + { + "name": "moll", + "email": "andri@dot.ee" + } + ], + "directories": {}, + "_resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/stringify.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/stringify.js new file mode 100644 index 00000000..124a4521 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/stringify.js @@ -0,0 +1,27 @@ +exports = module.exports = stringify +exports.getSerialize = serializer + +function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) +} + +function serializer(replacer, cycleReplacer) { + var stack = [], keys = [] + + if (cycleReplacer == null) cycleReplacer = function(key, value) { + if (stack[0] === value) return "[Circular ~]" + return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" + } + + return function(key, value) { + if (stack.length > 0) { + var thisPos = stack.indexOf(this) + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) + if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) + } + else stack.push(value) + + return replacer == null ? value : replacer.call(this, key, value) + } +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/mocha.opts b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/mocha.opts new file mode 100644 index 00000000..2544e586 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/mocha.opts @@ -0,0 +1,2 @@ +--recursive +--require must diff --git a/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/stringify_test.js b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/stringify_test.js new file mode 100644 index 00000000..5b325831 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/node_modules/json-stringify-safe/test/stringify_test.js @@ -0,0 +1,246 @@ +var Sinon = require("sinon") +var stringify = require("..") +function jsonify(obj) { return JSON.stringify(obj, null, 2) } + +describe("Stringify", function() { + it("must stringify circular objects", function() { + var obj = {name: "Alice"} + obj.self = obj + var json = stringify(obj, null, 2) + json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) + }) + + it("must stringify circular objects with intermediaries", function() { + var obj = {name: "Alice"} + obj.identity = {self: obj} + var json = stringify(obj, null, 2) + json.must.eql(jsonify({name: "Alice", identity: {self: "[Circular ~]"}})) + }) + + it("must stringify circular objects deeper", function() { + var obj = {name: "Alice", child: {name: "Bob"}} + obj.child.self = obj.child + + stringify(obj, null, 2).must.eql(jsonify({ + name: "Alice", + child: {name: "Bob", self: "[Circular ~.child]"} + })) + }) + + it("must stringify circular objects deeper with intermediaries", function() { + var obj = {name: "Alice", child: {name: "Bob"}} + obj.child.identity = {self: obj.child} + + stringify(obj, null, 2).must.eql(jsonify({ + name: "Alice", + child: {name: "Bob", identity: {self: "[Circular ~.child]"}} + })) + }) + + it("must stringify circular objects in an array", function() { + var obj = {name: "Alice"} + obj.self = [obj, obj] + + stringify(obj, null, 2).must.eql(jsonify({ + name: "Alice", self: ["[Circular ~]", "[Circular ~]"] + })) + }) + + it("must stringify circular objects deeper in an array", function() { + var obj = {name: "Alice", children: [{name: "Bob"}, {name: "Eve"}]} + obj.children[0].self = obj.children[0] + obj.children[1].self = obj.children[1] + + stringify(obj, null, 2).must.eql(jsonify({ + name: "Alice", + children: [ + {name: "Bob", self: "[Circular ~.children.0]"}, + {name: "Eve", self: "[Circular ~.children.1]"} + ] + })) + }) + + it("must stringify circular arrays", function() { + var obj = [] + obj.push(obj) + obj.push(obj) + var json = stringify(obj, null, 2) + json.must.eql(jsonify(["[Circular ~]", "[Circular ~]"])) + }) + + it("must stringify circular arrays with intermediaries", function() { + var obj = [] + obj.push({name: "Alice", self: obj}) + obj.push({name: "Bob", self: obj}) + + stringify(obj, null, 2).must.eql(jsonify([ + {name: "Alice", self: "[Circular ~]"}, + {name: "Bob", self: "[Circular ~]"} + ])) + }) + + it("must stringify repeated objects in objects", function() { + var obj = {} + var alice = {name: "Alice"} + obj.alice1 = alice + obj.alice2 = alice + + stringify(obj, null, 2).must.eql(jsonify({ + alice1: {name: "Alice"}, + alice2: {name: "Alice"} + })) + }) + + it("must stringify repeated objects in arrays", function() { + var alice = {name: "Alice"} + var obj = [alice, alice] + var json = stringify(obj, null, 2) + json.must.eql(jsonify([{name: "Alice"}, {name: "Alice"}])) + }) + + it("must call given decycler and use its output", function() { + var obj = {} + obj.a = obj + obj.b = obj + + var decycle = Sinon.spy(function() { return decycle.callCount }) + var json = stringify(obj, null, 2, decycle) + json.must.eql(jsonify({a: 1, b: 2}, null, 2)) + + decycle.callCount.must.equal(2) + decycle.thisValues[0].must.equal(obj) + decycle.args[0][0].must.equal("a") + decycle.args[0][1].must.equal(obj) + decycle.thisValues[1].must.equal(obj) + decycle.args[1][0].must.equal("b") + decycle.args[1][1].must.equal(obj) + }) + + it("must call replacer and use its output", function() { + var obj = {name: "Alice", child: {name: "Bob"}} + + var replacer = Sinon.spy(bangString) + var json = stringify(obj, replacer, 2) + json.must.eql(jsonify({name: "Alice!", child: {name: "Bob!"}})) + + replacer.callCount.must.equal(4) + replacer.args[0][0].must.equal("") + replacer.args[0][1].must.equal(obj) + replacer.thisValues[1].must.equal(obj) + replacer.args[1][0].must.equal("name") + replacer.args[1][1].must.equal("Alice") + replacer.thisValues[2].must.equal(obj) + replacer.args[2][0].must.equal("child") + replacer.args[2][1].must.equal(obj.child) + replacer.thisValues[3].must.equal(obj.child) + replacer.args[3][0].must.equal("name") + replacer.args[3][1].must.equal("Bob") + }) + + it("must call replacer after describing circular references", function() { + var obj = {name: "Alice"} + obj.self = obj + + var replacer = Sinon.spy(bangString) + var json = stringify(obj, replacer, 2) + json.must.eql(jsonify({name: "Alice!", self: "[Circular ~]!"})) + + replacer.callCount.must.equal(3) + replacer.args[0][0].must.equal("") + replacer.args[0][1].must.equal(obj) + replacer.thisValues[1].must.equal(obj) + replacer.args[1][0].must.equal("name") + replacer.args[1][1].must.equal("Alice") + replacer.thisValues[2].must.equal(obj) + replacer.args[2][0].must.equal("self") + replacer.args[2][1].must.equal("[Circular ~]") + }) + + it("must call given decycler and use its output for nested objects", + function() { + var obj = {} + obj.a = obj + obj.b = {self: obj} + + var decycle = Sinon.spy(function() { return decycle.callCount }) + var json = stringify(obj, null, 2, decycle) + json.must.eql(jsonify({a: 1, b: {self: 2}})) + + decycle.callCount.must.equal(2) + decycle.args[0][0].must.equal("a") + decycle.args[0][1].must.equal(obj) + decycle.args[1][0].must.equal("self") + decycle.args[1][1].must.equal(obj) + }) + + it("must use decycler's output when it returned null", function() { + var obj = {a: "b"} + obj.self = obj + obj.selves = [obj, obj] + + function decycle() { return null } + stringify(obj, null, 2, decycle).must.eql(jsonify({ + a: "b", + self: null, + selves: [null, null] + })) + }) + + it("must use decycler's output when it returned undefined", function() { + var obj = {a: "b"} + obj.self = obj + obj.selves = [obj, obj] + + function decycle() {} + stringify(obj, null, 2, decycle).must.eql(jsonify({ + a: "b", + selves: [null, null] + })) + }) + + it("must throw given a decycler that returns a cycle", function() { + var obj = {} + obj.self = obj + var err + function identity(key, value) { return value } + try { stringify(obj, null, 2, identity) } catch (ex) { err = ex } + err.must.be.an.instanceof(TypeError) + }) + + describe(".getSerialize", function() { + it("must stringify circular objects", function() { + var obj = {a: "b"} + obj.circularRef = obj + obj.list = [obj, obj] + + var json = JSON.stringify(obj, stringify.getSerialize(), 2) + json.must.eql(jsonify({ + "a": "b", + "circularRef": "[Circular ~]", + "list": ["[Circular ~]", "[Circular ~]"] + })) + }) + + // This is the behavior as of Mar 3, 2015. + // The serializer function keeps state inside the returned function and + // so far I'm not sure how to not do that. JSON.stringify's replacer is not + // called _after_ serialization. + xit("must return a function that could be called twice", function() { + var obj = {name: "Alice"} + obj.self = obj + + var json + var serializer = stringify.getSerialize() + + json = JSON.stringify(obj, serializer, 2) + json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) + + json = JSON.stringify(obj, serializer, 2) + json.must.eql(jsonify({name: "Alice", self: "[Circular ~]"})) + }) + }) +}) + +function bangString(key, value) { + return typeof value == "string" ? value + "!" : value +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/package.json b/test/fixtures/module-fixture/node_modules/pmx/package.json new file mode 100644 index 00000000..8fe1132d --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/package.json @@ -0,0 +1,38 @@ +{ + "name": "pmx", + "version": "0.3.23", + "description": "Keymetrics++ and PM2 adapter", + "main": "index.js", + "dependencies": { + "debug": "*", + "json-stringify-safe": "*" + }, + "devDependencies": { + "express": "*", + "request": "*", + "should": "*", + "mocha": "*", + "shelljs": "*" + }, + "scripts": { + "test": "DEBUG='axm:*' mocha test/*.mocha.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/keymetrics/pmx.git" + }, + "author": { + "name": "Keymetrics I/O" + }, + "license": "MIT", + "readme": "\n# Driver for Keymetrics\n\n![Keymetrics](https://keymetrics.io/assets/images/application-demo.png)\n\nPMX is a module that allows you to create advanced interactions with Keymetrics.\n\nWith it you can:\n- Trigger remote actions / functions\n- Analyze custom metrics / variables (with utilities like Histogram/Counter/Metric/Meters)\n- Report errors (uncaught exceptions and custom errors)\n- Emit events\n- Analyze HTTP latency\n\n# Installation\n\n![Build Status](https://api.travis-ci.org/keymetrics/pmx.png?branch=master)\n\nInstall PMX and add it to your package.json via:\n\n```bash\n$ npm install pmx --save\n```\n\nThen init the module to monitor HTTP, Errors and diverse metrics.\n```javascript\nvar pmx = require('pmx').init(); // By default everything is enabled and ignore_routes is empty\n```\nOr choose what to monitor.\n```javascript\nvar pmx = require('pmx').init({\n http : true, // HTTP routes logging (default: true)\n ignore_routes : [/socket\\.io/, /notFound/], // Ignore http routes with this pattern (Default: [])\n errors : true, // Exceptions loggin (default: true)\n custom_probes : true, // Custom probes (default: true)\n network : true, // Traffic usage monitoring (default: false)\n ports : true // Shows which ports your app is listening on (default: false)\n});\n```\n\n# Custom monitoring\n\n## Emit Events\n\nEmit events and get historical and statistics:\n\n```javascript\nvar pmx = require('pmx');\n\npmx.emit('user:register', {\n user : 'Alex registered',\n email : 'thorustor@gmail.com'\n});\n```\n\n## Custom Action\n\nTrigger function from Keymetrics\n\n### Long running\n\n```javascript\nvar pmx = require('pmx');\n\npmx.action('db:clean', { comment : 'Description for this action' }, function(reply) {\n clean.db(function() {\n /**\n * reply() must be called at the end of the action\n */\n reply({success : true});\n });\n});\n```\n\n## Errors\n\nCatch uncaught exceptions:\n```javascript\nvar pmx = require('pmx').init();\n```\n\nAttach more data from errors that happens in Express:\n```javascript\nvar pmx = require('pmx');\n\napp.get('/' ...);\napp.post(...);\n\napp.use(pmx.expressErrorHandler());\n```\n\nTrigger custom errors:\n```javascript\nvar pmx = require('pmx');\n\npmx.notify({ success : false });\n\npmx.notify('This is an error');\n\npmx.notify(new Error('This is an error'));\n```\n\n## TCP network usage monitoring\n\nIf you enable the flag `network: true` when you init pmx it will show network usage datas (download and upload) in realtime.\n\nIf you enable the flag `ports: true` when you init pmx it will show which ports your app is listenting on.\n\n\n## HTTP latency analysis\n\nMonitor routes, latency and codes. REST compliant.\n\n```javascript\npmx.http(); // You must do this BEFORE any require('http')\n```\nIgnore some routes by passing a list of regular expressions.\n```javascript\npmx.http({\n http : true, // (Default: true)\n ignore_routes : [/socket\\.io/, /notFound/] // Ignore http routes with this pattern (Default: [])\n});\n```\nThis can also be done via pmx.init()\n```javascript\npmx.init({\n http : true, // (Default: true)\n ignore_routes : [/socket\\.io/, /notFound/] // Ignore http routes with this pattern (Default: [])\n});\n```\n\n**This module is enabled by default if you called pmx with the init() function.**\n\n## Measure\n\nMeasure critical segments of you code thanks to 4 kind of probes:\n\n- Simple metrics: Values that can be read instantly\n - Monitor variable value\n- Counter: Things that increment or decrement\n - Downloads being processed, user connected\n- Meter: Things that are measured as events / interval\n - Request per minute for a http server\n- Histogram: Keeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution\n - Monitor the mean of execution of a query into database\n\n#### Common options\n\n- `name` : The probe name as is will be displayed on the **Keymetrics** dashboard\n- `agg_type` : This param is optionnal, it can be `sum`, `max`, `min`, `avg` (default) or `none`. It will impact the way the probe data are aggregated within the **Keymetrics** backend. Use `none` if this is irrelevant (eg: constant or string value).\n\n\n### Metric\n\nValues that can be read instantly.\n\n```javascript\nvar probe = pmx.probe();\n\nvar metric = probe.metric({\n name : 'Realtime user',\n agg_type: 'max',\n value : function() {\n return Object.keys(users).length;\n }\n});\n```\n\n### Counter\n\nThings that increment or decrement.\n\n```javascript\nvar probe = pmx.probe();\n\nvar counter = probe.counter({\n name : 'Downloads',\n agg_type: 'sum'\n});\n\nhttp.createServer(function(req, res) {\n counter.inc();\n req.on('end', function() {\n counter.dec();\n });\n});\n```\n\n### Meter\n\nThings that are measured as events / interval.\n\n```javascript\nvar probe = pmx.probe();\n\nvar meter = probe.meter({\n name : 'req/sec',\n samples : 1,\n timeframe : 60\n});\n\nhttp.createServer(function(req, res) {\n meter.mark();\n res.end({success:true});\n});\n```\n#### Options\n\n**samples** option is the rate unit. Defaults to **1** sec.\n\n**timeframe** option is the timeframe over which events will be analyzed. Defaults to **60** sec.\n\n### Histogram\n\nKeeps a resevoir of statistically relevant values biased towards the last 5 minutes to explore their distribution.\n\n```javascript\nvar probe = pmx.probe();\n\nvar histogram = probe.histogram({\n name : 'latency',\n measurement : 'mean'\n});\n\nvar latency = 0;\n\nsetInterval(function() {\n latency = Math.round(Math.random() * 100);\n histogram.update(latency);\n}, 100);\n```\n\n#### Options\n\n**measurement** option can be:\n\n- min: The lowest observed value.\n- max: The highest observed value.\n- sum: The sum of all observed values.\n- variance: The variance of all observed values.\n- mean: The average of all observed values.\n- stddev: The stddev of all observed values.\n- count: The number of observed values.\n- median: 50% of all values in the resevoir are at or below this value.\n- p75: See median, 75% percentile.\n- p95: See median, 95% percentile.\n- p99: See median, 99% percentile.\n- p999: See median, 99.9% percentile.\n\n## Expose data (JSON object)\n\n```javascript\npmx.transpose('variable name', function() { return my_data });\n\n// or\n\npmx.tranpose({\n name : 'variable name',\n value : function() { return my_data; }\n});\n```\n\n## Modules\n\n### Simple app\n\n```\nprocess.env.MODULE_DEBUG = true;\n\nvar pmx = require('pmx');\n\nvar conf = pmx.initModule();\n```\n\n# Beta\n\n### Long running with data emitter (scoped action)\n\nA scoped action is an action that can emit logs related to this action.\n\n```javascript\nvar pmx = require('pmx');\n\npmx.scopedAction('scoped:action', function(options, res) {\n var i = setInterval(function() {\n // Emit progress data\n if (error)\n res.error('oops');\n else\n res.send('this is a chunk of data');\n }, 1000);\n\n setTimeout(function() {\n clearInterval(i);\n return res.end();\n }, 8000);\n});\n```\n\n\n# License\n\nMIT\n", + "readmeFilename": "README.md", + "gitHead": "8f487ccb89d25d5bc1d23a0fc75a50ae7d9aab5a", + "bugs": { + "url": "https://github.com/keymetrics/pmx/issues" + }, + "homepage": "https://github.com/keymetrics/pmx#readme", + "_id": "pmx@0.3.23", + "_shasum": "fbb9c118f63109aedeb4309903898c70f978396b", + "_from": "pmx@*" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/action.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/action.mocha.js new file mode 100644 index 00000000..3a195adb --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/action.mocha.js @@ -0,0 +1,164 @@ + + +var pmx = require('..'); + +function forkApp(script) { + var app = require('child_process').fork(__dirname + (script || '/proc.mock.js'), []); + return app; +} + +function forkAppWithOptions() { + var app = require('child_process').fork(__dirname + '/proc-option.mock.js', []); + return app; +} + +describe('Action module', function() { + + describe('Action without option', function() { + var app; + var action_name; + + after(function() { + process.kill(app.pid); + }); + + it('should notify PM2 of a new action available', function(done) { + app = forkApp(); + + app.once('message', function(dt) { + dt.type.should.eql('axm:action'); + dt.data.action_name.should.eql('test:nab'); + dt.data.opts.comment.should.eql('This is a test'); + dt.data.opts.display.should.eql(true); + + action_name = dt.data.action_name; + + done(); + }); + }); + + it('should trigger the action', function(done) { + app.once('message', function(dt) { + dt.type.should.eql('axm:reply'); + dt.data.return.res.should.eql('hello moto'); + done(); + }); + + app.send(action_name); + }); + + it('should trigger the action via Object arity 1 (FALLBACK)', function(done) { + app.once('message', function(dt) { + dt.type.should.eql('axm:reply'); + dt.data.return.res.should.eql('hello moto'); + done(); + }); + + app.send({msg : action_name, opts : { sisi : 'true' }}); + }); + + it('should not trigger the action if wrong action name', function(done) { + app.once('message', function(dt) { + throw new Error('Should not be called'); + }); + + app.send({ + action_name : 'im unknown' + }); + + setTimeout(done, 200); + }); + }); + + describe('Action with extra options (parameters)', function() { + var app; + var action_name; + + after(function() { + process.kill(app.pid); + }); + + it('should notify PM2 of a new action available', function(done) { + app = forkAppWithOptions(); + + app.once('message', function(dt) { + dt.type.should.eql('axm:action'); + dt.data.action_name.should.eql('test:with:options'); + action_name = dt.data.action_name; + done(); + }); + }); + + it('should trigger the action without failing (2 args without option)', function(done) { + app.once('message', function(dt) { + dt.type.should.eql('axm:reply'); + dt.data.return.res.should.eql('hello moto'); + done(); + }); + + app.send(action_name); + }); + + it('should trigger the action', function(done) { + app.once('message', function(dt) { + dt.type.should.eql('axm:reply'); + dt.data.return.res.should.eql('hello moto'); + dt.data.return.options.f1.should.eql('ab'); + done(); + }); + + app.send({ msg : action_name, opts : { f1 : 'ab', f2 : 'cd'}}); + }); + + it('should not trigger the action if wrong action name', function(done) { + app.once('message', function(dt) { + throw new Error('Should not be called'); + }); + + app.send('im unknown'); + + setTimeout(done, 200); + }); + + }); + + describe('Scoped Action (option, emitter, callback)', function() { + var app; + var action_name; + + after(function() { + process.kill(app.pid); + }); + + it('should notify PM2 of a new action available', function(done) { + app = forkApp('/fixtures/scoped-action.fixture.js'); + + app.once('message', function(dt) { + dt.type.should.eql('axm:action'); + dt.data.action_name.should.eql('scoped:action'); + dt.data.action_type.should.eql('scoped'); + action_name = dt.data.action_name; + done(); + }); + }); + + it('should stream data', function(done) { + app.once('message', function(dt) { + dt.type.should.eql('axm:scoped_action:stream'); + dt.data.data.should.eql('data random'); + done(); + }); + + app.send({ action_name : action_name, uuid : 'Random nb'}); + }); + + it('should trigger the action', function(done) { + app.on('message', function(dt) { + if (dt.type == 'axm:scoped_action:end') + done(); + }); + }); + + }); + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/auto.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/auto.mocha.js new file mode 100644 index 00000000..7e4239ec --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/auto.mocha.js @@ -0,0 +1,101 @@ + + +var axm = require('..'); +var request = require('request'); +var should = require('should'); + +var Plan = require('./helpers/plan'); + +function fork() { + return require('child_process').fork(__dirname + '/transaction/app.mock.auto.js', []); +} + +describe('Automatic transaction', function() { + it('should have right properties', function(done) { + axm.should.have.property('http'); + done(); + }); + + var app; + + + after(function() { + process.kill(app.pid); + }); + + it('should receive configuration flag', function(done) { + app = fork(); + + app.once('message', function(data) { + data.type.should.eql('axm:option:configuration'); + done(); + }); + + }); + + it('should not log fast http request', function(done) { + var rcpt = function(data) { + if (data.type == 'axm:option:configuration') + return false; + if (data.type == 'axm:monitor') + return false; + + return data.type.should.not.eql('http:transaction'); + }; + + app.on('message', rcpt); + + setTimeout(function() { + app.removeListener('message', rcpt); + return done(); + }, 500); + + setTimeout(function() { + request('http://127.0.0.1:9007/', function(req, res) {}); + }, 100); + }); + + it('should not log ignored http request', function(done) { + var timer = setTimeout(function() { + app.removeListener('message', rcpt); + return done(); + }, 1000); + + var rcpt = function(data) { + if (data.type == 'axm:option:configuration') + return false; + if (data.type == 'axm:monitor') + return false; + + return data.type.should.not.eql('http:transaction'); + }; + + app.on('message', rcpt); + + setTimeout(function() { + request('http://127.0.0.1:9007/socket.io/slow', function(req, res) {}); + }, 100); + }); + + it('should log slow http request', function(done) { + var plan = new Plan(3, done); + + app.on('message', function(data) { + if (data.type == 'axm:monitor') { + plan.ok(true); + if (Object.keys(data.data) < 3) + plan.ok(false); + } + + if (data.type == 'http:transaction') { + data.data.should.have.properties('ip', 'time', 'url', 'method'); + plan.ok(true); + } + }); + + setTimeout(function() { + request('http://127.0.0.1:9007/slow', function(req, res) {}); + }, 100); + }); + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/event.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/event.mocha.js new file mode 100644 index 00000000..901597df --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/event.mocha.js @@ -0,0 +1,38 @@ + +var axm = require('..'); + +function fork() { + return require('child_process').fork(__dirname + '/event.mock.js', []); +} + +describe('Event', function() { + it('should have right property', function(done) { + axm.should.have.property('emit'); + done(); + }); + + describe('Event scenario', function() { + var app; + + before(function() { + app = fork(); + }); + + after(function() { + process.kill(app.pid); + }); + + it('should send right event data when called', function(done) { + app.once('message', function(data) { + data.type.should.eql('human:event'); + data.data.user.should.eql('toto'); + data.data.__name.should.eql('test'); + data.data.subobj.subobj.a.should.eql('b'); + done(); + }); + }); + }); + + + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/event.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/event.mock.js new file mode 100644 index 00000000..faacdcf5 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/event.mock.js @@ -0,0 +1,13 @@ + +var axm = require('..'); + +setInterval(function() { + axm.emit('test', { + user : 'toto', + subobj : { + subobj : { + a : 'b' + } + } + }); +}, 100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/express/app.js b/test/fixtures/module-fixture/node_modules/pmx/test/express/app.js new file mode 100644 index 00000000..39da2a22 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/express/app.js @@ -0,0 +1,23 @@ + +var axm = require('../..'); +var express = require('express'); +var app = express(); + +var err = new Error('jajajja'); + +err.url = 'http://thd.com/'; + +axm.notify(err); + +app.get('/', function(req, res){ + res.send('Hello World'); +}); + +app.get('/error', function(req, res, next){ + next(new Error('toto')); +}); + +app.use(axm.expressErrorHandler()); + + +app.listen(3001); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/express/package.json b/test/fixtures/module-fixture/node_modules/pmx/test/express/package.json new file mode 100644 index 00000000..a8c001f1 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/express/package.json @@ -0,0 +1,14 @@ +{ + "name": "express", + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies" : { + "express" : "*" + }, + "author": "", + "license": "ISC" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/histogram.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/histogram.fixture.js new file mode 100644 index 00000000..f8a2776a --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/histogram.fixture.js @@ -0,0 +1,45 @@ + + +var axm = require('../..'); + +var probe = axm.probe(); + +var histogram = probe.histogram({ + name : 'test', + measurement : 'p95', + agg_type: 'sum' +}); + +var a = 0; + +setInterval(function() { + a = Math.round(Math.random() * 100); + histogram.update(a); +}, 100); + +var h2 = probe.histogram({ + name : 'mean', + measurement : 'mean', + unit : 'ms' +}); + +var b = 0; + +setInterval(function() { + b = Math.round(Math.random() * 100); + h2.update(b); +}, 100); + + +var h3 = probe.histogram({ + name : 'min', + measurement : 'min', + agg_type: 'min' +}); + +var c = 0; + +setInterval(function() { + c = Math.round(Math.random() * 100); + h3.update(c); +}, 100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/module.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/module.fixture.js new file mode 100644 index 00000000..b19db785 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/module.fixture.js @@ -0,0 +1,4 @@ + +var pmx = require('../../..'); + +var conf = pmx.initModule(); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/package.json b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/package.json new file mode 100644 index 00000000..8d00b455 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/module/package.json @@ -0,0 +1,14 @@ +{ + "name": "module", + "version": "1.0.0", + "description": "comment", + "main": "module.fixture.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "config" : { + "initial" : "init-val" + }, + "author": "strzel", + "license": "ISC" +} diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor.mock.js new file mode 100644 index 00000000..ba175350 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor.mock.js @@ -0,0 +1,20 @@ + +var axm = require('../..'); + +var obj = axm.enableProbes(); + +var i = 2; + +obj.it_works = true; +obj.value = 20; +obj.i = i; + +setTimeout(function() { + i = 4; + obj.it_works = false; + obj.value = 99; + + setTimeout(function() { + axm.stopProbes(); + }, 1100); +}, 1100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor2.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor2.mock.js new file mode 100644 index 00000000..b9216580 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/monitor2.mock.js @@ -0,0 +1,26 @@ + + +var axm = require('../..'); + +var obj = axm.enableProbes(); + +var a = { + 'aaa' : { 'ok' : true }, + 'bbb' : { 'ok' : false } +}; + +// Does not refresh because it copies the val +obj.count = Object.keys(a).length; + +obj.countFn = function() { + return Object.keys(a).length; +}; + +setTimeout(function () { + a.ccc = 'test'; + a.ddd = 'test'; + + setTimeout(function () { + axm.stopProbes(); + }, 1100); +}, 1100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify.mock.js new file mode 100644 index 00000000..87d40422 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify.mock.js @@ -0,0 +1,6 @@ + +var axm = require('../..'); + +setTimeout(function() { + axm.notify('hey'); +}, 100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify_catch_all.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify_catch_all.mock.js new file mode 100644 index 00000000..a05c44df --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/notify_catch_all.mock.js @@ -0,0 +1,9 @@ + + +var axm = require('../..'); + +axm.catchAll(); + +setTimeout(function() { + throw new Error('global error'); +}, 200); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/probe.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/probe.fixture.js new file mode 100644 index 00000000..89e30c27 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/probe.fixture.js @@ -0,0 +1,118 @@ + +var axm = require('../..'); + +var probe = axm.probe(); + +var users = { + 'alex' : 'ok', + 'musta' : 'fa' +}; + +/** + * Monitor synchronous return of functions + */ +var rt_users = probe.metric({ + name : 'Realtime user', + agg_type: 'max', + value : function() { + return Object.keys(users).length; + } +}); + +/** + * Monitor value + */ + +var config_example = { + val : 'hey', + test : { + a : 'good', + sign : 'healthy' + } +} + +var cheerio = probe.metric({ + name : 'Cheerio', + value : function() { + return config_example; + } +}); + +/** + * Monitor value + */ + + +// probe.transpose('docker_config', config_example); + +probe.transpose({ + name : 'style_2_docker_config', + data : function doSomething() { + return config_example; + } +}); + +probe.transpose('style_1_docker_config', function doSomething() { + return config_example; +}); + + +/** + * Meter for HTTP + */ +var meter = probe.meter({ + name : 'req/min', + agg_type: 'min', + seconds : 60 +}); + +var http = require('http'); + +http.createServer(function(req, res) { + meter.mark(); + res.end('Thanks'); +}).listen(3400); + +/** + * Meter example + */ + +var meter2 = probe.meter({ + name : 'random', + unit : 'rd', + agg_type: 'sum', + seconds : 1 +}); + +setInterval(function() { + meter2.mark(Math.random() * 100); +}, 10); + + +setTimeout(function() { + counter.inc(); + config_example = { yes : true }; +}, 1100); + +/** + * Counter + */ + +var counter = probe.counter({ + name : 'Downloads', + agg_type: 'max' +}); + +counter.inc(); +counter.dec(); +counter.inc(); +counter.inc(); + +// console.log(cheerio.val()); +// setInterval(function() { +// console.log(counter.val()); +// console.log(meter.val()); +// console.log(meter2.val()); +// console.log(rt_users.val()); +// console.log(cheerio.val()); +// }, 1500); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.fixture.js new file mode 100644 index 00000000..ca3497f1 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.fixture.js @@ -0,0 +1,15 @@ + + +var pmx = require('../..'); + +pmx.scopedAction('scoped:action', function(opts, res) { + var i = setInterval(function() { + // Emit progress data + res.send('data random'); + }, 100); + + setTimeout(function() { + clearInterval(i); + res.end('end data'); + }, 800); +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.mocha.js new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/scoped-action.mocha.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/transpose.fixture.js b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/transpose.fixture.js new file mode 100644 index 00000000..8d7f926c --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/fixtures/transpose.fixture.js @@ -0,0 +1,36 @@ + +var axm = require('../..'); + +var probe = axm.probe(); + + +var config_example = { + val : 'hey', + test : { + a : 'good', + sign : 'healthy' + } +} + +/** + * Monitor value + */ + +// This is ompossible to do :( (refresh value by pointer): +// +// probe.transpose('docker_config', config_example); + +probe.transpose({ + name : 'style_2_docker_config', + data : function doSomething() { + return config_example; + } +}); + +probe.transpose('style_1_docker_config', function doSomething() { + return config_example; +}); + +setTimeout(function() { + config_example.val = 'new value'; +}, 1100); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/helpers/plan.js b/test/fixtures/module-fixture/node_modules/pmx/test/helpers/plan.js new file mode 100644 index 00000000..2523adcc --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/helpers/plan.js @@ -0,0 +1,23 @@ + +var assert = require('assert'); + +function Plan(count, done) { + this.done = done; + this.count = count; +} + +Plan.prototype.ok = function(expression) { + assert(expression); + + if (this.count === 0) { + assert(false, 'Too many assertions called'); + } else { + this.count--; + } + + if (this.count === 0) { + this.done(); + } +}; + +module.exports = Plan; diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/mocha.opts b/test/fixtures/module-fixture/node_modules/pmx/test/mocha.opts new file mode 100644 index 00000000..d3cb22ae --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/mocha.opts @@ -0,0 +1,4 @@ +--require should +--reporter spec +--timeout 30000000 +--slow 300 diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/module.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/module.mocha.js new file mode 100644 index 00000000..9f2982a4 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/module.mocha.js @@ -0,0 +1,119 @@ + +var pmx = require('..'); +var should = require('should'); + +function forkWithoutEnv() { + var app = require('child_process').fork(__dirname + '/fixtures/module/module.fixture.js', [], { + env : { + } + }); + return app; +} + +function forkWithSpecificVar() { + var app = require('child_process').fork(__dirname + '/fixtures/module/module.fixture.js', [], { + env : { + 'module' : '{ "option1" : "value1", "option2" : "value2", "initial" : "over" }' + } + }); + return app; +} + +describe('PMX module', function() { + var app; + var action_name; + + it('should emit a new action', function(done) { + // 1 - It should emit an action + app = forkWithoutEnv(); + + app.once('message', function(dt) { + + /** + * Right event sent + */ + dt.type.should.eql('axm:option:configuration'); + + /** + * Options set + */ + dt.data.show_module_meta.should.exists; + dt.data.description.should.eql('comment'); + dt.data.module_version.should.eql('1.0.0'); + dt.data.module_name.should.eql('module'); + + /** + * Configuration succesfully passed + */ + dt.data.initial.should.eql('init-val'); + + /** + * Should configuration variable be mirrored into module_conf + * attribute (for keymetrics purposes) + */ + dt.data.module_conf.initial.should.eql('init-val'); + done(); + }); + }); + + it('should emit a new action', function(done) { + // 1 - It should emit an action + app = forkWithSpecificVar(); + + app.once('message', function(dt) { + + /** + * Right event sent + */ + dt.type.should.eql('axm:option:configuration'); + + /** + * Options set + */ + dt.data.show_module_meta.should.exists; + dt.data.description.should.eql('comment'); + dt.data.module_version.should.eql('1.0.0'); + dt.data.module_name.should.eql('module'); + + /** + * Configuration succesfully passed + */ + dt.data.option1.should.eql('value1'); + dt.data.option2.should.eql('value2'); + dt.data.initial.should.eql('over'); + + /** + * Should configuration variable be mirrored into module_conf + * attribute (for keymetrics purposes) + */ + dt.data.module_conf.option1.should.eql('value1'); + dt.data.module_conf.option2.should.eql('value2'); + dt.data.module_conf.initial.should.eql('over'); + done(); + }); + }); + + it('should find existing file', function(done) { + var content = pmx.resolvePidPaths([ + 'asdasdsad', + 'asdasd', + 'lolilol', + __dirname + '/fixtures/file.pid' + ]); + + content.should.eql(1456); + done(); + }); + + it('should return null', function(done) { + var content = pmx.resolvePidPaths([ + 'asdasdsad', + 'asdasd', + 'lolilol' + ]); + + should(content).be.null; + done(); + }); + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/monitor.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/monitor.mocha.js new file mode 100644 index 00000000..859506d2 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/monitor.mocha.js @@ -0,0 +1,65 @@ + +var axm = require('..'); + +function fork() { + return require('child_process').fork(__dirname + '/fixtures/monitor.mock.js', []); +} + +function forkMonitor2() { + return require('child_process').fork(__dirname + '/fixtures/monitor2.mock.js', []); +} + +describe('Monitor', function() { + + it('should have properties', function(done) { + axm.should.have.property('enableProbes'); + done(); + }); + + + it('should send event when called', function(done) { + var app = fork(); + + app.once('message', function(pck) { + pck.type.should.eql('axm:monitor'); + + pck.data.it_works.should.eql(true); + pck.data.value.should.eql(20); + + app.once('message', function(pck) { + pck.type.should.eql('axm:monitor'); + + pck.data.it_works.should.eql(false); + pck.data.value.should.eql(99); + pck.data.i.should.eql(2); + + app.kill(); + + done(); + }); + }); + }); + + it('should send right value with monitor2', function(done) { + var app = forkMonitor2(); + + app.once('message', function(pck) { + pck.type.should.eql('axm:monitor'); + + pck.data.count.should.eql(2); + pck.data.countFn.should.eql(2); + + app.once('message', function(pck) { + pck.type.should.eql('axm:monitor'); + + pck.data.count.should.eql(2); + pck.data.countFn.should.eql(4); + + app.kill(); + + done(); + }); + }); + }); + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/notify.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/notify.mocha.js new file mode 100644 index 00000000..c38e8cff --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/notify.mocha.js @@ -0,0 +1,90 @@ + + +var axm = require('..'); +var should = require('should'); + +function forkCatch() { + var app = require('child_process').fork(__dirname + '/fixtures/notify_catch_all.mock.js', []); + return app; +} + +function forkNotify() { + var app = require('child_process').fork(__dirname + '/fixtures/notify.mock.js', []); + return app; +} + +describe('Notify exceptions', function() { + it('should have the right properties', function(done) { + axm.should.have.property('catchAll'); + axm.should.have.property('notify'); + axm.should.have.property('expressErrorHandler'); + done(); + }); + + it('should process simple string error', function(done) { + var ret = axm._interpretError('this is a message'); + should.exist(ret.stack); + should.exist(ret.message); + ret.message.should.eql('this is a message'); + done(); + }); + + it('should process JSON object', function(done) { + var ret = axm._interpretError({ + line : 'ok', + env : 'sisi' + }); + + should.exist(ret.stack); + should.exist(ret.message); + + ret.data.line.should.eql('ok'); + ret.data.env.should.eql('sisi'); + done(); + }); + + it('should process simple string', function(done) { + var ret = axm._interpretError('Error'); + + should.exist(ret.stack); + should.exist(ret.message); + + done(); + }); + + it('should process error', function(done) { + var ret = axm._interpretError(new Error('error')); + + should.exist(ret.stack); + should.exist(ret.message); + + done(); + }); + + + it('should catchAll exception in fork mode', function(done) { + var app = forkCatch(); + + app.once('message', function(data) { + data.type.should.eql('axm:option:configuration'); + app.once('message', function(data) { + data.type.should.eql('process:exception'); + data.data.message.should.eql('global error'); + process.kill(app.pid); + done(); + }); + }); + }); + + it('should notify process about error', function(done) { + var app = forkNotify(); + + app.once('message', function(data) { + data.type.should.eql('process:exception'); + data.data.message.should.eql('hey'); + process.kill(app.pid); + done(); + }); + }); + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/pmx.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/pmx.mocha.js new file mode 100644 index 00000000..3f75ba81 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/pmx.mocha.js @@ -0,0 +1,18 @@ + +var pmx = require('..'); + +describe('PMX driver', function() { + it('should have the right properties', function(done) { + pmx.should.have.property('emit'); + pmx.should.have.property('action'); + done(); + }); + + describe('Event module', function() { + it('should not hang if process not forked', function(done) { + pmx.emit('testo', { data : 'ok' }); + done(); + }); + + }); +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/probe.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/probe.mocha.js new file mode 100644 index 00000000..1b705454 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/probe.mocha.js @@ -0,0 +1,83 @@ + +var axm = require('..'); + +function fork(script) { + var app = require('child_process').fork(__dirname + (script || '/fixtures/probe.fixture.js'), []); + return app; +} + +function forkHistogram() { + var app = require('child_process').fork(__dirname + '/fixtures/histogram.fixture.js', []); + return app; + +} +describe('Probe', function() { + it('should have the right properties', function(done) { + axm.should.have.property('probe'); + + var probe = axm.probe(); + + probe.should.have.property('meter'); + probe.should.have.property('metric'); + probe.should.have.property('histogram'); + probe.should.have.property('counter'); + done(); + }); + + it('should fork app and receive data from probes', function(done) { + var app = fork(); + + app.on('message', function(pck) { + // Will iterate two times, metric change the value to false + pck.type.should.eql('axm:monitor'); + + pck.data.should.have.properties('req/min', + 'Realtime user', + 'random', + 'Cheerio'); + + if (pck.data.random.value && pck.data.random.agg_type == 'sum' && + pck.data.Cheerio.value.yes == true && pck.data.Cheerio.agg_type == 'avg' && + pck.data.Downloads.value > 1 && pck.data.Downloads.agg_type == 'max') { + app.kill(); + done(); + } + }); + }); + + it('should receive transposed data', function(done) { + var app = fork('/fixtures/transpose.fixture.js'); + var pass = 0; + + app.on('message', function(pck) { + // Will iterate two times, metric change the value to false + pck.type.should.eql('axm:monitor'); + + pck.data.should.have.properties('style_2_docker_config', + 'style_1_docker_config'); + + if (pck.data.style_1_docker_config.value.val == 'new value' && + pck.data.style_2_docker_config.value.val == 'new value') { + app.kill(); + done(); + } + }); + }); + + it('should fork app and receive data', function(done) { + var app = forkHistogram(); + + app.on('message', function(pck) { + pck.type.should.eql('axm:monitor'); + + if (pck.data.mean && pck.data.mean.agg_type == 'avg' && + pck.data.min && pck.data.min.agg_type == 'min' && + pck.data.test && pck.data.test.agg_type == 'sum') { + app.kill(); + done(); + } + }); + }); + + +}) diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/proc-option.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/proc-option.mock.js new file mode 100644 index 00000000..bcdd7794 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/proc-option.mock.js @@ -0,0 +1,8 @@ + + +var axm = require('..'); + +axm.action('test:with:options', function(options, reply) { + console.log('CHILD: Action test called from external process'); + reply({ res : 'hello moto', options : options}); +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/proc.mock.js b/test/fixtures/module-fixture/node_modules/pmx/test/proc.mock.js new file mode 100644 index 00000000..3bb99a4d --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/proc.mock.js @@ -0,0 +1,7 @@ + +var axm = require('..'); + +axm.action('test:nab', {comment : 'This is a test', display : true}, function(reply) { + console.log('CHILD: Action test called from external process'); + reply({ res : 'hello moto'}); +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/profiling.mocha.js b/test/fixtures/module-fixture/node_modules/pmx/test/profiling.mocha.js new file mode 100644 index 00000000..05da3f5b --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/profiling.mocha.js @@ -0,0 +1,47 @@ + +var pmx = require('..'); +var Profiling = require('../lib/probes/profiling.js'); +var should = require('should'); +var shelljs = require('shelljs'); + +describe('Profiling', function() { + it('should have right properties', function(done) { + pmx.should.have.property('v8Profiling'); + Profiling.should.have.property('detectV8Profiler'); + Profiling.should.have.property('exposeProfiling'); + Profiling.should.have.property('v8Profiling'); + done(); + }); + + it('should return error as v8-profiler not present', function(done) { + Profiling.detectV8Profiler(function(err, data) { + err.should.not.be.null; + should(data).be.undefined; + done(); + }); + }); + + describe.skip('V8-profiler', function() { + before(function(done) { + shelljs.exec('npm install v8-profiler', function() { + setTimeout(done, 10000); + }); + }); + + after(function(done) { + shelljs.exec('npm uninstall v8-profiler', function() { + done(); + }); + }); + + it('should detect v8 profiler', function(done) { + Profiling.detectV8Profiler(function(err, data) { + console.log(arguments); + err.should.not.be.null; + should(data).be.undefined; + done(); + }); + }); + }); + +}); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/transaction.pmx.js b/test/fixtures/module-fixture/node_modules/pmx/test/transaction.pmx.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/transaction/app.mock.auto.js b/test/fixtures/module-fixture/node_modules/pmx/test/transaction/app.mock.auto.js new file mode 100644 index 00000000..50cd2bd3 --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/transaction/app.mock.auto.js @@ -0,0 +1,37 @@ + +require('../..').init({ + ignore_routes : [/\/socket\.io.*/] +}); + +var express = require('express'); +var app = express(); + +app.get('/', function(req, res) { + res.send(202, {success:true}); +}); + +app.get('/nothing', function(req, res) { + res.send('yes'); +}); + + +app.get('/slow', function(req, res) { + setTimeout(function() { + res.send('yes'); + }, 700); +}); + +app.get('/socket.io/slow', function(req, res) { + setTimeout(function() { + res.send('yes'); + }, 700); +}); + +app.get('/nothing2', function(req, res) { + setTimeout(function() { + res.send('yes'); + }, 1000); +}); + + +app.listen(9007); diff --git a/test/fixtures/module-fixture/node_modules/pmx/test/transaction/package.json b/test/fixtures/module-fixture/node_modules/pmx/test/transaction/package.json new file mode 100644 index 00000000..bdc7199e --- /dev/null +++ b/test/fixtures/module-fixture/node_modules/pmx/test/transaction/package.json @@ -0,0 +1,14 @@ +{ + "name": "transaction", + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies" : { + "express" : "*" + }, + "author": "", + "license": "ISC" +}