@ai.ntellect/core 0.7.8 → 1.0.0
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 +158 -81
- package/index.ts +462 -22
- package/package copy.json +21 -0
- package/package.json +9 -44
- package/tsconfig.json +108 -22
- package/types.ts +62 -0
- package/utils/executor.ts +42 -0
- package/.mocharc.json +0 -5
- package/dist/graph/controller.d.ts +0 -31
- package/dist/graph/controller.d.ts.map +0 -1
- package/dist/graph/controller.js +0 -71
- package/dist/graph/controller.js.map +0 -1
- package/dist/graph/event-manager.d.ts +0 -93
- package/dist/graph/event-manager.d.ts.map +0 -1
- package/dist/graph/event-manager.js +0 -296
- package/dist/graph/event-manager.js.map +0 -1
- package/dist/graph/index.d.ts +0 -159
- package/dist/graph/index.d.ts.map +0 -1
- package/dist/graph/index.js +0 -303
- package/dist/graph/index.js.map +0 -1
- package/dist/graph/logger.d.ts +0 -46
- package/dist/graph/logger.d.ts.map +0 -1
- package/dist/graph/logger.js +0 -69
- package/dist/graph/logger.js.map +0 -1
- package/dist/graph/node.d.ts +0 -93
- package/dist/graph/node.d.ts.map +0 -1
- package/dist/graph/node.js +0 -259
- package/dist/graph/node.js.map +0 -1
- package/dist/graph/observer.d.ts +0 -115
- package/dist/graph/observer.d.ts.map +0 -1
- package/dist/graph/observer.js +0 -198
- package/dist/graph/observer.js.map +0 -1
- package/dist/index.d.ts +0 -26
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -42
- package/dist/index.js.map +0 -1
- package/dist/interfaces/index.d.ts +0 -447
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/index.js +0 -75
- package/dist/interfaces/index.js.map +0 -1
- package/dist/modules/agenda/adapters/node-cron/index.d.ts +0 -17
- package/dist/modules/agenda/adapters/node-cron/index.d.ts.map +0 -1
- package/dist/modules/agenda/adapters/node-cron/index.js +0 -30
- package/dist/modules/agenda/adapters/node-cron/index.js.map +0 -1
- package/dist/modules/agenda/index.d.ts +0 -63
- package/dist/modules/agenda/index.d.ts.map +0 -1
- package/dist/modules/agenda/index.js +0 -141
- package/dist/modules/agenda/index.js.map +0 -1
- package/dist/modules/embedding/adapters/ai/index.d.ts +0 -29
- package/dist/modules/embedding/adapters/ai/index.d.ts.map +0 -1
- package/dist/modules/embedding/adapters/ai/index.js +0 -58
- package/dist/modules/embedding/adapters/ai/index.js.map +0 -1
- package/dist/modules/embedding/index.d.ts +0 -36
- package/dist/modules/embedding/index.d.ts.map +0 -1
- package/dist/modules/embedding/index.js +0 -60
- package/dist/modules/embedding/index.js.map +0 -1
- package/dist/modules/memory/adapters/in-memory/index.d.ts +0 -120
- package/dist/modules/memory/adapters/in-memory/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/in-memory/index.js +0 -211
- package/dist/modules/memory/adapters/in-memory/index.js.map +0 -1
- package/dist/modules/memory/adapters/meilisearch/index.d.ts +0 -110
- package/dist/modules/memory/adapters/meilisearch/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/meilisearch/index.js +0 -321
- package/dist/modules/memory/adapters/meilisearch/index.js.map +0 -1
- package/dist/modules/memory/adapters/redis/index.d.ts +0 -82
- package/dist/modules/memory/adapters/redis/index.d.ts.map +0 -1
- package/dist/modules/memory/adapters/redis/index.js +0 -159
- package/dist/modules/memory/adapters/redis/index.js.map +0 -1
- package/dist/modules/memory/index.d.ts +0 -67
- package/dist/modules/memory/index.d.ts.map +0 -1
- package/dist/modules/memory/index.js +0 -104
- package/dist/modules/memory/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -170
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/utils/generate-action-schema.d.ts +0 -5
- package/dist/utils/generate-action-schema.d.ts.map +0 -1
- package/dist/utils/generate-action-schema.js +0 -44
- package/dist/utils/generate-action-schema.js.map +0 -1
- package/dist/utils/header-builder.d.ts +0 -12
- package/dist/utils/header-builder.d.ts.map +0 -1
- package/dist/utils/header-builder.js +0 -35
- package/dist/utils/header-builder.js.map +0 -1
- package/graph/controller.ts +0 -74
- package/graph/event-manager.ts +0 -363
- package/graph/index.ts +0 -395
- package/graph/logger.ts +0 -70
- package/graph/node.ts +0 -327
- package/graph/observer.ts +0 -368
- package/interfaces/index.ts +0 -548
- package/modules/agenda/adapters/node-cron/index.ts +0 -25
- package/modules/agenda/index.ts +0 -146
- package/modules/embedding/adapters/ai/index.ts +0 -42
- package/modules/embedding/index.ts +0 -45
- package/modules/memory/adapters/in-memory/index.ts +0 -207
- package/modules/memory/adapters/meilisearch/index.ts +0 -361
- package/modules/memory/adapters/redis/index.ts +0 -164
- package/modules/memory/index.ts +0 -93
- package/test/graph/controller.test.ts +0 -187
- package/test/graph/event-manager.test.ts +0 -118
- package/test/graph/index.test.ts +0 -684
- package/test/graph/node.test.ts +0 -655
- package/test/graph/observer.test.ts +0 -398
- package/test/modules/agenda/node-cron.test.ts +0 -307
- package/test/modules/memory/adapters/in-memory.test.ts +0 -153
- package/test/modules/memory/adapters/meilisearch.test.ts +0 -287
- package/test/modules/memory/base.test.ts +0 -230
- package/types/index.ts +0 -190
- package/utils/generate-action-schema.ts +0 -46
- package/utils/header-builder.ts +0 -40
@@ -1,398 +0,0 @@
|
|
1
|
-
import { expect } from "chai";
|
2
|
-
import { BehaviorSubject, Subject } from "rxjs";
|
3
|
-
import { z } from "zod";
|
4
|
-
import { GraphFlow } from "../../graph";
|
5
|
-
import { GraphObserver } from "../../graph/observer";
|
6
|
-
import { GraphContext, GraphEvent, Node } from "../../types";
|
7
|
-
|
8
|
-
/**
|
9
|
-
* Test schema definition for observer tests
|
10
|
-
* Defines a schema with:
|
11
|
-
* - value: numeric value for tracking state changes
|
12
|
-
* - status: enum representing node execution status
|
13
|
-
*/
|
14
|
-
const TestSchema = z.object({
|
15
|
-
value: z.number().default(0),
|
16
|
-
status: z.enum(["pending", "completed", "failed"]).optional(),
|
17
|
-
});
|
18
|
-
|
19
|
-
/**
|
20
|
-
* Test suite for the GraphObserver implementation
|
21
|
-
* This suite validates the reactive observation capabilities:
|
22
|
-
* - State observation (global and node-specific)
|
23
|
-
* - Property observation (single and multiple)
|
24
|
-
* - Event observation and correlation
|
25
|
-
* - Workflow monitoring
|
26
|
-
* - Node execution triggers
|
27
|
-
*/
|
28
|
-
describe("GraphObserver", () => {
|
29
|
-
let graph: GraphFlow<typeof TestSchema>;
|
30
|
-
let eventSubject: Subject<GraphEvent<typeof TestSchema>>;
|
31
|
-
let stateSubject: BehaviorSubject<GraphContext<typeof TestSchema>>;
|
32
|
-
let destroySubject: Subject<void>;
|
33
|
-
let observer: GraphObserver<typeof TestSchema>;
|
34
|
-
|
35
|
-
beforeEach(() => {
|
36
|
-
graph = new GraphFlow("TestGraph", {
|
37
|
-
name: "TestGraph",
|
38
|
-
nodes: [],
|
39
|
-
context: { value: 0 },
|
40
|
-
schema: TestSchema,
|
41
|
-
});
|
42
|
-
|
43
|
-
// Initialize subjects
|
44
|
-
eventSubject = new Subject();
|
45
|
-
stateSubject = new BehaviorSubject(graph.getContext());
|
46
|
-
destroySubject = new Subject();
|
47
|
-
|
48
|
-
// Create observer instance
|
49
|
-
observer = new GraphObserver(
|
50
|
-
graph,
|
51
|
-
eventSubject,
|
52
|
-
stateSubject,
|
53
|
-
destroySubject
|
54
|
-
);
|
55
|
-
});
|
56
|
-
|
57
|
-
/**
|
58
|
-
* Test suite for state observation functionality
|
59
|
-
* Validates the ability to track and react to state changes
|
60
|
-
* in the graph context
|
61
|
-
*/
|
62
|
-
describe("State Observation", () => {
|
63
|
-
/**
|
64
|
-
* Tests sequential state change observation
|
65
|
-
* Validates that:
|
66
|
-
* - All state changes are captured in order
|
67
|
-
* - Multiple state changes in a single node are tracked
|
68
|
-
* - Initial state is included in observations
|
69
|
-
*/
|
70
|
-
it("should observe state changes", async () => {
|
71
|
-
const states: any[] = [];
|
72
|
-
const testNode: Node<typeof TestSchema> = {
|
73
|
-
name: "testNode",
|
74
|
-
execute: async (context) => {
|
75
|
-
context.value = 1;
|
76
|
-
context.value = 2;
|
77
|
-
context.value = 3;
|
78
|
-
},
|
79
|
-
};
|
80
|
-
|
81
|
-
graph.addNode(testNode);
|
82
|
-
const subscription = graph
|
83
|
-
.observe()
|
84
|
-
.state()
|
85
|
-
.subscribe((state) => {
|
86
|
-
if (state.value !== undefined) {
|
87
|
-
states.push(state.value);
|
88
|
-
}
|
89
|
-
});
|
90
|
-
|
91
|
-
await graph.execute("testNode");
|
92
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
93
|
-
subscription.unsubscribe();
|
94
|
-
|
95
|
-
expect(states).to.deep.equal([0, 1, 2, 3]);
|
96
|
-
});
|
97
|
-
|
98
|
-
/**
|
99
|
-
* Tests node-specific state observation
|
100
|
-
* Validates that:
|
101
|
-
* - State changes for specific nodes are tracked
|
102
|
-
* - Status transitions are captured correctly
|
103
|
-
* - Async state changes are properly observed
|
104
|
-
*/
|
105
|
-
it("should observe specific node state changes", async () => {
|
106
|
-
const states: any[] = [];
|
107
|
-
const testNode: Node<typeof TestSchema> = {
|
108
|
-
name: "testNode",
|
109
|
-
execute: async (context) => {
|
110
|
-
context.status = "pending";
|
111
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
112
|
-
context.status = "completed";
|
113
|
-
},
|
114
|
-
};
|
115
|
-
|
116
|
-
graph.addNode(testNode);
|
117
|
-
const subscription = graph
|
118
|
-
.observe()
|
119
|
-
.node("testNode")
|
120
|
-
.subscribe((state) => state.status && states.push(state.status));
|
121
|
-
|
122
|
-
await graph.execute("testNode");
|
123
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
124
|
-
subscription.unsubscribe();
|
125
|
-
|
126
|
-
expect(states).to.deep.equal(["pending", "completed"]);
|
127
|
-
});
|
128
|
-
|
129
|
-
/**
|
130
|
-
* Tests multi-node state observation
|
131
|
-
* Validates that:
|
132
|
-
* - Multiple nodes can be observed simultaneously
|
133
|
-
* - State changes are correctly attributed to nodes
|
134
|
-
* - Sequential execution is properly tracked
|
135
|
-
*/
|
136
|
-
it("should observe multiple nodes", async () => {
|
137
|
-
const states: any[] = [];
|
138
|
-
const node1: Node<typeof TestSchema> = {
|
139
|
-
name: "node1",
|
140
|
-
execute: async (context) => {
|
141
|
-
context.value = 1;
|
142
|
-
},
|
143
|
-
};
|
144
|
-
const node2: Node<typeof TestSchema> = {
|
145
|
-
name: "node2",
|
146
|
-
execute: async (context) => {
|
147
|
-
context.value = 2;
|
148
|
-
},
|
149
|
-
};
|
150
|
-
|
151
|
-
graph.addNode(node1);
|
152
|
-
graph.addNode(node2);
|
153
|
-
|
154
|
-
const subscription = graph
|
155
|
-
.observe()
|
156
|
-
.nodes(["node1", "node2"])
|
157
|
-
.subscribe((state) => state.value && states.push(state.value));
|
158
|
-
|
159
|
-
await graph.execute("node1");
|
160
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
161
|
-
await graph.execute("node2");
|
162
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
163
|
-
subscription.unsubscribe();
|
164
|
-
|
165
|
-
expect(states).to.deep.equal([1, 2]);
|
166
|
-
});
|
167
|
-
});
|
168
|
-
|
169
|
-
/**
|
170
|
-
* Test suite for property observation functionality
|
171
|
-
* Validates the ability to track specific properties
|
172
|
-
* of the graph context
|
173
|
-
*/
|
174
|
-
describe("Property Observation", () => {
|
175
|
-
/**
|
176
|
-
* Tests single property observation
|
177
|
-
* Validates that:
|
178
|
-
* - Individual property changes are tracked
|
179
|
-
* - Initial property value is captured
|
180
|
-
* - Property updates trigger observations
|
181
|
-
*/
|
182
|
-
it("should observe single property", async () => {
|
183
|
-
const values: any[] = [];
|
184
|
-
const testNode: Node<typeof TestSchema> = {
|
185
|
-
name: "testNode",
|
186
|
-
execute: async (context) => {
|
187
|
-
context.value = 42;
|
188
|
-
},
|
189
|
-
};
|
190
|
-
|
191
|
-
graph.addNode(testNode);
|
192
|
-
const subscription = graph
|
193
|
-
.observe()
|
194
|
-
.property("value")
|
195
|
-
.subscribe((state) => values.push(state.value));
|
196
|
-
|
197
|
-
await graph.execute("testNode");
|
198
|
-
subscription.unsubscribe();
|
199
|
-
|
200
|
-
expect(values).to.deep.equal([0, 42]);
|
201
|
-
});
|
202
|
-
|
203
|
-
/**
|
204
|
-
* Tests multiple property observation
|
205
|
-
* Validates that:
|
206
|
-
* - Multiple properties can be tracked simultaneously
|
207
|
-
* - Changes to any observed property trigger updates
|
208
|
-
* - Final state contains all tracked properties
|
209
|
-
*/
|
210
|
-
it("should observe multiple properties", async () => {
|
211
|
-
const values: any[] = [];
|
212
|
-
const testNode: Node<typeof TestSchema> = {
|
213
|
-
name: "testNode",
|
214
|
-
execute: async (context) => {
|
215
|
-
context.value = 42;
|
216
|
-
context.status = "completed";
|
217
|
-
},
|
218
|
-
};
|
219
|
-
|
220
|
-
graph.addNode(testNode);
|
221
|
-
const subscription = graph
|
222
|
-
.observe()
|
223
|
-
.property(["value", "status"])
|
224
|
-
.subscribe((change) => {
|
225
|
-
const { name, ...rest } = change;
|
226
|
-
values.push(rest);
|
227
|
-
});
|
228
|
-
|
229
|
-
await graph.execute("testNode");
|
230
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
231
|
-
subscription.unsubscribe();
|
232
|
-
|
233
|
-
expect(values[values.length - 1]).to.deep.equal({
|
234
|
-
value: 42,
|
235
|
-
status: "completed",
|
236
|
-
});
|
237
|
-
});
|
238
|
-
|
239
|
-
/**
|
240
|
-
* Tests condition-based observation
|
241
|
-
* Validates that:
|
242
|
-
* - Multiple conditions can be combined
|
243
|
-
* - Observation completes when conditions are met
|
244
|
-
* - State reflects all required changes
|
245
|
-
*/
|
246
|
-
it("should wait for multiple conditions", async () => {
|
247
|
-
const testNode: Node<typeof TestSchema> = {
|
248
|
-
name: "testNode",
|
249
|
-
execute: async (context) => {
|
250
|
-
context.value = 42;
|
251
|
-
context.status = "completed";
|
252
|
-
},
|
253
|
-
};
|
254
|
-
|
255
|
-
graph.addNode(testNode);
|
256
|
-
const promise = graph
|
257
|
-
.observe()
|
258
|
-
.until(
|
259
|
-
graph.observe().property(["value", "status"]),
|
260
|
-
(state) => state.value === 42 && state.status === "completed"
|
261
|
-
);
|
262
|
-
|
263
|
-
await graph.execute("testNode");
|
264
|
-
await promise;
|
265
|
-
|
266
|
-
const context = graph.getContext();
|
267
|
-
expect(context.value).to.equal(42);
|
268
|
-
expect(context.status).to.equal("completed");
|
269
|
-
});
|
270
|
-
});
|
271
|
-
|
272
|
-
/**
|
273
|
-
* Test suite for event observation functionality
|
274
|
-
* Validates the handling of events and event correlation
|
275
|
-
*/
|
276
|
-
describe("Event Observation", () => {
|
277
|
-
/**
|
278
|
-
* Tests specific event observation
|
279
|
-
*/
|
280
|
-
it("should observe specific events", async () => {
|
281
|
-
const events: GraphEvent<typeof TestSchema>[] = [];
|
282
|
-
const subscription = graph
|
283
|
-
.observe()
|
284
|
-
.event("testEvent")
|
285
|
-
.subscribe((e) => events.push(e));
|
286
|
-
|
287
|
-
await graph.emit("testEvent", { value: 1 });
|
288
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
289
|
-
subscription.unsubscribe();
|
290
|
-
|
291
|
-
expect(events.length).to.equal(1);
|
292
|
-
expect(events[0].type).to.equal("testEvent");
|
293
|
-
});
|
294
|
-
|
295
|
-
/**
|
296
|
-
* Tests debounced event handling
|
297
|
-
*/
|
298
|
-
it("should handle debounced events", async () => {
|
299
|
-
const events: GraphEvent<typeof TestSchema>[] = [];
|
300
|
-
const subscription = graph
|
301
|
-
.observe()
|
302
|
-
.event("event1")
|
303
|
-
.subscribe((e) => events.push(e));
|
304
|
-
|
305
|
-
await graph.emit("event1", { value: 1 });
|
306
|
-
await graph.emit("event1", { value: 2 });
|
307
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
308
|
-
subscription.unsubscribe();
|
309
|
-
|
310
|
-
expect(events.length).to.equal(2);
|
311
|
-
});
|
312
|
-
|
313
|
-
/**
|
314
|
-
* Tests event correlation functionality
|
315
|
-
* Validates that:
|
316
|
-
* - Multiple events can be correlated
|
317
|
-
* - Correlation conditions are properly evaluated
|
318
|
-
* - Timeout handling works correctly
|
319
|
-
* - Events are captured in correct order
|
320
|
-
*/
|
321
|
-
it("should wait for correlated events", async () => {
|
322
|
-
// Create test events
|
323
|
-
const eventA = {
|
324
|
-
type: "eventA",
|
325
|
-
payload: { eventPayload: { status: "success" } },
|
326
|
-
timestamp: Date.now(),
|
327
|
-
} as GraphEvent<typeof TestSchema>;
|
328
|
-
|
329
|
-
const eventB = {
|
330
|
-
type: "eventB",
|
331
|
-
payload: { eventPayload: { status: "success" } },
|
332
|
-
timestamp: Date.now(),
|
333
|
-
} as GraphEvent<typeof TestSchema>;
|
334
|
-
|
335
|
-
// Emit events after a short delay
|
336
|
-
setTimeout(() => {
|
337
|
-
eventSubject.next(eventA);
|
338
|
-
eventSubject.next(eventB);
|
339
|
-
}, 100);
|
340
|
-
|
341
|
-
const events = await observer.waitForCorrelatedEvents(
|
342
|
-
["eventA", "eventB"],
|
343
|
-
2000,
|
344
|
-
(events) =>
|
345
|
-
events.every((e) => e.payload.eventPayload?.status === "success")
|
346
|
-
);
|
347
|
-
|
348
|
-
expect(events.length).to.equal(2);
|
349
|
-
expect(events[0].type).to.equal("eventA");
|
350
|
-
expect(events[1].type).to.equal("eventB");
|
351
|
-
});
|
352
|
-
|
353
|
-
afterEach(() => {
|
354
|
-
destroySubject.next();
|
355
|
-
destroySubject.complete();
|
356
|
-
eventSubject.complete();
|
357
|
-
stateSubject.complete();
|
358
|
-
});
|
359
|
-
});
|
360
|
-
|
361
|
-
/**
|
362
|
-
* Test suite for workflow observation functionality
|
363
|
-
* Validates the ability to monitor complete workflow execution
|
364
|
-
*/
|
365
|
-
describe("Workflow Observation", () => {
|
366
|
-
/**
|
367
|
-
* Tests complete workflow observation
|
368
|
-
* Validates that:
|
369
|
-
* - Entire workflow execution is tracked
|
370
|
-
* - State transitions are captured
|
371
|
-
* - Final state reflects completed workflow
|
372
|
-
*/
|
373
|
-
it("should observe complete workflow", async () => {
|
374
|
-
const states: any[] = [];
|
375
|
-
const node: Node<typeof TestSchema> = {
|
376
|
-
name: "testNode",
|
377
|
-
execute: async (context) => {
|
378
|
-
context.status = "pending";
|
379
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
380
|
-
context.status = "completed";
|
381
|
-
},
|
382
|
-
};
|
383
|
-
|
384
|
-
graph.addNode(node);
|
385
|
-
const subscription = graph
|
386
|
-
.observe()
|
387
|
-
.node("testNode")
|
388
|
-
.subscribe((state) => states.push(state));
|
389
|
-
|
390
|
-
await graph.execute("testNode");
|
391
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
392
|
-
subscription.unsubscribe();
|
393
|
-
|
394
|
-
expect(states.length).to.be.greaterThan(0);
|
395
|
-
expect(states[states.length - 1].status).to.equal("completed");
|
396
|
-
});
|
397
|
-
});
|
398
|
-
});
|
@@ -1,307 +0,0 @@
|
|
1
|
-
import { expect } from "chai";
|
2
|
-
import sinon from "sinon";
|
3
|
-
import { Agenda } from "../../../modules/agenda";
|
4
|
-
import { NodeCronAdapter } from "../../../modules/agenda/adapters/node-cron";
|
5
|
-
import { Memory } from "../../../modules/memory";
|
6
|
-
import { InMemoryAdapter } from "../../../modules/memory/adapters/in-memory";
|
7
|
-
|
8
|
-
before(function () {
|
9
|
-
this.timeout(10000);
|
10
|
-
});
|
11
|
-
|
12
|
-
describe("Agenda Service", () => {
|
13
|
-
let agenda: Agenda;
|
14
|
-
const scheduledIds: string[] = []; // Track all scheduled request IDs
|
15
|
-
|
16
|
-
beforeEach(() => {
|
17
|
-
const cronService = new NodeCronAdapter();
|
18
|
-
const inMemory = new InMemoryAdapter();
|
19
|
-
const memory = new Memory(inMemory);
|
20
|
-
agenda = new Agenda(cronService, memory);
|
21
|
-
});
|
22
|
-
|
23
|
-
afterEach(async () => {
|
24
|
-
// Cancel all scheduled requests by their IDs
|
25
|
-
scheduledIds.forEach((id) => agenda.cancelScheduledRequest(id));
|
26
|
-
scheduledIds.length = 0; // Clear the array
|
27
|
-
|
28
|
-
// Ensure all tasks are stopped
|
29
|
-
await agenda.stop();
|
30
|
-
|
31
|
-
await agenda.cancel({});
|
32
|
-
|
33
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
34
|
-
});
|
35
|
-
|
36
|
-
describe("Request Scheduling", () => {
|
37
|
-
it("should schedule a new request and return an id", async () => {
|
38
|
-
const request = {
|
39
|
-
originalRequest: "test request",
|
40
|
-
cronExpression: "0 0 * * *",
|
41
|
-
};
|
42
|
-
|
43
|
-
const id = await agenda.scheduleRequest(request);
|
44
|
-
scheduledIds.push(id); // Track the ID
|
45
|
-
|
46
|
-
expect(id).to.be.a("string");
|
47
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(1);
|
48
|
-
|
49
|
-
const scheduledRequest = (await agenda.getScheduledRequests())[0];
|
50
|
-
expect(scheduledRequest.originalRequest).to.equal(
|
51
|
-
request.originalRequest
|
52
|
-
);
|
53
|
-
expect(scheduledRequest.cronExpression).to.equal(request.cronExpression);
|
54
|
-
expect(scheduledRequest.isRecurring).to.be.false;
|
55
|
-
|
56
|
-
agenda.cancelScheduledRequest(id);
|
57
|
-
});
|
58
|
-
|
59
|
-
it("should execute callbacks when scheduling and executing", async function () {
|
60
|
-
this.timeout(5000);
|
61
|
-
|
62
|
-
const onScheduledSpy = sinon.spy();
|
63
|
-
const onExecutedSpy = sinon.spy();
|
64
|
-
|
65
|
-
const request = {
|
66
|
-
originalRequest: "test request",
|
67
|
-
cronExpression: `${(new Date().getSeconds() + 1) % 60} * * * * *`,
|
68
|
-
};
|
69
|
-
|
70
|
-
const id = await agenda.scheduleRequest(request, {
|
71
|
-
onScheduled: onScheduledSpy,
|
72
|
-
onExecuted: onExecutedSpy,
|
73
|
-
});
|
74
|
-
scheduledIds.push(id);
|
75
|
-
|
76
|
-
expect(onScheduledSpy.calledOnce).to.be.true;
|
77
|
-
|
78
|
-
await new Promise<void>((resolve, reject) => {
|
79
|
-
const timeout = setTimeout(() => {
|
80
|
-
reject(new Error("Callback execution timeout"));
|
81
|
-
}, 4000);
|
82
|
-
|
83
|
-
const checkExecution = () => {
|
84
|
-
if (onExecutedSpy.calledOnce) {
|
85
|
-
clearTimeout(timeout);
|
86
|
-
agenda.cancelScheduledRequest(id);
|
87
|
-
resolve();
|
88
|
-
return;
|
89
|
-
}
|
90
|
-
setTimeout(checkExecution, 100);
|
91
|
-
};
|
92
|
-
checkExecution();
|
93
|
-
});
|
94
|
-
|
95
|
-
expect(onExecutedSpy.calledOnce).to.be.true;
|
96
|
-
});
|
97
|
-
});
|
98
|
-
|
99
|
-
describe("Request Management", () => {
|
100
|
-
it("should cancel a scheduled request", async () => {
|
101
|
-
const request = {
|
102
|
-
originalRequest: "test request",
|
103
|
-
cronExpression: "*/1 * * * *",
|
104
|
-
};
|
105
|
-
|
106
|
-
const id = await agenda.scheduleRequest(request);
|
107
|
-
scheduledIds.push(id);
|
108
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(1);
|
109
|
-
|
110
|
-
const cancelled = await agenda.cancelScheduledRequest(id);
|
111
|
-
expect(cancelled).to.be.true;
|
112
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
113
|
-
});
|
114
|
-
|
115
|
-
it("should return false when cancelling non-existent request", async () => {
|
116
|
-
const cancelled = await agenda.cancelScheduledRequest("non-existent-id");
|
117
|
-
expect(cancelled).to.be.false;
|
118
|
-
});
|
119
|
-
|
120
|
-
it("should get all scheduled requests", async () => {
|
121
|
-
const requests = [
|
122
|
-
{
|
123
|
-
originalRequest: "request 1",
|
124
|
-
cronExpression: "*/1 * * * *",
|
125
|
-
},
|
126
|
-
{
|
127
|
-
originalRequest: "request 2",
|
128
|
-
cronExpression: "*/5 * * * *",
|
129
|
-
},
|
130
|
-
];
|
131
|
-
|
132
|
-
for (const request of requests) {
|
133
|
-
const id = await agenda.scheduleRequest(request);
|
134
|
-
scheduledIds.push(id);
|
135
|
-
}
|
136
|
-
|
137
|
-
const scheduledRequests = await agenda.getScheduledRequests();
|
138
|
-
expect(scheduledRequests).to.have.lengthOf(2);
|
139
|
-
expect(scheduledRequests[0].originalRequest).to.equal("request 1");
|
140
|
-
expect(scheduledRequests[1].originalRequest).to.equal("request 2");
|
141
|
-
});
|
142
|
-
|
143
|
-
it("should stop all scheduled requests", async () => {
|
144
|
-
const requests = [
|
145
|
-
{
|
146
|
-
originalRequest: "request 1",
|
147
|
-
cronExpression: "*/1 * * * *",
|
148
|
-
},
|
149
|
-
{
|
150
|
-
originalRequest: "request 2",
|
151
|
-
cronExpression: "*/5 * * * *",
|
152
|
-
},
|
153
|
-
];
|
154
|
-
|
155
|
-
for (const request of requests) {
|
156
|
-
await agenda.scheduleRequest(request);
|
157
|
-
}
|
158
|
-
|
159
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(2);
|
160
|
-
|
161
|
-
await agenda.stopAll();
|
162
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
163
|
-
});
|
164
|
-
});
|
165
|
-
|
166
|
-
describe("Global Management", () => {
|
167
|
-
it("should stop all scheduled requests", async () => {
|
168
|
-
const requests = [
|
169
|
-
{
|
170
|
-
originalRequest: "request 1",
|
171
|
-
cronExpression: "*/1 * * * *",
|
172
|
-
},
|
173
|
-
{
|
174
|
-
originalRequest: "request 2",
|
175
|
-
cronExpression: "*/5 * * * *",
|
176
|
-
},
|
177
|
-
];
|
178
|
-
|
179
|
-
for (const request of requests) {
|
180
|
-
await agenda.scheduleRequest(request);
|
181
|
-
}
|
182
|
-
|
183
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(2);
|
184
|
-
|
185
|
-
await agenda.stopAll();
|
186
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
187
|
-
});
|
188
|
-
});
|
189
|
-
|
190
|
-
describe("Error Handling", () => {
|
191
|
-
it("should handle execution errors gracefully", async () => {
|
192
|
-
const consoleSpy = sinon.spy(console, "error");
|
193
|
-
|
194
|
-
const request = {
|
195
|
-
originalRequest: "error request",
|
196
|
-
cronExpression: "0 0 * * *",
|
197
|
-
};
|
198
|
-
|
199
|
-
const id = await agenda.scheduleRequest(request);
|
200
|
-
|
201
|
-
// Wait for execution
|
202
|
-
await new Promise((resolve) => setTimeout(resolve, 1100));
|
203
|
-
|
204
|
-
expect(consoleSpy.called).to.be.false;
|
205
|
-
|
206
|
-
agenda.cancelScheduledRequest(id);
|
207
|
-
consoleSpy.restore();
|
208
|
-
});
|
209
|
-
});
|
210
|
-
|
211
|
-
describe("Request Execution", () => {
|
212
|
-
it("should execute non-recurring requests only once", async function () {
|
213
|
-
this.timeout(5000);
|
214
|
-
const onExecutedSpy = sinon.spy();
|
215
|
-
|
216
|
-
const request = {
|
217
|
-
originalRequest: "single execution",
|
218
|
-
cronExpression: `${new Date().getSeconds() + 1} * * * * *`,
|
219
|
-
};
|
220
|
-
|
221
|
-
const id = await agenda.scheduleRequest(request, {
|
222
|
-
onExecuted: onExecutedSpy,
|
223
|
-
});
|
224
|
-
|
225
|
-
try {
|
226
|
-
await new Promise<void>((resolve, reject) => {
|
227
|
-
const timeout = setTimeout(
|
228
|
-
() => reject(new Error("Test timeout")),
|
229
|
-
4000
|
230
|
-
);
|
231
|
-
const checkExecution = () => {
|
232
|
-
if (onExecutedSpy.calledOnce) {
|
233
|
-
clearTimeout(timeout);
|
234
|
-
resolve();
|
235
|
-
return;
|
236
|
-
}
|
237
|
-
setTimeout(checkExecution, 100);
|
238
|
-
};
|
239
|
-
checkExecution();
|
240
|
-
});
|
241
|
-
} finally {
|
242
|
-
agenda.cancelScheduledRequest(id);
|
243
|
-
}
|
244
|
-
|
245
|
-
expect(onExecutedSpy.calledOnce).to.be.true;
|
246
|
-
expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
|
247
|
-
});
|
248
|
-
|
249
|
-
it("should log execution status", async function () {
|
250
|
-
this.timeout(10000);
|
251
|
-
const consoleLogSpy = sinon.spy(console, "log");
|
252
|
-
|
253
|
-
const request = {
|
254
|
-
originalRequest: "test request",
|
255
|
-
cronExpression: `${new Date().getSeconds() + 1} * * * * *`,
|
256
|
-
};
|
257
|
-
|
258
|
-
const id = await agenda.scheduleRequest(request);
|
259
|
-
|
260
|
-
await new Promise<void>((resolve) => {
|
261
|
-
const checkExecution = () => {
|
262
|
-
if (
|
263
|
-
consoleLogSpy.calledWith(`🔄 Executing scheduled request: ${id}`) &&
|
264
|
-
consoleLogSpy.calledWith(
|
265
|
-
`✅ Scheduled request executed successfully: ${id}`
|
266
|
-
)
|
267
|
-
) {
|
268
|
-
agenda.cancelScheduledRequest(id);
|
269
|
-
resolve();
|
270
|
-
return;
|
271
|
-
}
|
272
|
-
setTimeout(checkExecution, 100);
|
273
|
-
};
|
274
|
-
checkExecution();
|
275
|
-
});
|
276
|
-
|
277
|
-
expect(consoleLogSpy.calledWith(`🔄 Executing scheduled request: ${id}`))
|
278
|
-
.to.be.true;
|
279
|
-
expect(
|
280
|
-
consoleLogSpy.calledWith(
|
281
|
-
`✅ Scheduled request executed successfully: ${id}`
|
282
|
-
)
|
283
|
-
).to.be.true;
|
284
|
-
|
285
|
-
consoleLogSpy.restore();
|
286
|
-
});
|
287
|
-
});
|
288
|
-
});
|
289
|
-
|
290
|
-
let globalAgenda: Agenda;
|
291
|
-
before(() => {
|
292
|
-
const cronService = new NodeCronAdapter();
|
293
|
-
const inMemoryAdapter = new InMemoryAdapter();
|
294
|
-
const memory = new Memory(inMemoryAdapter);
|
295
|
-
globalAgenda = new Agenda(cronService, memory);
|
296
|
-
});
|
297
|
-
|
298
|
-
after(async () => {
|
299
|
-
if (globalAgenda) {
|
300
|
-
globalAgenda.stopAll();
|
301
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
302
|
-
}
|
303
|
-
|
304
|
-
await globalAgenda.stop();
|
305
|
-
await globalAgenda.cancel({});
|
306
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
307
|
-
});
|