diff --git a/load-test-engine/load-test-engine.js b/load-test-engine/load-test-engine.js new file mode 100644 index 0000000..7be015c --- /dev/null +++ b/load-test-engine/load-test-engine.js @@ -0,0 +1,92 @@ +var hash = require('crypto'); +// Generates a label if one is not provided by user +function hashCall(stub, message, interval) { + return hash.createHash('sha256') + .update(stub.toString() + JSON.stringify(message) + interval.toString()) + .digest('hex'); +} +// Recursive setTimeout for repeating calls +function repeatCall(call) { + call.stub(call.message); + call.timeout = setTimeout(function () { repeatCall(call); }, call.interval); +} +var LoadTestEngine = /** @class */ (function () { + function LoadTestEngine() { + this.calls = {}; + this.active = {}; + } + LoadTestEngine.prototype.addCall = function (stub, message, interval, label, timeout) { + if (label === void 0) { label = hashCall(stub, message, interval); } + if (this.calls[label]) { + throw new Error('Label already exists.'); + } + this.calls[label] = { + stub: stub, + message: message, + interval: interval, + timeout: timeout + }; + console.log("Call ".concat(label, " added.")); + return this; + }; + LoadTestEngine.prototype.removeCall = function (label) { + if (this.calls[label]) { + delete this.calls[label]; + console.log("Call ".concat(label, " removed")); + return this; + } + else { + throw new Error('Label does not exist.'); + } + }; + LoadTestEngine.prototype.getLabels = function () { + return Object.keys(this.calls); + }; + LoadTestEngine.prototype.start = function (labels) { + var _this = this; + labels.forEach(function (label) { + // Check that call is not already active + if (!_this.active[label]) { + // The associated this.calls object for the current label + var call = _this.calls[label]; + // Set a recursive timeout + console.log("Call ".concat(label, " started.")); + repeatCall(call); + // Add to active calls tracker + _this.active[label] = call; + } + }); + }; + LoadTestEngine.prototype.startAll = function () { + console.log("Starting all calls."); + for (var label in this.calls) { + if (!this.active[label]) { + var call = this.calls[label]; + console.log("Call ".concat(label, " started.")); + repeatCall(call); + this.active[label] = call; + } + } + }; + LoadTestEngine.prototype.stop = function (labels) { + var _this = this; + labels.forEach(function (label) { + clearTimeout(_this.active[label].timeout); + delete _this.active[label]; + console.log("Call ".concat(label, " stopped.")); + }); + }; + LoadTestEngine.prototype.stopAll = function () { + if (!Object.keys(this.active).length) { + throw new Error('No active calls.'); + } + for (var label in this.active) { + clearTimeout(this.active[label].timeout); + delete this.active[label]; + console.log("Call ".concat(label, " stopped.")); + } + console.log('All active calls stopped.'); + }; + return LoadTestEngine; +}()); +module.exports = new LoadTestEngine(); diff --git a/load-test-engine/load-test-engine.ts b/load-test-engine/load-test-engine.ts new file mode 100644 index 0000000..92c3802 --- /dev/null +++ b/load-test-engine/load-test-engine.ts @@ -0,0 +1,110 @@ +const hash = require('crypto'); + +// Generates a label if one is not provided by user +function hashCall(stub, message, interval) { + return hash.createHash('sha256') + .update(stub.toString() + JSON.stringify(message) + interval.toString()) + .digest('hex'); +} + +// Recursive setTimeout for repeating calls +function repeatCall(call) { + call.stub(call.message); + call.timeout = setTimeout(() => {repeatCall(call)}, call.interval); +} + +type stub = { + stub: (arg: any) => any, + message: Record, + interval: number, + timeout: NodeJS.Timeout | undefined, +} + +class LoadTestEngine { + private calls: Record + private active: Record + + constructor() { + this.calls = {}; + this.active = {}; + } + + addCall(stub: (arg: any) => any, message: Record, interval: number, label: string = hashCall(stub, message, interval), timeout: NodeJS.Timeout | undefined): LoadTestEngine { + if (this.calls[label]) { + throw new Error('Label already exists.'); + } + this.calls[label] = { + stub, + message, + interval, + timeout + } + console.log(`Call ${label} added.`); + return this; + } + + removeCall(label): LoadTestEngine { + if (this.calls[label]) { + delete this.calls[label]; + console.log(`Call ${label} removed`); + return this; + } else { + throw new Error('Label does not exist.') + } + } + + getLabels(): Array { + return Object.keys(this.calls); + } + + start(labels: Array): void { + labels.forEach((label) => { + // Check that call is not already active + if (!this.active[label]) { + // The associated this.calls object for the current label + const call = this.calls[label]; + // Set a recursive timeout + console.log(`Call ${label} started.`); + repeatCall(call); + // Add to active calls tracker + this.active[label] = call; + } + }) + } + + startAll(): void { + console.log(`Starting all calls.`); + for (const label in this.calls) { + if (!this.active[label]) { + const call = this.calls[label]; + console.log(`Call ${label} started.`); + repeatCall(call); + this.active[label] = call; + } + } + } + + stop(labels: Array): void { + labels.forEach((label) => { + clearTimeout(this.active[label].timeout); + delete this.active[label]; + console.log(`Call ${label} stopped.`); + }) + } + + stopAll(): void { + if (!Object.keys(this.active).length) { + throw new Error('No active calls.') + } + + for (const label in this.active) { + clearTimeout(this.active[label].timeout); + delete this.active[label]; + console.log(`Call ${label} stopped.`); + } + console.log('All active calls stopped.'); + } + +} + +module.exports = new LoadTestEngine(); \ No newline at end of file diff --git a/load-test-engine/lte-demo.js b/load-test-engine/lte-demo.js new file mode 100644 index 0000000..b9b4396 --- /dev/null +++ b/load-test-engine/lte-demo.js @@ -0,0 +1,25 @@ +const engine = require('./load-test-engine.js'); + +const dummyFn1 = (message) => { + console.log(message) +} +const dummyFn2 = (message) => { + console.log(message) +} + +engine + .addCall(dummyFn1, {dF1Message: 'Hi!'}, 1000, 'dummy1') + .addCall(dummyFn2, {dF2Message: 'Hello!'}, 2000, 'dummy2') + .startAll(); + +setTimeout(() => { + engine.stopAll(); + + console.log('Calls currently stored on engine: ', engine.getLabels()); + + engine + .removeCall('dummy1') + .removeCall('dummy2'); + + console.log('Calls currently stored on engine: ', engine.getLabels()); +}, 10000) \ No newline at end of file