@principal-ai/principal-view-core 0.5.6
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 +126 -0
- package/dist/ConfigurationLoader.d.ts +76 -0
- package/dist/ConfigurationLoader.d.ts.map +1 -0
- package/dist/ConfigurationLoader.js +144 -0
- package/dist/ConfigurationLoader.js.map +1 -0
- package/dist/ConfigurationValidator.d.ts +31 -0
- package/dist/ConfigurationValidator.d.ts.map +1 -0
- package/dist/ConfigurationValidator.js +242 -0
- package/dist/ConfigurationValidator.js.map +1 -0
- package/dist/EventProcessor.d.ts +49 -0
- package/dist/EventProcessor.d.ts.map +1 -0
- package/dist/EventProcessor.js +215 -0
- package/dist/EventProcessor.js.map +1 -0
- package/dist/EventRecorderService.d.ts +305 -0
- package/dist/EventRecorderService.d.ts.map +1 -0
- package/dist/EventRecorderService.js +463 -0
- package/dist/EventRecorderService.js.map +1 -0
- package/dist/LibraryLoader.d.ts +63 -0
- package/dist/LibraryLoader.d.ts.map +1 -0
- package/dist/LibraryLoader.js +188 -0
- package/dist/LibraryLoader.js.map +1 -0
- package/dist/PathBasedEventProcessor.d.ts +90 -0
- package/dist/PathBasedEventProcessor.d.ts.map +1 -0
- package/dist/PathBasedEventProcessor.js +239 -0
- package/dist/PathBasedEventProcessor.js.map +1 -0
- package/dist/SessionManager.d.ts +194 -0
- package/dist/SessionManager.d.ts.map +1 -0
- package/dist/SessionManager.js +299 -0
- package/dist/SessionManager.js.map +1 -0
- package/dist/ValidationEngine.d.ts +31 -0
- package/dist/ValidationEngine.d.ts.map +1 -0
- package/dist/ValidationEngine.js +158 -0
- package/dist/ValidationEngine.js.map +1 -0
- package/dist/helpers/GraphInstrumentationHelper.d.ts +93 -0
- package/dist/helpers/GraphInstrumentationHelper.d.ts.map +1 -0
- package/dist/helpers/GraphInstrumentationHelper.js +248 -0
- package/dist/helpers/GraphInstrumentationHelper.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/config.d.ts +57 -0
- package/dist/rules/config.d.ts.map +1 -0
- package/dist/rules/config.js +382 -0
- package/dist/rules/config.js.map +1 -0
- package/dist/rules/engine.d.ts +70 -0
- package/dist/rules/engine.d.ts.map +1 -0
- package/dist/rules/engine.js +252 -0
- package/dist/rules/engine.js.map +1 -0
- package/dist/rules/implementations/connection-type-references.d.ts +7 -0
- package/dist/rules/implementations/connection-type-references.d.ts.map +1 -0
- package/dist/rules/implementations/connection-type-references.js +104 -0
- package/dist/rules/implementations/connection-type-references.js.map +1 -0
- package/dist/rules/implementations/dead-end-states.d.ts +17 -0
- package/dist/rules/implementations/dead-end-states.d.ts.map +1 -0
- package/dist/rules/implementations/dead-end-states.js +72 -0
- package/dist/rules/implementations/dead-end-states.js.map +1 -0
- package/dist/rules/implementations/index.d.ts +24 -0
- package/dist/rules/implementations/index.d.ts.map +1 -0
- package/dist/rules/implementations/index.js +62 -0
- package/dist/rules/implementations/index.js.map +1 -0
- package/dist/rules/implementations/library-node-type-match.d.ts +17 -0
- package/dist/rules/implementations/library-node-type-match.d.ts.map +1 -0
- package/dist/rules/implementations/library-node-type-match.js +123 -0
- package/dist/rules/implementations/library-node-type-match.js.map +1 -0
- package/dist/rules/implementations/minimum-node-sources.d.ts +22 -0
- package/dist/rules/implementations/minimum-node-sources.d.ts.map +1 -0
- package/dist/rules/implementations/minimum-node-sources.js +54 -0
- package/dist/rules/implementations/minimum-node-sources.js.map +1 -0
- package/dist/rules/implementations/no-unknown-fields.d.ts +7 -0
- package/dist/rules/implementations/no-unknown-fields.d.ts.map +1 -0
- package/dist/rules/implementations/no-unknown-fields.js +211 -0
- package/dist/rules/implementations/no-unknown-fields.js.map +1 -0
- package/dist/rules/implementations/orphaned-edge-types.d.ts +7 -0
- package/dist/rules/implementations/orphaned-edge-types.d.ts.map +1 -0
- package/dist/rules/implementations/orphaned-edge-types.js +47 -0
- package/dist/rules/implementations/orphaned-edge-types.js.map +1 -0
- package/dist/rules/implementations/orphaned-node-types.d.ts +7 -0
- package/dist/rules/implementations/orphaned-node-types.d.ts.map +1 -0
- package/dist/rules/implementations/orphaned-node-types.js +50 -0
- package/dist/rules/implementations/orphaned-node-types.js.map +1 -0
- package/dist/rules/implementations/required-metadata.d.ts +7 -0
- package/dist/rules/implementations/required-metadata.d.ts.map +1 -0
- package/dist/rules/implementations/required-metadata.js +57 -0
- package/dist/rules/implementations/required-metadata.js.map +1 -0
- package/dist/rules/implementations/state-transition-references.d.ts +7 -0
- package/dist/rules/implementations/state-transition-references.d.ts.map +1 -0
- package/dist/rules/implementations/state-transition-references.js +135 -0
- package/dist/rules/implementations/state-transition-references.js.map +1 -0
- package/dist/rules/implementations/unreachable-states.d.ts +7 -0
- package/dist/rules/implementations/unreachable-states.d.ts.map +1 -0
- package/dist/rules/implementations/unreachable-states.js +80 -0
- package/dist/rules/implementations/unreachable-states.js.map +1 -0
- package/dist/rules/implementations/valid-action-patterns.d.ts +17 -0
- package/dist/rules/implementations/valid-action-patterns.d.ts.map +1 -0
- package/dist/rules/implementations/valid-action-patterns.js +109 -0
- package/dist/rules/implementations/valid-action-patterns.js.map +1 -0
- package/dist/rules/implementations/valid-color-format.d.ts +7 -0
- package/dist/rules/implementations/valid-color-format.d.ts.map +1 -0
- package/dist/rules/implementations/valid-color-format.js +91 -0
- package/dist/rules/implementations/valid-color-format.js.map +1 -0
- package/dist/rules/implementations/valid-edge-types.d.ts +7 -0
- package/dist/rules/implementations/valid-edge-types.d.ts.map +1 -0
- package/dist/rules/implementations/valid-edge-types.js +244 -0
- package/dist/rules/implementations/valid-edge-types.js.map +1 -0
- package/dist/rules/implementations/valid-node-types.d.ts +7 -0
- package/dist/rules/implementations/valid-node-types.d.ts.map +1 -0
- package/dist/rules/implementations/valid-node-types.js +175 -0
- package/dist/rules/implementations/valid-node-types.js.map +1 -0
- package/dist/rules/index.d.ts +28 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +45 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/types.d.ts +309 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +35 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/types/canvas.d.ts +409 -0
- package/dist/types/canvas.d.ts.map +1 -0
- package/dist/types/canvas.js +70 -0
- package/dist/types/canvas.js.map +1 -0
- package/dist/types/index.d.ts +311 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/library.d.ts +185 -0
- package/dist/types/library.d.ts.map +1 -0
- package/dist/types/library.js +15 -0
- package/dist/types/library.js.map +1 -0
- package/dist/types/path-based-config.d.ts +230 -0
- package/dist/types/path-based-config.d.ts.map +1 -0
- package/dist/types/path-based-config.js +9 -0
- package/dist/types/path-based-config.js.map +1 -0
- package/dist/utils/CanvasConverter.d.ts +118 -0
- package/dist/utils/CanvasConverter.d.ts.map +1 -0
- package/dist/utils/CanvasConverter.js +315 -0
- package/dist/utils/CanvasConverter.js.map +1 -0
- package/dist/utils/GraphConverter.d.ts +18 -0
- package/dist/utils/GraphConverter.d.ts.map +1 -0
- package/dist/utils/GraphConverter.js +61 -0
- package/dist/utils/GraphConverter.js.map +1 -0
- package/dist/utils/LibraryConverter.d.ts +113 -0
- package/dist/utils/LibraryConverter.d.ts.map +1 -0
- package/dist/utils/LibraryConverter.js +166 -0
- package/dist/utils/LibraryConverter.js.map +1 -0
- package/dist/utils/PathMatcher.d.ts +55 -0
- package/dist/utils/PathMatcher.d.ts.map +1 -0
- package/dist/utils/PathMatcher.js +172 -0
- package/dist/utils/PathMatcher.js.map +1 -0
- package/dist/utils/YamlParser.d.ts +36 -0
- package/dist/utils/YamlParser.d.ts.map +1 -0
- package/dist/utils/YamlParser.js +63 -0
- package/dist/utils/YamlParser.js.map +1 -0
- package/package.json +47 -0
- package/src/ConfigurationLoader.test.ts +490 -0
- package/src/ConfigurationLoader.ts +185 -0
- package/src/ConfigurationValidator.test.ts +200 -0
- package/src/ConfigurationValidator.ts +283 -0
- package/src/EventProcessor.test.ts +405 -0
- package/src/EventProcessor.ts +250 -0
- package/src/EventRecorderService.test.ts +541 -0
- package/src/EventRecorderService.ts +744 -0
- package/src/LibraryLoader.ts +215 -0
- package/src/PathBasedEventProcessor.test.ts +567 -0
- package/src/PathBasedEventProcessor.ts +332 -0
- package/src/SessionManager.test.ts +424 -0
- package/src/SessionManager.ts +470 -0
- package/src/ValidationEngine.test.ts +371 -0
- package/src/ValidationEngine.ts +196 -0
- package/src/helpers/GraphInstrumentationHelper.test.ts +340 -0
- package/src/helpers/GraphInstrumentationHelper.ts +326 -0
- package/src/index.ts +85 -0
- package/src/rules/config.test.ts +278 -0
- package/src/rules/config.ts +459 -0
- package/src/rules/engine.test.ts +332 -0
- package/src/rules/engine.ts +318 -0
- package/src/rules/implementations/connection-type-references.ts +117 -0
- package/src/rules/implementations/dead-end-states.ts +101 -0
- package/src/rules/implementations/index.ts +73 -0
- package/src/rules/implementations/library-node-type-match.ts +148 -0
- package/src/rules/implementations/minimum-node-sources.ts +82 -0
- package/src/rules/implementations/no-unknown-fields.ts +342 -0
- package/src/rules/implementations/orphaned-edge-types.ts +55 -0
- package/src/rules/implementations/orphaned-node-types.ts +58 -0
- package/src/rules/implementations/required-metadata.ts +64 -0
- package/src/rules/implementations/state-transition-references.ts +151 -0
- package/src/rules/implementations/unreachable-states.ts +94 -0
- package/src/rules/implementations/valid-action-patterns.ts +136 -0
- package/src/rules/implementations/valid-color-format.ts +140 -0
- package/src/rules/implementations/valid-edge-types.ts +258 -0
- package/src/rules/implementations/valid-node-types.ts +189 -0
- package/src/rules/index.ts +95 -0
- package/src/rules/types.ts +426 -0
- package/src/types/canvas.ts +496 -0
- package/src/types/index.ts +382 -0
- package/src/types/library.ts +233 -0
- package/src/types/path-based-config.ts +281 -0
- package/src/utils/CanvasConverter.ts +431 -0
- package/src/utils/GraphConverter.test.ts +195 -0
- package/src/utils/GraphConverter.ts +71 -0
- package/src/utils/LibraryConverter.ts +245 -0
- package/src/utils/PathMatcher.test.ts +148 -0
- package/src/utils/PathMatcher.ts +183 -0
- package/src/utils/YamlParser.ts +75 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { GraphInstrumentationHelper } from './GraphInstrumentationHelper';
|
|
2
|
+
import type { GraphConfiguration, GraphEvent } from '../types';
|
|
3
|
+
|
|
4
|
+
describe('GraphInstrumentationHelper', () => {
|
|
5
|
+
let testConfig: GraphConfiguration;
|
|
6
|
+
let capturedEvents: GraphEvent[];
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
testConfig = {
|
|
10
|
+
metadata: {
|
|
11
|
+
name: 'Test System',
|
|
12
|
+
version: '1.0.0',
|
|
13
|
+
},
|
|
14
|
+
nodeTypes: {
|
|
15
|
+
user: {
|
|
16
|
+
shape: 'circle',
|
|
17
|
+
color: '#4CAF50',
|
|
18
|
+
dataSchema: {
|
|
19
|
+
userId: { type: 'string', required: true },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
server: {
|
|
23
|
+
shape: 'hexagon',
|
|
24
|
+
color: '#9C27B0',
|
|
25
|
+
dataSchema: {
|
|
26
|
+
uptime: { type: 'number' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
edgeTypes: {
|
|
31
|
+
connection: {
|
|
32
|
+
style: 'solid',
|
|
33
|
+
directed: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
allowedConnections: [{ from: 'user', to: 'server', via: 'connection' }],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
capturedEvents = [];
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('Event Emission', () => {
|
|
43
|
+
it('should emit events through callback', () => {
|
|
44
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
45
|
+
capturedEvents.push(event);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
|
|
49
|
+
|
|
50
|
+
expect(capturedEvents).toHaveLength(1);
|
|
51
|
+
expect(capturedEvents[0].type).toBe('node_created');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should allow setting event emitter after construction', () => {
|
|
55
|
+
const helper = new GraphInstrumentationHelper(testConfig);
|
|
56
|
+
|
|
57
|
+
helper.setEventEmitter((event) => {
|
|
58
|
+
capturedEvents.push(event);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
|
|
62
|
+
|
|
63
|
+
expect(capturedEvents).toHaveLength(1);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('Node Event Helpers', () => {
|
|
68
|
+
it('should emit node created event', () => {
|
|
69
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
70
|
+
capturedEvents.push(event);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
|
|
74
|
+
|
|
75
|
+
expect(event.category).toBe('node');
|
|
76
|
+
expect(event.operation).toBe('create');
|
|
77
|
+
expect(event.type).toBe('node_created');
|
|
78
|
+
expect(event.expected).toBe(true);
|
|
79
|
+
|
|
80
|
+
const payload = event.payload as any;
|
|
81
|
+
expect(payload.nodeId).toBe('user-1');
|
|
82
|
+
expect(payload.nodeType).toBe('user');
|
|
83
|
+
expect(payload.data.userId).toBe('alice');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should emit node created event with position', () => {
|
|
87
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
88
|
+
capturedEvents.push(event);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' }, {
|
|
92
|
+
position: { x: 100, y: 200 },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const payload = event.payload as any;
|
|
96
|
+
expect(payload.position).toEqual({ x: 100, y: 200 });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should emit node updated event', () => {
|
|
100
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
101
|
+
capturedEvents.push(event);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const event = helper.emitNodeUpdated('user-1', { status: 'online' });
|
|
105
|
+
|
|
106
|
+
expect(event.category).toBe('node');
|
|
107
|
+
expect(event.operation).toBe('update');
|
|
108
|
+
expect(event.type).toBe('node_updated');
|
|
109
|
+
|
|
110
|
+
const payload = event.payload as any;
|
|
111
|
+
expect(payload.nodeId).toBe('user-1');
|
|
112
|
+
expect(payload.data.status).toBe('online');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should emit node deleted event', () => {
|
|
116
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
117
|
+
capturedEvents.push(event);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const event = helper.emitNodeDeleted('user-1');
|
|
121
|
+
|
|
122
|
+
expect(event.category).toBe('node');
|
|
123
|
+
expect(event.operation).toBe('delete');
|
|
124
|
+
expect(event.type).toBe('node_deleted');
|
|
125
|
+
|
|
126
|
+
const payload = event.payload as any;
|
|
127
|
+
expect(payload.nodeId).toBe('user-1');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should mark events as unexpected when specified', () => {
|
|
131
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
132
|
+
capturedEvents.push(event);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' }, {
|
|
136
|
+
expected: false,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(event.expected).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('Edge Event Helpers', () => {
|
|
144
|
+
it('should emit edge created event', () => {
|
|
145
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
146
|
+
capturedEvents.push(event);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const event = helper.emitEdgeCreated('conn-1', 'connection', 'user-1', 'server-1');
|
|
150
|
+
|
|
151
|
+
expect(event.category).toBe('edge');
|
|
152
|
+
expect(event.operation).toBe('create');
|
|
153
|
+
expect(event.type).toBe('edge_created');
|
|
154
|
+
|
|
155
|
+
const payload = event.payload as any;
|
|
156
|
+
expect(payload.edgeId).toBe('conn-1');
|
|
157
|
+
expect(payload.edgeType).toBe('connection');
|
|
158
|
+
expect(payload.from).toBe('user-1');
|
|
159
|
+
expect(payload.to).toBe('server-1');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should emit edge created event with data', () => {
|
|
163
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
164
|
+
capturedEvents.push(event);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const event = helper.emitEdgeCreated('conn-1', 'connection', 'user-1', 'server-1', {
|
|
168
|
+
data: { bandwidth: '1gbps' },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const payload = event.payload as any;
|
|
172
|
+
expect(payload.data.bandwidth).toBe('1gbps');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should emit edge animated event', () => {
|
|
176
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
177
|
+
capturedEvents.push(event);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const event = helper.emitEdgeAnimated('msg-1', 'message', 'user-1', 'user-2', {
|
|
181
|
+
duration: 500,
|
|
182
|
+
direction: 'forward',
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(event.category).toBe('edge');
|
|
186
|
+
expect(event.operation).toBe('animate');
|
|
187
|
+
expect(event.type).toBe('edge_animated');
|
|
188
|
+
|
|
189
|
+
const payload = event.payload as any;
|
|
190
|
+
expect(payload.animation?.duration).toBe(500);
|
|
191
|
+
expect(payload.animation?.direction).toBe('forward');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should emit edge deleted event', () => {
|
|
195
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
196
|
+
capturedEvents.push(event);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const event = helper.emitEdgeDeleted('conn-1');
|
|
200
|
+
|
|
201
|
+
expect(event.category).toBe('edge');
|
|
202
|
+
expect(event.operation).toBe('delete');
|
|
203
|
+
expect(event.type).toBe('edge_deleted');
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('State Event Helpers', () => {
|
|
208
|
+
it('should emit state change event', () => {
|
|
209
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
210
|
+
capturedEvents.push(event);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const event = helper.emitStateChange('user-1', 'online');
|
|
214
|
+
|
|
215
|
+
expect(event.category).toBe('state');
|
|
216
|
+
expect(event.type).toBe('state_changed');
|
|
217
|
+
|
|
218
|
+
const payload = event.payload as any;
|
|
219
|
+
expect(payload.nodeId).toBe('user-1');
|
|
220
|
+
expect(payload.newState).toBe('online');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should emit state change with previous state', () => {
|
|
224
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
225
|
+
capturedEvents.push(event);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const event = helper.emitStateChange('user-1', 'online', {
|
|
229
|
+
previousState: 'offline',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const payload = event.payload as any;
|
|
233
|
+
expect(payload.previousState).toBe('offline');
|
|
234
|
+
expect(payload.newState).toBe('online');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should emit state change with additional data', () => {
|
|
238
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
239
|
+
capturedEvents.push(event);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const event = helper.emitStateChange('user-1', 'online', {
|
|
243
|
+
data: { lastSeen: Date.now() },
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const payload = event.payload as any;
|
|
247
|
+
expect(payload.data.lastSeen).toBeDefined();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('System Event Helpers', () => {
|
|
252
|
+
it('should emit reset event', () => {
|
|
253
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
254
|
+
capturedEvents.push(event);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const event = helper.emitReset();
|
|
258
|
+
|
|
259
|
+
expect(event.category).toBe('system');
|
|
260
|
+
expect(event.type).toBe('system_reset');
|
|
261
|
+
|
|
262
|
+
const payload = event.payload as any;
|
|
263
|
+
expect(payload.action).toBe('reset');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should emit pause event', () => {
|
|
267
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
268
|
+
capturedEvents.push(event);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const event = helper.emitPause();
|
|
272
|
+
|
|
273
|
+
expect(event.category).toBe('system');
|
|
274
|
+
expect(event.type).toBe('system_pause');
|
|
275
|
+
|
|
276
|
+
const payload = event.payload as any;
|
|
277
|
+
expect(payload.action).toBe('pause');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should emit resume event', () => {
|
|
281
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
282
|
+
capturedEvents.push(event);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const event = helper.emitResume();
|
|
286
|
+
|
|
287
|
+
expect(event.category).toBe('system');
|
|
288
|
+
expect(event.type).toBe('system_resume');
|
|
289
|
+
|
|
290
|
+
const payload = event.payload as any;
|
|
291
|
+
expect(payload.action).toBe('resume');
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('Event IDs', () => {
|
|
296
|
+
it('should generate unique event IDs', () => {
|
|
297
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
298
|
+
capturedEvents.push(event);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
|
|
302
|
+
helper.emitNodeCreated('user-2', 'user', { userId: 'bob' });
|
|
303
|
+
|
|
304
|
+
expect(capturedEvents[0].id).not.toBe(capturedEvents[1].id);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should include timestamp in event', () => {
|
|
308
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
309
|
+
capturedEvents.push(event);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const before = Date.now();
|
|
313
|
+
helper.emitNodeCreated('user-1', 'user', { userId: 'alice' });
|
|
314
|
+
const after = Date.now();
|
|
315
|
+
|
|
316
|
+
expect(capturedEvents[0].timestamp).toBeGreaterThanOrEqual(before);
|
|
317
|
+
expect(capturedEvents[0].timestamp).toBeLessThanOrEqual(after);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe('Metadata Support', () => {
|
|
322
|
+
it('should include metadata in events', () => {
|
|
323
|
+
const helper = new GraphInstrumentationHelper(testConfig, (event) => {
|
|
324
|
+
capturedEvents.push(event);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const event = helper.emitNodeCreated('user-1', 'user', { userId: 'alice' }, {
|
|
328
|
+
metadata: {
|
|
329
|
+
source: 'test:line:42',
|
|
330
|
+
tags: ['important', 'user-creation'],
|
|
331
|
+
description: 'Creating test user',
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
expect(event.metadata?.source).toBe('test:line:42');
|
|
336
|
+
expect(event.metadata?.tags).toEqual(['important', 'user-creation']);
|
|
337
|
+
expect(event.metadata?.description).toBe('Creating test user');
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
});
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import type { GraphConfiguration, GraphEvent, NodeEvent, EdgeEvent, StateEvent } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper class for instrumenting tests with graph events
|
|
5
|
+
* Makes it easy to emit events from tests
|
|
6
|
+
*/
|
|
7
|
+
export class GraphInstrumentationHelper {
|
|
8
|
+
private configuration: GraphConfiguration;
|
|
9
|
+
private eventEmitter?: (event: GraphEvent) => void;
|
|
10
|
+
private eventCounter = 0;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
configuration: GraphConfiguration,
|
|
14
|
+
eventEmitter?: (event: GraphEvent) => void
|
|
15
|
+
) {
|
|
16
|
+
this.configuration = configuration;
|
|
17
|
+
this.eventEmitter = eventEmitter;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set the event emitter callback
|
|
22
|
+
*/
|
|
23
|
+
setEventEmitter(emitter: (event: GraphEvent) => void): void {
|
|
24
|
+
this.eventEmitter = emitter;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Emit a node creation event
|
|
29
|
+
*/
|
|
30
|
+
emitNodeCreated(
|
|
31
|
+
id: string,
|
|
32
|
+
type: string,
|
|
33
|
+
data: Record<string, any>,
|
|
34
|
+
options: {
|
|
35
|
+
expected?: boolean;
|
|
36
|
+
position?: { x: number; y: number };
|
|
37
|
+
metadata?: Record<string, any>;
|
|
38
|
+
} = {}
|
|
39
|
+
): GraphEvent {
|
|
40
|
+
const event: GraphEvent = {
|
|
41
|
+
id: this.generateEventId(),
|
|
42
|
+
type: 'node_created',
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
category: 'node',
|
|
45
|
+
operation: 'create',
|
|
46
|
+
payload: {
|
|
47
|
+
operation: 'create',
|
|
48
|
+
nodeId: id,
|
|
49
|
+
nodeType: type,
|
|
50
|
+
data,
|
|
51
|
+
position: options.position,
|
|
52
|
+
} as NodeEvent,
|
|
53
|
+
expected: options.expected ?? true,
|
|
54
|
+
metadata: options.metadata,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
this.emit(event);
|
|
58
|
+
return event;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Emit a node update event
|
|
63
|
+
*/
|
|
64
|
+
emitNodeUpdated(
|
|
65
|
+
id: string,
|
|
66
|
+
updates: Record<string, any>,
|
|
67
|
+
options: { expected?: boolean; metadata?: Record<string, any> } = {}
|
|
68
|
+
): GraphEvent {
|
|
69
|
+
const event: GraphEvent = {
|
|
70
|
+
id: this.generateEventId(),
|
|
71
|
+
type: 'node_updated',
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
category: 'node',
|
|
74
|
+
operation: 'update',
|
|
75
|
+
payload: {
|
|
76
|
+
operation: 'update',
|
|
77
|
+
nodeId: id,
|
|
78
|
+
nodeType: '', // Will be determined from state
|
|
79
|
+
data: updates,
|
|
80
|
+
} as NodeEvent,
|
|
81
|
+
expected: options.expected ?? true,
|
|
82
|
+
metadata: options.metadata,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.emit(event);
|
|
86
|
+
return event;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Emit a node deletion event
|
|
91
|
+
*/
|
|
92
|
+
emitNodeDeleted(
|
|
93
|
+
id: string,
|
|
94
|
+
options: { expected?: boolean; metadata?: Record<string, any> } = {}
|
|
95
|
+
): GraphEvent {
|
|
96
|
+
const event: GraphEvent = {
|
|
97
|
+
id: this.generateEventId(),
|
|
98
|
+
type: 'node_deleted',
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
category: 'node',
|
|
101
|
+
operation: 'delete',
|
|
102
|
+
payload: {
|
|
103
|
+
operation: 'delete',
|
|
104
|
+
nodeId: id,
|
|
105
|
+
nodeType: '', // Will be determined from state
|
|
106
|
+
} as NodeEvent,
|
|
107
|
+
expected: options.expected ?? true,
|
|
108
|
+
metadata: options.metadata,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
this.emit(event);
|
|
112
|
+
return event;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Emit an edge creation event
|
|
117
|
+
*/
|
|
118
|
+
emitEdgeCreated(
|
|
119
|
+
id: string,
|
|
120
|
+
type: string,
|
|
121
|
+
from: string,
|
|
122
|
+
to: string,
|
|
123
|
+
options: {
|
|
124
|
+
expected?: boolean;
|
|
125
|
+
data?: Record<string, any>;
|
|
126
|
+
metadata?: Record<string, any>;
|
|
127
|
+
} = {}
|
|
128
|
+
): GraphEvent {
|
|
129
|
+
const event: GraphEvent = {
|
|
130
|
+
id: this.generateEventId(),
|
|
131
|
+
type: 'edge_created',
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
category: 'edge',
|
|
134
|
+
operation: 'create',
|
|
135
|
+
payload: {
|
|
136
|
+
operation: 'create',
|
|
137
|
+
edgeId: id,
|
|
138
|
+
edgeType: type,
|
|
139
|
+
from,
|
|
140
|
+
to,
|
|
141
|
+
data: options.data,
|
|
142
|
+
} as EdgeEvent,
|
|
143
|
+
expected: options.expected ?? true,
|
|
144
|
+
metadata: options.metadata,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
this.emit(event);
|
|
148
|
+
return event;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Emit an edge animation event
|
|
153
|
+
*/
|
|
154
|
+
emitEdgeAnimated(
|
|
155
|
+
id: string,
|
|
156
|
+
type: string,
|
|
157
|
+
from: string,
|
|
158
|
+
to: string,
|
|
159
|
+
options: {
|
|
160
|
+
duration?: number;
|
|
161
|
+
direction?: 'forward' | 'backward' | 'bidirectional';
|
|
162
|
+
metadata?: Record<string, any>;
|
|
163
|
+
} = {}
|
|
164
|
+
): GraphEvent {
|
|
165
|
+
const event: GraphEvent = {
|
|
166
|
+
id: this.generateEventId(),
|
|
167
|
+
type: 'edge_animated',
|
|
168
|
+
timestamp: Date.now(),
|
|
169
|
+
category: 'edge',
|
|
170
|
+
operation: 'animate',
|
|
171
|
+
payload: {
|
|
172
|
+
operation: 'animate',
|
|
173
|
+
edgeId: id,
|
|
174
|
+
edgeType: type,
|
|
175
|
+
from,
|
|
176
|
+
to,
|
|
177
|
+
animation: {
|
|
178
|
+
duration: options.duration,
|
|
179
|
+
direction: options.direction || 'forward',
|
|
180
|
+
},
|
|
181
|
+
} as EdgeEvent,
|
|
182
|
+
expected: true,
|
|
183
|
+
metadata: options.metadata,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
this.emit(event);
|
|
187
|
+
return event;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Emit an edge deletion event
|
|
192
|
+
*/
|
|
193
|
+
emitEdgeDeleted(
|
|
194
|
+
id: string,
|
|
195
|
+
options: { expected?: boolean; metadata?: Record<string, any> } = {}
|
|
196
|
+
): GraphEvent {
|
|
197
|
+
const event: GraphEvent = {
|
|
198
|
+
id: this.generateEventId(),
|
|
199
|
+
type: 'edge_deleted',
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
category: 'edge',
|
|
202
|
+
operation: 'delete',
|
|
203
|
+
payload: {
|
|
204
|
+
operation: 'delete',
|
|
205
|
+
edgeId: id,
|
|
206
|
+
edgeType: '', // Will be determined from state
|
|
207
|
+
from: '',
|
|
208
|
+
to: '',
|
|
209
|
+
} as EdgeEvent,
|
|
210
|
+
expected: options.expected ?? true,
|
|
211
|
+
metadata: options.metadata,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
this.emit(event);
|
|
215
|
+
return event;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Emit a state change event
|
|
220
|
+
*/
|
|
221
|
+
emitStateChange(
|
|
222
|
+
nodeId: string,
|
|
223
|
+
newState: string,
|
|
224
|
+
options: {
|
|
225
|
+
previousState?: string;
|
|
226
|
+
expected?: boolean;
|
|
227
|
+
data?: Record<string, any>;
|
|
228
|
+
metadata?: Record<string, any>;
|
|
229
|
+
} = {}
|
|
230
|
+
): GraphEvent {
|
|
231
|
+
const event: GraphEvent = {
|
|
232
|
+
id: this.generateEventId(),
|
|
233
|
+
type: 'state_changed',
|
|
234
|
+
timestamp: Date.now(),
|
|
235
|
+
category: 'state',
|
|
236
|
+
operation: 'update',
|
|
237
|
+
payload: {
|
|
238
|
+
nodeId,
|
|
239
|
+
previousState: options.previousState,
|
|
240
|
+
newState,
|
|
241
|
+
data: options.data,
|
|
242
|
+
} as StateEvent,
|
|
243
|
+
expected: options.expected ?? true,
|
|
244
|
+
metadata: options.metadata,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
this.emit(event);
|
|
248
|
+
return event;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Emit a system reset event
|
|
253
|
+
*/
|
|
254
|
+
emitReset(): GraphEvent {
|
|
255
|
+
const event: GraphEvent = {
|
|
256
|
+
id: this.generateEventId(),
|
|
257
|
+
type: 'system_reset',
|
|
258
|
+
timestamp: Date.now(),
|
|
259
|
+
category: 'system',
|
|
260
|
+
operation: 'update',
|
|
261
|
+
payload: {
|
|
262
|
+
action: 'reset',
|
|
263
|
+
},
|
|
264
|
+
expected: true,
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
this.emit(event);
|
|
268
|
+
return event;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Emit a system pause event
|
|
273
|
+
*/
|
|
274
|
+
emitPause(): GraphEvent {
|
|
275
|
+
const event: GraphEvent = {
|
|
276
|
+
id: this.generateEventId(),
|
|
277
|
+
type: 'system_pause',
|
|
278
|
+
timestamp: Date.now(),
|
|
279
|
+
category: 'system',
|
|
280
|
+
operation: 'update',
|
|
281
|
+
payload: {
|
|
282
|
+
action: 'pause',
|
|
283
|
+
},
|
|
284
|
+
expected: true,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
this.emit(event);
|
|
288
|
+
return event;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Emit a system resume event
|
|
293
|
+
*/
|
|
294
|
+
emitResume(): GraphEvent {
|
|
295
|
+
const event: GraphEvent = {
|
|
296
|
+
id: this.generateEventId(),
|
|
297
|
+
type: 'system_resume',
|
|
298
|
+
timestamp: Date.now(),
|
|
299
|
+
category: 'system',
|
|
300
|
+
operation: 'update',
|
|
301
|
+
payload: {
|
|
302
|
+
action: 'resume',
|
|
303
|
+
},
|
|
304
|
+
expected: true,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
this.emit(event);
|
|
308
|
+
return event;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Generate a unique event ID
|
|
313
|
+
*/
|
|
314
|
+
private generateEventId(): string {
|
|
315
|
+
return `evt-${++this.eventCounter}-${Date.now()}`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Emit an event
|
|
320
|
+
*/
|
|
321
|
+
private emit(event: GraphEvent): void {
|
|
322
|
+
if (this.eventEmitter) {
|
|
323
|
+
this.eventEmitter(event);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|