@ably-labs/locust 0.0.1 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ _[Ably](https://ably.com) is the platform that powers synchronized digital exper
10
10
 
11
11
  ## Quick Start
12
12
 
13
- See [example/users.js](/example/users.js) for an example of defining Locust users in JavaScript, and [example/index.js](/example/index.js) for an example program which connects to a Locust master and runs the defined users during a load test.
13
+ See [example/users.ts](/example/users.ts) for an example of defining Locust users in TypeScript, and [example/index.ts](/example/index.ts) for an example program which connects to a Locust master and runs the defined users during a load test.
14
14
 
15
15
  The example can be run by copying `example/.env.sample` to `example/.env`, setting `ABLY_API_KEY` to your Ably API key, and running [Docker Compose](https://docs.docker.com/compose/):
16
16
 
@@ -83,3 +83,15 @@ worker.register('ExampleUser', () => new User());
83
83
 
84
84
  worker.run();
85
85
  ```
86
+
87
+ ## Testing
88
+
89
+ Start an instance of Locust and run the tests:
90
+
91
+ ```
92
+ cd tests
93
+
94
+ docker compose up --build
95
+
96
+ npm test
97
+ ```
package/lib/Worker.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import winston from 'winston';
1
+ import * as winston from 'winston';
2
2
  import { Dealer } from 'zeromq';
3
3
  import { Message, MessageType } from './Message';
4
4
  import { Stats } from './Stats';
@@ -48,6 +48,10 @@ interface User {
48
48
  * A function which is registered with a worker to initialise a User.
49
49
  */
50
50
  declare type UserFn = () => User;
51
+ /**
52
+ * A function which is called when an event happens.
53
+ */
54
+ declare type EventFn = (data: any) => void;
51
55
  /**
52
56
  * A Locust worker which connects to a Locust master ZeroMQ socket and spawns
53
57
  * Locust users during an active load test.
@@ -105,6 +109,12 @@ export declare class Worker {
105
109
  * discard outdated messages.
106
110
  */
107
111
  lastReceivedSpawnTimestamp: number;
112
+ /**
113
+ * A set of functions which are called when events happen.
114
+ */
115
+ callbacks: {
116
+ [key: string]: EventFn[];
117
+ };
108
118
  constructor(opts: Options);
109
119
  /**
110
120
  * Register a function to initialise users of the given class name.
@@ -120,6 +130,18 @@ export declare class Worker {
120
130
  *
121
131
  */
122
132
  run(): Promise<void>;
133
+ /**
134
+ * Register a callback to be called when an event happens.
135
+ */
136
+ on(event: string, callback: (data: any) => void): void;
137
+ /**
138
+ * Register a callback to be called when a user starts.
139
+ */
140
+ onUserStart(callback: EventFn): void;
141
+ /**
142
+ * Register a callback to be called when a user stops.
143
+ */
144
+ onUserStop(callback: EventFn): void;
123
145
  /**
124
146
  * Send a protocol message to the Locust master.
125
147
  */
@@ -195,5 +217,9 @@ export declare class Worker {
195
217
  };
196
218
  user_count: number;
197
219
  };
220
+ /**
221
+ * Invoke all callbacks for the given event with the given data.
222
+ */
223
+ emit(event: string, data: any): void;
198
224
  }
199
225
  export {};
package/lib/Worker.js CHANGED
@@ -1,4 +1,23 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
22
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
23
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -42,12 +61,9 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
42
61
  function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
43
62
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
44
63
  };
45
- var __importDefault = (this && this.__importDefault) || function (mod) {
46
- return (mod && mod.__esModule) ? mod : { "default": mod };
47
- };
48
64
  Object.defineProperty(exports, "__esModule", { value: true });
49
65
  exports.Worker = void 0;
50
- var winston_1 = __importDefault(require("winston"));
66
+ var winston = __importStar(require("winston"));
51
67
  var zeromq_1 = require("zeromq");
52
68
  var constants_1 = require("./constants");
53
69
  var Message_1 = require("./Message");
@@ -72,15 +88,16 @@ var Worker = /** @class */ (function () {
72
88
  this.userFns = {};
73
89
  this.users = {};
74
90
  this.state = "ready" /* Ready */;
75
- this.log = winston_1.default.createLogger({
76
- format: winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.json()),
91
+ this.log = winston.createLogger({
92
+ format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
77
93
  transports: [
78
- new winston_1.default.transports.Console({
94
+ new winston.transports.Console({
79
95
  level: opts.logLevel || 'info',
80
96
  }),
81
97
  ],
82
98
  });
83
99
  this.lastReceivedSpawnTimestamp = 0;
100
+ this.callbacks = {};
84
101
  }
85
102
  /**
86
103
  * Register a function to initialise users of the given class name.
@@ -127,7 +144,7 @@ var Worker = /** @class */ (function () {
127
144
  data = _c.value[0];
128
145
  try {
129
146
  msg = Message_1.Message.decode(data);
130
- this.log.info("Received '" + msg.type + "' message");
147
+ this.log.debug("Received '" + msg.type + "' message");
131
148
  this.handle(msg);
132
149
  }
133
150
  catch (err) {
@@ -157,6 +174,27 @@ var Worker = /** @class */ (function () {
157
174
  });
158
175
  });
159
176
  };
177
+ /**
178
+ * Register a callback to be called when an event happens.
179
+ */
180
+ Worker.prototype.on = function (event, callback) {
181
+ if (!this.callbacks[event]) {
182
+ this.callbacks[event] = [];
183
+ }
184
+ this.callbacks[event].push(callback);
185
+ };
186
+ /**
187
+ * Register a callback to be called when a user starts.
188
+ */
189
+ Worker.prototype.onUserStart = function (callback) {
190
+ this.on('user.start', callback);
191
+ };
192
+ /**
193
+ * Register a callback to be called when a user stops.
194
+ */
195
+ Worker.prototype.onUserStop = function (callback) {
196
+ this.on('user.stop', callback);
197
+ };
160
198
  /**
161
199
  * Send a protocol message to the Locust master.
162
200
  */
@@ -199,6 +237,7 @@ var Worker = /** @class */ (function () {
199
237
  this.lastReceivedSpawnTimestamp = data.timestamp;
200
238
  this.state = "spawning" /* Spawning */;
201
239
  this.send("spawning" /* Spawning */, null);
240
+ this.log.info("Handling spawn message: expected = " + JSON.stringify(data.user_classes_count) + ", actual = " + JSON.stringify(this.spawnState().user_classes_count));
202
241
  for (var _i = 0, _a = Object.keys(data.user_classes_count); _i < _a.length; _i++) {
203
242
  var userClass = _a[_i];
204
243
  // check we have a registered function for the given class
@@ -279,11 +318,13 @@ var Worker = /** @class */ (function () {
279
318
  * Start the given number of users.
280
319
  */
281
320
  Worker.prototype.startUsers = function (userClass, userFn, count) {
321
+ this.log.debug("Starting " + count + " " + userClass + " users");
282
322
  for (var i = 0; i < count; i++) {
283
323
  try {
284
324
  var user = userFn();
285
325
  user.start();
286
326
  this.users[userClass].push(user);
327
+ this.emit('user.start', { userClass: userClass });
287
328
  }
288
329
  catch (err) {
289
330
  this.logException("Error initialising " + userClass + " user: " + err);
@@ -301,10 +342,12 @@ var Worker = /** @class */ (function () {
301
342
  * Stop the given number of users.
302
343
  */
303
344
  Worker.prototype.stopUsers = function (userClass, count) {
345
+ this.log.debug("Stopping " + count + " " + userClass + " users");
304
346
  for (var i = 0; i < count; i++) {
305
347
  var user = this.users[userClass].pop();
306
348
  if (user !== undefined) {
307
349
  user.stop();
350
+ this.emit('user.stop', { userClass: userClass });
308
351
  }
309
352
  }
310
353
  };
@@ -378,6 +421,19 @@ var Worker = /** @class */ (function () {
378
421
  user_count: Object.values(this.users).reduce(function (sum, users) { return sum + users.length; }, 0),
379
422
  };
380
423
  };
424
+ /**
425
+ * Invoke all callbacks for the given event with the given data.
426
+ */
427
+ Worker.prototype.emit = function (event, data) {
428
+ var callbacks = this.callbacks[event];
429
+ if (!callbacks) {
430
+ return;
431
+ }
432
+ for (var _i = 0, callbacks_1 = callbacks; _i < callbacks_1.length; _i++) {
433
+ var callback = callbacks_1[_i];
434
+ callback(data);
435
+ }
436
+ };
381
437
  return Worker;
382
438
  }());
383
439
  exports.Worker = Worker;
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * See https://github.com/locustio/locust/blob/2.4.3/locust/runners.py#L879-L886
8
8
  */
9
- export declare const PROTOCOL_VERSION = "2.4.3";
9
+ export declare const PROTOCOL_VERSION = "2.5.0";
10
10
  /**
11
11
  * The interval between successive heartbeats.
12
12
  */
package/lib/constants.js CHANGED
@@ -9,7 +9,7 @@ exports.STATS_INTERVAL = exports.HEARTBEAT_INTERVAL = exports.PROTOCOL_VERSION =
9
9
  *
10
10
  * See https://github.com/locustio/locust/blob/2.4.3/locust/runners.py#L879-L886
11
11
  */
12
- exports.PROTOCOL_VERSION = '2.4.3';
12
+ exports.PROTOCOL_VERSION = '2.5.0';
13
13
  /**
14
14
  * The interval between successive heartbeats.
15
15
  */
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@ably-labs/locust",
3
- "version": "0.0.1",
3
+ "version": "0.0.5",
4
4
  "description": "A JavaScript load generator for Locust.io.",
5
5
  "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
6
7
  "scripts": {
7
8
  "build": "tsc --build",
8
9
  "clean": "tsc --build --clean",
@@ -11,7 +12,7 @@
11
12
  "test": "jest"
12
13
  },
13
14
  "files": [
14
- "lib"
15
+ "lib/**/*"
15
16
  ],
16
17
  "repository": {
17
18
  "type": "git",