clever-queue 0.2.4 → 0.4.1

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.
Files changed (49) hide show
  1. package/.gitlab-ci.yml +20 -8
  2. package/README.md +111 -16
  3. package/dist/engine/index.d.ts +1 -1
  4. package/dist/engine/index.js +2 -2
  5. package/dist/engine/index.js.map +1 -1
  6. package/dist/engine/interfaces.d.ts +1 -1
  7. package/dist/helpers/errors.js +1 -1
  8. package/dist/helpers/errors.js.map +1 -1
  9. package/dist/queues/index.d.ts +1 -1
  10. package/dist/queues/index.js +15 -4
  11. package/dist/queues/index.js.map +1 -1
  12. package/dist/queues/interfaces.d.ts +1 -1
  13. package/dist/queues/interfaces.js +10 -2
  14. package/dist/queues/interfaces.js.map +1 -1
  15. package/dist/runners/index.d.ts +1 -1
  16. package/dist/runners/index.js +1 -1
  17. package/dist/runners/index.js.map +1 -1
  18. package/dist/tasks/index.d.ts +1 -1
  19. package/dist/tasks/index.js +8 -3
  20. package/dist/tasks/index.js.map +1 -1
  21. package/documentation/demo_1E_2Q_1R_8T_Weight.svg +1 -0
  22. package/documentation/demo_1E_2Q_2R_8T_ProrityRunner.svg +1 -0
  23. package/documentation/demo_1E_3Q_1R_12T_Priority.svg +1 -0
  24. package/eslint.config.mjs +1 -0
  25. package/examples/src/demo_1E_2Q_1R_8T_Weight.ts +40 -0
  26. package/examples/src/demo_1E_2Q_2R_8T_ProrityRunner.ts +41 -0
  27. package/examples/src/demo_1E_3Q_1R_12T_Priority.ts +45 -0
  28. package/examples/src/example00.ts +1 -1
  29. package/examples/src/helpers/animations.ts +2 -2
  30. package/examples/src/path-expression-matcher.d.ts +6 -0
  31. package/package.json +15 -5
  32. package/src/engine/index.ts +3 -3
  33. package/src/engine/interfaces.ts +1 -1
  34. package/src/helpers/errors.ts +1 -1
  35. package/src/queues/index.ts +14 -5
  36. package/src/queues/interfaces.ts +17 -4
  37. package/src/runners/index.ts +2 -2
  38. package/src/tasks/index.ts +8 -4
  39. package/test/behavior/scheduler.mjs +124 -0
  40. package/test/issues/00001.mjs +16 -14
  41. package/test/issues/00002.mjs +38 -0
  42. package/test/issues/00008-error-contract.mjs +52 -0
  43. package/test/issues/00009-weight-semantics.mjs +54 -0
  44. package/test/issues/00010-lifecycle-semantics.mjs +101 -0
  45. package/test/issues/00012-wording-cleanup.mjs +30 -0
  46. package/test/miscellaneous/test.mjs +16 -9
  47. package/test/units/engine.mjs +2 -2
  48. package/test/units/queue.mjs +19 -2
  49. package/test/units/task.mjs +13 -12
@@ -3,7 +3,7 @@
3
3
  import * as _ from "../helpers";
4
4
  import { Options, Statistics, ErrorsList, FunctionToExecute } from "./interfaces";
5
5
 
6
- type Status = "unknown" | "initialiazing" | "queued" | "running" | "runned" | "exception" | "destroying";
6
+ type Status = "unknown" | "initializing" | "queued" | "running" | "completed" | "exception" | "destroying";
7
7
 
8
8
  const defaultOptions: Options = {
9
9
  logFunction: undefined,
@@ -33,7 +33,7 @@ class Task {
33
33
  }
34
34
 
35
35
  constructor(function_: FunctionToExecute, opt: Options) {
36
- this.status = "initialiazing";
36
+ this.status = "initializing";
37
37
  this.options = this.#checkOptionsConsistancy({ ...defaultOptions, ...opt });
38
38
  if (!this.options.id) this.options.id = _.Id.generate();
39
39
  if (typeof function_ !== "function") throw new _.Errors.CQError(ErrorsList.FunctionIsNotAFunction);
@@ -46,11 +46,15 @@ class Task {
46
46
  this.status = "running";
47
47
  try {
48
48
  this.result = await this.#functionToExecute();
49
- this.status = "runned";
49
+ this.status = "completed";
50
50
  this.resolver(this.result);
51
51
  } catch (error) {
52
52
  this.status = "exception";
53
- this.rejecter(new _.Errors.CQError(ErrorsList.FunctionRaisedAnHundledException));
53
+ if (error instanceof Error) {
54
+ this.rejecter(error);
55
+ } else {
56
+ this.rejecter(new _.Errors.CQError(ErrorsList.FunctionRaisedAnHundledException));
57
+ }
54
58
  }
55
59
  };
56
60
  }
@@ -0,0 +1,124 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import * as cleverQueue from "../../dist/index.js";
5
+
6
+ function createTrackedTask(engine, label) {
7
+ return engine.createTask(() => label, { id: label });
8
+ }
9
+
10
+ async function dequeueLabels(engine, count, priority = cleverQueue.queues.Priorities.BestEffort) {
11
+ const labels = [];
12
+ for (let index = 0; index < count; index += 1) {
13
+ const task = await engine.getNextTask(priority);
14
+ assert.ok(task, `expected task ${index + 1} to be available`);
15
+ labels.push(task.options.id);
16
+ }
17
+
18
+ const finalTask = await engine.getNextTask(priority);
19
+ assert.equal(finalTask, undefined);
20
+ return labels;
21
+ }
22
+
23
+ describe("scheduler behavior", () => {
24
+ it("prefers higher-priority queues before lower-priority queues", async () => {
25
+ const engine = cleverQueue.createEngine({ autostart: false, bestEffortRunners: 1 });
26
+ const bestEffortQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.BestEffort, weight: 10 });
27
+ const standardQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 10 });
28
+ const absoluteQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Absolute, weight: 10 });
29
+
30
+ bestEffortQueue.enqueue(createTrackedTask(engine, "best-effort"));
31
+ standardQueue.enqueue(createTrackedTask(engine, "standard"));
32
+ absoluteQueue.enqueue(createTrackedTask(engine, "absolute"));
33
+
34
+ engine.start();
35
+ const labels = await dequeueLabels(engine, 3);
36
+ assert.deepEqual(labels, ["absolute", "standard", "best-effort"]);
37
+ engine.stop();
38
+ });
39
+
40
+ it("distributes work proportionally between queues with different weights", async () => {
41
+ const engine = cleverQueue.createEngine({ autostart: false, bestEffortRunners: 1 });
42
+ const heavyQueue = engine.createQueue({ id: "heavy", priority: cleverQueue.queues.Priorities.Standard, weight: 2 });
43
+ const lightQueue = engine.createQueue({ id: "light", priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
44
+
45
+ for (let index = 0; index < 4; index += 1) heavyQueue.enqueue(createTrackedTask(engine, `heavy-${index}`));
46
+ for (let index = 0; index < 2; index += 1) lightQueue.enqueue(createTrackedTask(engine, `light-${index}`));
47
+
48
+ engine.start();
49
+ const labels = await dequeueLabels(engine, 6);
50
+ assert.deepEqual(labels, ["heavy-0", "heavy-1", "light-0", "heavy-2", "heavy-3", "light-1"]);
51
+ engine.stop();
52
+ });
53
+
54
+ it("starts multiple tasks concurrently when multiple runners are available", async () => {
55
+ const engine = cleverQueue.createEngine({ bestEffortRunners: 2 });
56
+ const queue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
57
+
58
+ let started = 0;
59
+ let release;
60
+ const releasePromise = new Promise((resolve) => {
61
+ release = resolve;
62
+ });
63
+ const events = [];
64
+
65
+ const createBlockingTask = (label) =>
66
+ engine.createTask(async () => {
67
+ started += 1;
68
+ events.push(`${label}:start:${started}`);
69
+ await releasePromise;
70
+ events.push(`${label}:finish`);
71
+ return label;
72
+ }, { id: label });
73
+
74
+ const first = queue.enqueue(createBlockingTask("first"));
75
+ const second = queue.enqueue(createBlockingTask("second"));
76
+
77
+ await new Promise((resolve, reject) => {
78
+ const timeout = setTimeout(() => reject(new Error("timed out waiting for both runners to start tasks")), 250);
79
+ const poll = () => {
80
+ if (started === 2) {
81
+ clearTimeout(timeout);
82
+ resolve();
83
+ return;
84
+ }
85
+
86
+ setTimeout(poll, 5);
87
+ };
88
+
89
+ poll();
90
+ });
91
+
92
+ release();
93
+ assert.deepEqual(await Promise.all([first, second]), ["first", "second"]);
94
+ assert.deepEqual(events.slice(0, 2), ["first:start:1", "second:start:2"]);
95
+ engine.stop();
96
+ });
97
+
98
+ it("keeps priority dominance while still applying weighted fairness within a priority", async () => {
99
+ const engine = cleverQueue.createEngine({ autostart: false, bestEffortRunners: 1 });
100
+ const absoluteQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Absolute, weight: 1 });
101
+ const standardHeavyQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 2 });
102
+ const standardLightQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
103
+
104
+ absoluteQueue.enqueue(createTrackedTask(engine, "absolute-0"));
105
+ absoluteQueue.enqueue(createTrackedTask(engine, "absolute-1"));
106
+ for (let index = 0; index < 4; index += 1) standardHeavyQueue.enqueue(createTrackedTask(engine, `standard-heavy-${index}`));
107
+ for (let index = 0; index < 2; index += 1) standardLightQueue.enqueue(createTrackedTask(engine, `standard-light-${index}`));
108
+
109
+ engine.start();
110
+ const labels = await dequeueLabels(engine, 8);
111
+
112
+ assert.deepEqual(labels, [
113
+ "absolute-0",
114
+ "absolute-1",
115
+ "standard-heavy-0",
116
+ "standard-heavy-1",
117
+ "standard-light-0",
118
+ "standard-heavy-2",
119
+ "standard-heavy-3",
120
+ "standard-light-1",
121
+ ]);
122
+ engine.stop();
123
+ });
124
+ });
@@ -8,28 +8,30 @@ const engineOptions = {
8
8
  logFunction: undefined,
9
9
  };
10
10
 
11
- const task = async (string_) => {
12
- // console.log(string_, "Start", new Date());
13
- await new Promise((resolve, reject) => setTimeout(reject(string_), 2000));
14
- // console.log(string_, "End", new Date());
15
- return string_;
11
+
12
+ const task = async (customErrorMessage) => {
13
+ await new Promise((resolve) => setTimeout(resolve, 1000));
14
+ throw customErrorMessage; // this is the not tight way to throw an exception, but it is to test the case where the function raises an exception that is not an instance of Error
16
15
  };
17
16
 
18
17
  describe("Issue #1", () => {
19
- it("Exception if function passed raised an exception", () => {
18
+ it("Exception if function passed raised an exception that is not an instance of Error", async () => {
20
19
  const engine = cleverQueue.createEngine(engineOptions);
21
20
  const queue1 = engine.createQueue({
22
21
  name: "A",
23
22
  priority: cleverQueue.queues.Priorities.Absolute,
24
23
  weight: 50,
25
24
  });
26
- assert.rejects(
27
- async () => {
28
- const myTask = engine.createTask(() => task("taskMustFail"), {});
29
- await queue1.enqueue(myTask);
30
- },
31
- { name: "FunctionRaisedAnHundledException" },
32
- );
33
- engine.stop();
25
+ try {
26
+ await assert.rejects(
27
+ async () => {
28
+ const myTask = engine.createTask(() => task("a message exception that is not an instance of Error"), {});
29
+ await queue1.enqueue(myTask);
30
+ },
31
+ { name: cleverQueue.tasks.ErrorsList.FunctionRaisedAnHundledException.name },
32
+ );
33
+ } finally {
34
+ engine.stop();
35
+ }
34
36
  });
35
37
  });
@@ -0,0 +1,38 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import * as cleverQueue from "../../dist/index.js";
5
+
6
+ const engineOptions = {
7
+ bestEffortRunners: 1,
8
+ logFunction: undefined,
9
+ };
10
+
11
+
12
+ const task = async (customErrorMessage) => {
13
+ await new Promise((resolve) => setTimeout(resolve, 1000));
14
+ if (customErrorMessage) throw new Error(customErrorMessage); // this is the not tight way to throw an exception, but it is to test the case where the function raises an exception that is not an instance of Error
15
+ };
16
+
17
+ describe("Issue #2", () => {
18
+ it("Exception if function passed raised an exception with a correct Error Object", async () => {
19
+ const engine = cleverQueue.createEngine(engineOptions);
20
+ const queue1 = engine.createQueue({
21
+ name: "A",
22
+ priority: cleverQueue.queues.Priorities.Absolute,
23
+ weight: 50,
24
+ });
25
+ const customErrorMessage = "Custom error message";
26
+ try {
27
+ await assert.rejects(
28
+ async () => {
29
+ const myTask = engine.createTask(() => task(customErrorMessage), {});
30
+ await queue1.enqueue(myTask);
31
+ },
32
+ { name: "Error", message: customErrorMessage },
33
+ );
34
+ } finally {
35
+ engine.stop();
36
+ }
37
+ });
38
+ });
@@ -0,0 +1,52 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import * as cleverQueue from "../../dist/index.js";
5
+
6
+ const engineOptions = {
7
+ bestEffortRunners: 1,
8
+ };
9
+
10
+ async function runFailingTask(thrownValue) {
11
+ const engine = cleverQueue.createEngine(engineOptions);
12
+ const queue = engine.createQueue({
13
+ priority: cleverQueue.queues.Priorities.Absolute,
14
+ weight: 50,
15
+ });
16
+
17
+ try {
18
+ await queue.enqueue(
19
+ engine.createTask(async () => {
20
+ throw thrownValue;
21
+ }, {}),
22
+ );
23
+ throw new Error("task was expected to reject");
24
+ } finally {
25
+ engine.stop();
26
+ }
27
+ }
28
+
29
+ describe("Issue #8 - task error propagation contract", () => {
30
+ it("rejects with the original Error instance when a task throws an Error", async () => {
31
+ const originalError = new Error("original error");
32
+
33
+ await assert.rejects(() => runFailingTask(originalError), (error) => {
34
+ assert.equal(error, originalError);
35
+ return true;
36
+ });
37
+ });
38
+
39
+ it("wraps thrown strings in the documented CQError fallback", async () => {
40
+ await assert.rejects(() => runFailingTask("boom"), {
41
+ name: cleverQueue.tasks.ErrorsList.FunctionRaisedAnHundledException.name,
42
+ message: cleverQueue.tasks.ErrorsList.FunctionRaisedAnHundledException.message,
43
+ });
44
+ });
45
+
46
+ it("wraps thrown objects in the documented CQError fallback", async () => {
47
+ await assert.rejects(() => runFailingTask({ code: "E_OBJECT" }), {
48
+ name: cleverQueue.tasks.ErrorsList.FunctionRaisedAnHundledException.name,
49
+ message: cleverQueue.tasks.ErrorsList.FunctionRaisedAnHundledException.message,
50
+ });
51
+ });
52
+ });
@@ -0,0 +1,54 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import * as cleverQueue from "../../dist/index.js";
5
+
6
+ async function dequeueIds(engine, count) {
7
+ const ids = [];
8
+ for (let index = 0; index < count; index += 1) {
9
+ const task = await engine.getNextTask(cleverQueue.queues.Priorities.BestEffort);
10
+ assert.ok(task);
11
+ ids.push(task.options.id);
12
+ }
13
+
14
+ return ids;
15
+ }
16
+
17
+ describe("Issue #9 - queue weight semantics", () => {
18
+ it("exposes 1 as the lowest valid queue weight", () => {
19
+ assert.equal(cleverQueue.queues.Weights.Lowest, 1);
20
+ });
21
+
22
+ it("rejects weight 0 because active queues must have a positive scheduling weight", () => {
23
+ const engine = cleverQueue.createEngine({ bestEffortRunners: 1 });
24
+
25
+ assert.throws(
26
+ () =>
27
+ engine.createQueue({
28
+ priority: cleverQueue.queues.Priorities.Standard,
29
+ weight: 0,
30
+ }),
31
+ {
32
+ name: cleverQueue.queues.ErrorsList.NoWeightOptionsOnQueueInitialization.name,
33
+ message: cleverQueue.queues.ErrorsList.NoWeightOptionsOnQueueInitialization.message,
34
+ },
35
+ );
36
+
37
+ engine.stop();
38
+ });
39
+
40
+ it("uses larger weights to receive proportionally more scheduling turns over time", async () => {
41
+ const engine = cleverQueue.createEngine({ autostart: false, bestEffortRunners: 1 });
42
+ const heavyQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 2 });
43
+ const lightQueue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
44
+
45
+ for (let index = 0; index < 4; index += 1) heavyQueue.enqueue(engine.createTask(() => undefined, { id: `heavy-${index}` }));
46
+ for (let index = 0; index < 2; index += 1) lightQueue.enqueue(engine.createTask(() => undefined, { id: `light-${index}` }));
47
+
48
+ engine.start();
49
+ const ids = await dequeueIds(engine, 6);
50
+
51
+ assert.deepEqual(ids, ["heavy-0", "heavy-1", "light-0", "heavy-2", "heavy-3", "light-1"]);
52
+ engine.stop();
53
+ });
54
+ });
@@ -0,0 +1,101 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import * as cleverQueue from "../../dist/index.js";
5
+
6
+ function createDeferred() {
7
+ /** @type {(value?: unknown) => void} */
8
+ let resolve;
9
+ const promise = new Promise((resolver) => {
10
+ resolve = resolver;
11
+ });
12
+
13
+ return { promise, resolve };
14
+ }
15
+
16
+ describe("Issue #10 - lifecycle semantics", () => {
17
+ it("rejects queued tasks when the engine is stopped, while letting the in-flight task finish", async () => {
18
+ const blocker = createDeferred();
19
+ const engine = cleverQueue.createEngine({ bestEffortRunners: 1 });
20
+ const queue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
21
+
22
+ const firstTask = queue.enqueue(
23
+ engine.createTask(async () => {
24
+ await blocker.promise;
25
+ return "first";
26
+ }, {}),
27
+ );
28
+ const secondTask = queue.enqueue(engine.createTask(() => "second", {}));
29
+
30
+ await new Promise((resolve) => setTimeout(resolve, 25));
31
+ engine.stop();
32
+ blocker.resolve();
33
+
34
+ assert.equal(await firstTask, "first");
35
+ await assert.rejects(secondTask, {
36
+ name: cleverQueue.queues.ErrorsList.QueueIsStopping.name,
37
+ });
38
+ });
39
+
40
+ it("rejects pending tasks immediately when a queue is stopped", async () => {
41
+ const engine = cleverQueue.createEngine({ bestEffortRunners: 1 });
42
+ const queue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
43
+ const pendingTask = queue.enqueue(engine.createTask(() => "never runs", {}));
44
+
45
+ queue.stop();
46
+
47
+ await assert.rejects(pendingTask, {
48
+ name: cleverQueue.queues.ErrorsList.QueueIsStopping.name,
49
+ });
50
+ engine.stop();
51
+ });
52
+
53
+ it("lets a runner finish its current task before stopping without starting the next one", async () => {
54
+ const blocker = createDeferred();
55
+ const engine = cleverQueue.createEngine({ autostart: false, bestEffortRunners: 1 });
56
+ const queue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
57
+ const runner = engine.runners[0];
58
+
59
+ const firstTask = queue.enqueue(
60
+ engine.createTask(async () => {
61
+ await blocker.promise;
62
+ return "first";
63
+ }, {}),
64
+ );
65
+ const secondTask = queue.enqueue(engine.createTask(() => "second", {}));
66
+
67
+ engine.start();
68
+ await new Promise((resolve) => setTimeout(resolve, 25));
69
+ runner.stop();
70
+ blocker.resolve();
71
+
72
+ assert.equal(await firstTask, "first");
73
+ assert.equal(runner.statistics().status, "stopping");
74
+
75
+ await new Promise((resolve) => setTimeout(resolve, 25));
76
+ assert.equal(queue.statistics().tasks, 1);
77
+
78
+ queue.stop();
79
+ await assert.rejects(secondTask, {
80
+ name: cleverQueue.queues.ErrorsList.QueueIsStopping.name,
81
+ });
82
+ engine.stop();
83
+ });
84
+
85
+ it("rejects new tasks after a queue has been stopped", async () => {
86
+ const engine = cleverQueue.createEngine({ bestEffortRunners: 1 });
87
+ const queue = engine.createQueue({ priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
88
+
89
+ queue.stop();
90
+
91
+ await assert.rejects(
92
+ queue.enqueue(engine.createTask(() => "late", {})),
93
+ {
94
+ name: cleverQueue.queues.ErrorsList.QueueIsStopping.name,
95
+ message: cleverQueue.queues.ErrorsList.QueueIsStopping.message,
96
+ },
97
+ );
98
+
99
+ engine.stop();
100
+ });
101
+ });
@@ -0,0 +1,30 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ import * as cleverQueue from "../../dist/index.js";
5
+
6
+ describe("Issue #12 - wording and status cleanup", () => {
7
+ it("uses the normalized initializing status wording across runtime objects", () => {
8
+ const task = new cleverQueue.tasks.Task(() => undefined, { autostart: false });
9
+ assert.equal(task.statistics().status, "initializing");
10
+
11
+ const engine = cleverQueue.createEngine({ autostart: false, bestEffortRunners: 1 });
12
+ const queue = engine.createQueue({ autostart: false, priority: cleverQueue.queues.Priorities.Standard, weight: 1 });
13
+ const runner = engine.createRunner({ autostart: false, priority: cleverQueue.queues.Priorities.BestEffort });
14
+
15
+ assert.equal(engine.statistics().engine.status, "initializing");
16
+ assert.equal(queue.statistics().status, "initializing");
17
+ assert.equal(runner.statistics().status, "initializing");
18
+ engine.stop();
19
+ });
20
+
21
+ it("reports completed instead of runned after successful task execution", async () => {
22
+ const task = new cleverQueue.tasks.Task(() => "done", {});
23
+ task.resolver = () => {};
24
+ task.rejecter = () => {};
25
+
26
+ await task.run();
27
+
28
+ assert.equal(task.statistics().status, "completed");
29
+ });
30
+ });
@@ -8,27 +8,32 @@ const engineOptions = {
8
8
  logFunction: undefined,
9
9
  };
10
10
 
11
+ function trackEnqueue(promises, queue, task) {
12
+ promises.push(queue.enqueue(task).catch(() => undefined));
13
+ }
14
+
11
15
  describe("Scheduler Unit Tests", () => {
12
16
  it("Priority Test", () => {
17
+ const pending = [];
13
18
  const engine = cleverQueue.createEngine(engineOptions);
14
19
  const queue1 = engine.createQueue({
15
20
  name: "A",
16
21
  priority: cleverQueue.queues.Priorities.Absolute,
17
22
  weight: 50,
18
23
  });
19
- for (let index = 0; index < 100; index++) queue1.enqueue(engine.createTask(() => {}, {}));
24
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue1, engine.createTask(() => {}, {}));
20
25
  const queue2 = engine.createQueue({
21
26
  name: "B",
22
27
  priority: cleverQueue.queues.Priorities.Standard,
23
28
  weight: 50,
24
29
  });
25
- for (let index = 0; index < 100; index++) queue2.enqueue(engine.createTask(() => {}, {}));
30
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue2, engine.createTask(() => {}, {}));
26
31
  const queue3 = engine.createQueue({
27
32
  name: "C",
28
33
  priority: cleverQueue.queues.Priorities.BestEffort,
29
34
  weight: 50,
30
35
  });
31
- for (let index = 0; index < 100; index++) queue3.enqueue(engine.createTask(() => {}, {}));
36
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue3, engine.createTask(() => {}, {}));
32
37
 
33
38
  const engineStatistics = engine.statistics();
34
39
  assert.equal(engineStatistics.engine.queues, 3);
@@ -36,25 +41,26 @@ describe("Scheduler Unit Tests", () => {
36
41
  });
37
42
 
38
43
  it("Weighted Test", () => {
44
+ const pending = [];
39
45
  const engine = cleverQueue.createEngine(engineOptions);
40
46
  const queue1 = engine.createQueue({
41
47
  name: "A",
42
48
  priority: cleverQueue.queues.Priorities.Absolute,
43
49
  weight: 80,
44
50
  });
45
- for (let index = 0; index < 100; index++) queue1.enqueue(engine.createTask(() => {}, {}));
51
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue1, engine.createTask(() => {}, {}));
46
52
  const queue2 = engine.createQueue({
47
53
  name: "B",
48
54
  priority: cleverQueue.queues.Priorities.Absolute,
49
55
  weight: 50,
50
56
  });
51
- for (let index = 0; index < 100; index++) queue2.enqueue(engine.createTask(() => {}, {}));
57
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue2, engine.createTask(() => {}, {}));
52
58
  const queue3 = engine.createQueue({
53
59
  name: "C",
54
60
  priority: cleverQueue.queues.Priorities.Absolute,
55
61
  weight: 20,
56
62
  });
57
- for (let index = 0; index < 100; index++) queue3.enqueue(engine.createTask(() => {}, {}));
63
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue3, engine.createTask(() => {}, {}));
58
64
 
59
65
  const engineStatistics = engine.statistics();
60
66
  assert.equal(engineStatistics.engine.queues, 3);
@@ -62,6 +68,7 @@ describe("Scheduler Unit Tests", () => {
62
68
  });
63
69
 
64
70
  it("Combine Test", () => {
71
+ const pending = [];
65
72
  const engine = cleverQueue.createEngine(engineOptions);
66
73
 
67
74
  const queue1 = engine.createQueue({
@@ -69,21 +76,21 @@ describe("Scheduler Unit Tests", () => {
69
76
  priority: cleverQueue.queues.Priorities.Absolute,
70
77
  weight: 80,
71
78
  });
72
- for (let index = 0; index < 100; index++) queue1.enqueue(engine.createTask(() => {}, {}));
79
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue1, engine.createTask(() => {}, {}));
73
80
 
74
81
  const queue2 = engine.createQueue({
75
82
  name: "B - Standard - 50",
76
83
  priority: cleverQueue.queues.Priorities.Standard,
77
84
  weight: 50,
78
85
  });
79
- for (let index = 0; index < 100; index++) queue2.enqueue(engine.createTask(() => {}, {}));
86
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue2, engine.createTask(() => {}, {}));
80
87
 
81
88
  const queue3 = engine.createQueue({
82
89
  name: "C- Standard - 20",
83
90
  priority: cleverQueue.queues.Priorities.Standard,
84
91
  weight: 20,
85
92
  });
86
- for (let index = 0; index < 100; index++) queue3.enqueue(engine.createTask(() => {}, {}));
93
+ for (let index = 0; index < 100; index++) trackEnqueue(pending, queue3, engine.createTask(() => {}, {}));
87
94
 
88
95
  const engineStatistics = engine.statistics();
89
96
  assert.equal(engineStatistics.engine.queues, 3);
@@ -4,7 +4,7 @@ import { describe, it } from "node:test";
4
4
  import * as cleverQueue from "../../dist/index.js";
5
5
 
6
6
  describe("Engine Class Unit Tests", () => {
7
- it("Successfull Initialisation without any option (use hardcoded default options)", () => {
7
+ it("Successful Initialisation without any option (use hardcoded default options)", () => {
8
8
  const engine = cleverQueue.createEngine();
9
9
  const statistics = engine.statistics();
10
10
  assert.equal(statistics.engine.runners, 1);
@@ -22,7 +22,7 @@ describe("Engine Class Unit Tests", () => {
22
22
  );
23
23
  });
24
24
 
25
- it("Successfull Initialisation with 3 Runners", () => {
25
+ it("Successful Initialisation with 3 Runners", () => {
26
26
  const engineOptions = {
27
27
  bestEffortRunners: 3,
28
28
  };
@@ -9,7 +9,7 @@ const engineOptions = {
9
9
  };
10
10
 
11
11
  describe("Queue Class Unit Tests", () => {
12
- it("Successfull Initialisation without any option (use hardcoded default options)", () => {
12
+ it("Successful Initialisation without any option (use hardcoded default options)", () => {
13
13
  const engine = cleverQueue.createEngine();
14
14
  const statistics = engine.statistics();
15
15
  assert.equal(statistics.engine.runners, 1);
@@ -17,7 +17,7 @@ describe("Queue Class Unit Tests", () => {
17
17
  engine.stop();
18
18
  });
19
19
 
20
- it("Successfull Initialisation", () => {
20
+ it("Successful Initialisation", () => {
21
21
  const engine = cleverQueue.createEngine(engineOptions);
22
22
  const queueOptions = {
23
23
  priority: cleverQueue.queues.Priorities.Standard,
@@ -31,4 +31,21 @@ describe("Queue Class Unit Tests", () => {
31
31
  assert.equal(queueStatistics.weight, queueOptions.weight);
32
32
  engine.stop();
33
33
  });
34
+
35
+ it("Rejects negative weight because queue weights must stay within the supported range", () => {
36
+ const engine = cleverQueue.createEngine(engineOptions);
37
+ assert.throws(
38
+ () =>
39
+ engine.createQueue({
40
+ priority: cleverQueue.queues.Priorities.Standard,
41
+ weight: -1,
42
+ }),
43
+ { name: cleverQueue.queues.ErrorsList.NoWeightOptionsOnQueueInitialization.name },
44
+ );
45
+ engine.stop();
46
+ });
47
+
48
+ it("Keeps the exported lowest weight constant at one", () => {
49
+ assert.equal(cleverQueue.queues.Weights.Lowest, 1);
50
+ });
34
51
  });