@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,567 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { PathBasedEventProcessor, type LogEntry } from './PathBasedEventProcessor';
|
|
3
|
+
import type { PathBasedGraphConfiguration } from './types/path-based-config';
|
|
4
|
+
|
|
5
|
+
describe('PathBasedEventProcessor', () => {
|
|
6
|
+
// Sample configuration
|
|
7
|
+
const sampleConfig: PathBasedGraphConfiguration = {
|
|
8
|
+
metadata: {
|
|
9
|
+
name: 'Test System',
|
|
10
|
+
version: '1.0.0'
|
|
11
|
+
},
|
|
12
|
+
nodeTypes: {
|
|
13
|
+
'lock-manager': {
|
|
14
|
+
shape: 'rectangle',
|
|
15
|
+
icon: 'lock',
|
|
16
|
+
color: '#3b82f6',
|
|
17
|
+
dataSchema: {},
|
|
18
|
+
sources: ['lib/lock-manager.ts', 'lib/branch-aware-lock-manager.ts']
|
|
19
|
+
},
|
|
20
|
+
'github-api': {
|
|
21
|
+
shape: 'hexagon',
|
|
22
|
+
icon: 'github',
|
|
23
|
+
color: '#22c55e',
|
|
24
|
+
dataSchema: {},
|
|
25
|
+
sources: ['lib/github-api-client.ts', 'services/github/*.ts']
|
|
26
|
+
},
|
|
27
|
+
'request-handler': {
|
|
28
|
+
shape: 'rectangle',
|
|
29
|
+
icon: 'server',
|
|
30
|
+
color: '#f59e0b',
|
|
31
|
+
dataSchema: {},
|
|
32
|
+
sources: ['app/**/*.ts']
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
edgeTypes: {},
|
|
36
|
+
allowedConnections: []
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe('Milestone 1: Basic path-based association', () => {
|
|
40
|
+
it('should associate log with component by exact path match', () => {
|
|
41
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
42
|
+
|
|
43
|
+
const log: LogEntry = {
|
|
44
|
+
message: 'Lock acquired',
|
|
45
|
+
metadata: {
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
level: 'info',
|
|
48
|
+
source: {
|
|
49
|
+
file: 'lib/lock-manager.ts',
|
|
50
|
+
line: 42
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const events = processor.processLog(log);
|
|
56
|
+
|
|
57
|
+
expect(events).toHaveLength(1);
|
|
58
|
+
expect(events[0].type).toBe('component-activity');
|
|
59
|
+
const activityEvent = events[0] as any;
|
|
60
|
+
expect(activityEvent.componentId).toBe('lock-manager');
|
|
61
|
+
expect(activityEvent.level).toBe('info');
|
|
62
|
+
expect(activityEvent.message).toBe('Lock acquired');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should associate log with component by glob pattern match', () => {
|
|
66
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
67
|
+
|
|
68
|
+
const log: LogEntry = {
|
|
69
|
+
message: 'GitHub API call',
|
|
70
|
+
metadata: {
|
|
71
|
+
timestamp: Date.now(),
|
|
72
|
+
level: 'debug',
|
|
73
|
+
source: {
|
|
74
|
+
file: 'services/github/client.ts',
|
|
75
|
+
line: 10
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const events = processor.processLog(log);
|
|
81
|
+
|
|
82
|
+
expect(events).toHaveLength(1);
|
|
83
|
+
const activityEvent = events[0] as any;
|
|
84
|
+
expect(activityEvent.componentId).toBe('github-api');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should associate log with component using ** wildcard', () => {
|
|
88
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
89
|
+
|
|
90
|
+
const log: LogEntry = {
|
|
91
|
+
message: 'Request received',
|
|
92
|
+
metadata: {
|
|
93
|
+
timestamp: Date.now(),
|
|
94
|
+
level: 'info',
|
|
95
|
+
source: {
|
|
96
|
+
file: 'app/handlers/webhook.ts',
|
|
97
|
+
line: 5
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const events = processor.processLog(log);
|
|
103
|
+
|
|
104
|
+
expect(events).toHaveLength(1);
|
|
105
|
+
const activityEvent = events[0] as any;
|
|
106
|
+
expect(activityEvent.componentId).toBe('request-handler');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should return empty array for logs without source', () => {
|
|
110
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
111
|
+
|
|
112
|
+
const log: LogEntry = {
|
|
113
|
+
message: 'No source info',
|
|
114
|
+
metadata: {
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
level: 'info'
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const events = processor.processLog(log);
|
|
121
|
+
expect(events).toHaveLength(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should return empty array for logs that do not match any component', () => {
|
|
125
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
126
|
+
|
|
127
|
+
const log: LogEntry = {
|
|
128
|
+
message: 'Unmatched log',
|
|
129
|
+
metadata: {
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
level: 'info',
|
|
132
|
+
source: {
|
|
133
|
+
file: 'test/foo.ts',
|
|
134
|
+
line: 1
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const events = processor.processLog(log);
|
|
140
|
+
expect(events).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should preserve log level in activity event', () => {
|
|
144
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
145
|
+
|
|
146
|
+
const levels = ['debug', 'info', 'warn', 'error'] as const;
|
|
147
|
+
|
|
148
|
+
for (const level of levels) {
|
|
149
|
+
const log: LogEntry = {
|
|
150
|
+
message: `${level} message`,
|
|
151
|
+
metadata: {
|
|
152
|
+
timestamp: Date.now(),
|
|
153
|
+
level,
|
|
154
|
+
source: {
|
|
155
|
+
file: 'lib/lock-manager.ts',
|
|
156
|
+
line: 1
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const events = processor.processLog(log);
|
|
162
|
+
expect(events).toHaveLength(1);
|
|
163
|
+
const activityEvent = events[0] as any;
|
|
164
|
+
expect(activityEvent.level).toBe(level);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should batch process multiple logs', () => {
|
|
169
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
170
|
+
|
|
171
|
+
const logs: LogEntry[] = [
|
|
172
|
+
{
|
|
173
|
+
message: 'Log 1',
|
|
174
|
+
metadata: {
|
|
175
|
+
timestamp: Date.now(),
|
|
176
|
+
level: 'info',
|
|
177
|
+
source: { file: 'lib/lock-manager.ts' }
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
message: 'Log 2',
|
|
182
|
+
metadata: {
|
|
183
|
+
timestamp: Date.now(),
|
|
184
|
+
level: 'info',
|
|
185
|
+
source: { file: 'lib/github-api-client.ts' }
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
message: 'Log 3',
|
|
190
|
+
metadata: {
|
|
191
|
+
timestamp: Date.now(),
|
|
192
|
+
level: 'warn',
|
|
193
|
+
source: { file: 'app/main.ts' }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
const events = processor.processLogs(logs);
|
|
199
|
+
expect(events).toHaveLength(3);
|
|
200
|
+
expect((events[0] as any).componentId).toBe('lock-manager');
|
|
201
|
+
expect((events[1] as any).componentId).toBe('github-api');
|
|
202
|
+
expect((events[2] as any).componentId).toBe('request-handler');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('Milestone 2: Action pattern matching', () => {
|
|
207
|
+
const configWithActions: PathBasedGraphConfiguration = {
|
|
208
|
+
...sampleConfig,
|
|
209
|
+
pathBasedConfig: {
|
|
210
|
+
enableActionPatterns: true
|
|
211
|
+
},
|
|
212
|
+
nodeTypes: {
|
|
213
|
+
...sampleConfig.nodeTypes,
|
|
214
|
+
'lock-manager': {
|
|
215
|
+
...sampleConfig.nodeTypes['lock-manager'],
|
|
216
|
+
actions: [
|
|
217
|
+
{
|
|
218
|
+
pattern: 'Lock acquired for (?<lockId>\\S+)',
|
|
219
|
+
event: 'lock_acquired',
|
|
220
|
+
state: 'acquired',
|
|
221
|
+
metadata: {
|
|
222
|
+
lockId: '$lockId'
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
pattern: 'Lock released',
|
|
227
|
+
event: 'lock_released',
|
|
228
|
+
state: 'idle'
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
it('should match action pattern and extract metadata', () => {
|
|
236
|
+
const processor = new PathBasedEventProcessor(configWithActions);
|
|
237
|
+
|
|
238
|
+
const log: LogEntry = {
|
|
239
|
+
message: 'Lock acquired for branch-123',
|
|
240
|
+
metadata: {
|
|
241
|
+
timestamp: Date.now(),
|
|
242
|
+
level: 'info',
|
|
243
|
+
source: {
|
|
244
|
+
file: 'lib/lock-manager.ts',
|
|
245
|
+
line: 42
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const events = processor.processLog(log);
|
|
251
|
+
|
|
252
|
+
expect(events).toHaveLength(1);
|
|
253
|
+
expect(events[0].type).toBe('component-action');
|
|
254
|
+
const actionEvent = events[0] as any;
|
|
255
|
+
expect(actionEvent.componentId).toBe('lock-manager');
|
|
256
|
+
expect(actionEvent.action).toBe('lock_acquired');
|
|
257
|
+
expect(actionEvent.state).toBe('acquired');
|
|
258
|
+
expect(actionEvent.metadata).toEqual({ lockId: 'branch-123' });
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should fall back to activity event when no pattern matches', () => {
|
|
262
|
+
const processor = new PathBasedEventProcessor(configWithActions);
|
|
263
|
+
|
|
264
|
+
const log: LogEntry = {
|
|
265
|
+
message: 'Random log message',
|
|
266
|
+
metadata: {
|
|
267
|
+
timestamp: Date.now(),
|
|
268
|
+
level: 'info',
|
|
269
|
+
source: {
|
|
270
|
+
file: 'lib/lock-manager.ts',
|
|
271
|
+
line: 42
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const events = processor.processLog(log);
|
|
277
|
+
|
|
278
|
+
expect(events).toHaveLength(1);
|
|
279
|
+
expect(events[0].type).toBe('component-activity');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should match pattern without metadata extraction', () => {
|
|
283
|
+
const processor = new PathBasedEventProcessor(configWithActions);
|
|
284
|
+
|
|
285
|
+
const log: LogEntry = {
|
|
286
|
+
message: 'Lock released',
|
|
287
|
+
metadata: {
|
|
288
|
+
timestamp: Date.now(),
|
|
289
|
+
level: 'info',
|
|
290
|
+
source: {
|
|
291
|
+
file: 'lib/lock-manager.ts',
|
|
292
|
+
line: 50
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const events = processor.processLog(log);
|
|
298
|
+
|
|
299
|
+
expect(events).toHaveLength(1);
|
|
300
|
+
const actionEvent = events[0] as any;
|
|
301
|
+
expect(actionEvent.type).toBe('component-action');
|
|
302
|
+
expect(actionEvent.action).toBe('lock_released');
|
|
303
|
+
expect(actionEvent.state).toBe('idle');
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('Edge activation (Milestone 2)', () => {
|
|
308
|
+
const configWithEdges: PathBasedGraphConfiguration = {
|
|
309
|
+
...sampleConfig,
|
|
310
|
+
pathBasedConfig: {
|
|
311
|
+
enableActionPatterns: true
|
|
312
|
+
},
|
|
313
|
+
nodeTypes: {
|
|
314
|
+
...sampleConfig.nodeTypes,
|
|
315
|
+
'lock-manager': {
|
|
316
|
+
...sampleConfig.nodeTypes['lock-manager'],
|
|
317
|
+
actions: [
|
|
318
|
+
{
|
|
319
|
+
pattern: 'Lock acquired',
|
|
320
|
+
event: 'lock_acquired',
|
|
321
|
+
state: 'acquired'
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
edgeTypes: {
|
|
327
|
+
'lock-request': {
|
|
328
|
+
style: 'solid',
|
|
329
|
+
activatedBy: [
|
|
330
|
+
{
|
|
331
|
+
action: 'lock_acquired',
|
|
332
|
+
animation: 'flow',
|
|
333
|
+
direction: 'forward',
|
|
334
|
+
duration: 2000
|
|
335
|
+
}
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
it('should trigger edge animation when action matches', () => {
|
|
342
|
+
const processor = new PathBasedEventProcessor(configWithEdges);
|
|
343
|
+
|
|
344
|
+
const log: LogEntry = {
|
|
345
|
+
message: 'Lock acquired',
|
|
346
|
+
metadata: {
|
|
347
|
+
timestamp: Date.now(),
|
|
348
|
+
level: 'info',
|
|
349
|
+
source: {
|
|
350
|
+
file: 'lib/lock-manager.ts',
|
|
351
|
+
line: 42
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const events = processor.processLog(log);
|
|
357
|
+
|
|
358
|
+
// Should have both component action and edge animation
|
|
359
|
+
expect(events.length).toBeGreaterThanOrEqual(1);
|
|
360
|
+
|
|
361
|
+
const actionEvent = events.find(e => e.type === 'component-action');
|
|
362
|
+
expect(actionEvent).toBeDefined();
|
|
363
|
+
|
|
364
|
+
const edgeEvent = events.find(e => e.type === 'edge-animation') as any;
|
|
365
|
+
expect(edgeEvent).toBeDefined();
|
|
366
|
+
expect(edgeEvent?.edgeId).toBe('lock-request');
|
|
367
|
+
expect(edgeEvent?.animation).toBe('flow');
|
|
368
|
+
expect(edgeEvent?.direction).toBe('forward');
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('Validation', () => {
|
|
373
|
+
it('should detect overlapping source patterns', () => {
|
|
374
|
+
const configWithOverlap: PathBasedGraphConfiguration = {
|
|
375
|
+
...sampleConfig,
|
|
376
|
+
nodeTypes: {
|
|
377
|
+
'comp-a': {
|
|
378
|
+
shape: 'rectangle',
|
|
379
|
+
dataSchema: {},
|
|
380
|
+
sources: ['lib/*.ts']
|
|
381
|
+
},
|
|
382
|
+
'comp-b': {
|
|
383
|
+
shape: 'circle',
|
|
384
|
+
dataSchema: {},
|
|
385
|
+
sources: ['lib/*.ts'] // Same pattern!
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const processor = new PathBasedEventProcessor(configWithOverlap);
|
|
391
|
+
const issues = processor.validate();
|
|
392
|
+
|
|
393
|
+
expect(issues.length).toBeGreaterThan(0);
|
|
394
|
+
expect(issues[0].type).toBe('warning');
|
|
395
|
+
expect(issues[0].message).toContain('both use pattern');
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should detect invalid regex patterns', () => {
|
|
399
|
+
const configWithBadRegex: PathBasedGraphConfiguration = {
|
|
400
|
+
...sampleConfig,
|
|
401
|
+
nodeTypes: {
|
|
402
|
+
'lock-manager': {
|
|
403
|
+
...sampleConfig.nodeTypes['lock-manager'],
|
|
404
|
+
actions: [
|
|
405
|
+
{
|
|
406
|
+
pattern: '[invalid(regex', // Invalid regex
|
|
407
|
+
event: 'test'
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const processor = new PathBasedEventProcessor(configWithBadRegex);
|
|
415
|
+
const issues = processor.validate();
|
|
416
|
+
|
|
417
|
+
expect(issues.length).toBeGreaterThan(0);
|
|
418
|
+
expect(issues.some(i => i.type === 'error')).toBe(true);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe('Statistics', () => {
|
|
423
|
+
it('should return correct stats', () => {
|
|
424
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
425
|
+
const stats = processor.getStats();
|
|
426
|
+
|
|
427
|
+
expect(stats.totalComponents).toBe(3);
|
|
428
|
+
expect(stats.componentsWithSources).toBe(3);
|
|
429
|
+
expect(stats.totalSourcePatterns).toBe(5); // Sum of all source patterns
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
describe('Instance ID support', () => {
|
|
434
|
+
it('should include instanceId in activity event when provided', () => {
|
|
435
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
436
|
+
|
|
437
|
+
const log: LogEntry = {
|
|
438
|
+
message: 'Client connected',
|
|
439
|
+
metadata: {
|
|
440
|
+
timestamp: Date.now(),
|
|
441
|
+
level: 'info',
|
|
442
|
+
source: {
|
|
443
|
+
file: 'lib/lock-manager.ts',
|
|
444
|
+
line: 42
|
|
445
|
+
},
|
|
446
|
+
instanceId: 'client-1'
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const events = processor.processLog(log);
|
|
451
|
+
|
|
452
|
+
expect(events).toHaveLength(1);
|
|
453
|
+
expect(events[0].type).toBe('component-activity');
|
|
454
|
+
const activityEvent = events[0] as any;
|
|
455
|
+
expect(activityEvent.componentId).toBe('lock-manager');
|
|
456
|
+
expect(activityEvent.instanceId).toBe('client-1');
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should have undefined instanceId when not provided', () => {
|
|
460
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
461
|
+
|
|
462
|
+
const log: LogEntry = {
|
|
463
|
+
message: 'Client connected',
|
|
464
|
+
metadata: {
|
|
465
|
+
timestamp: Date.now(),
|
|
466
|
+
level: 'info',
|
|
467
|
+
source: {
|
|
468
|
+
file: 'lib/lock-manager.ts',
|
|
469
|
+
line: 42
|
|
470
|
+
}
|
|
471
|
+
// No instanceId
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const events = processor.processLog(log);
|
|
476
|
+
|
|
477
|
+
expect(events).toHaveLength(1);
|
|
478
|
+
const activityEvent = events[0] as any;
|
|
479
|
+
expect(activityEvent.instanceId).toBeUndefined();
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('should include instanceId in action event when provided', () => {
|
|
483
|
+
const configWithActions: PathBasedGraphConfiguration = {
|
|
484
|
+
...sampleConfig,
|
|
485
|
+
pathBasedConfig: {
|
|
486
|
+
enableActionPatterns: true
|
|
487
|
+
},
|
|
488
|
+
nodeTypes: {
|
|
489
|
+
...sampleConfig.nodeTypes,
|
|
490
|
+
'lock-manager': {
|
|
491
|
+
...sampleConfig.nodeTypes['lock-manager'],
|
|
492
|
+
actions: [
|
|
493
|
+
{
|
|
494
|
+
pattern: 'Lock acquired',
|
|
495
|
+
event: 'lock_acquired',
|
|
496
|
+
state: 'acquired'
|
|
497
|
+
}
|
|
498
|
+
]
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const processor = new PathBasedEventProcessor(configWithActions);
|
|
504
|
+
|
|
505
|
+
const log: LogEntry = {
|
|
506
|
+
message: 'Lock acquired',
|
|
507
|
+
metadata: {
|
|
508
|
+
timestamp: Date.now(),
|
|
509
|
+
level: 'info',
|
|
510
|
+
source: {
|
|
511
|
+
file: 'lib/lock-manager.ts',
|
|
512
|
+
line: 42
|
|
513
|
+
},
|
|
514
|
+
instanceId: 'lock-manager-primary'
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const events = processor.processLog(log);
|
|
519
|
+
|
|
520
|
+
expect(events).toHaveLength(1);
|
|
521
|
+
expect(events[0].type).toBe('component-action');
|
|
522
|
+
const actionEvent = events[0] as any;
|
|
523
|
+
expect(actionEvent.componentId).toBe('lock-manager');
|
|
524
|
+
expect(actionEvent.instanceId).toBe('lock-manager-primary');
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('should preserve instanceId across batch processing', () => {
|
|
528
|
+
const processor = new PathBasedEventProcessor(sampleConfig);
|
|
529
|
+
|
|
530
|
+
const logs: LogEntry[] = [
|
|
531
|
+
{
|
|
532
|
+
message: 'Log from client 1',
|
|
533
|
+
metadata: {
|
|
534
|
+
timestamp: Date.now(),
|
|
535
|
+
level: 'info',
|
|
536
|
+
source: { file: 'lib/lock-manager.ts' },
|
|
537
|
+
instanceId: 'client-1'
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
message: 'Log from client 2',
|
|
542
|
+
metadata: {
|
|
543
|
+
timestamp: Date.now(),
|
|
544
|
+
level: 'info',
|
|
545
|
+
source: { file: 'lib/lock-manager.ts' },
|
|
546
|
+
instanceId: 'client-2'
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
message: 'Log without instance',
|
|
551
|
+
metadata: {
|
|
552
|
+
timestamp: Date.now(),
|
|
553
|
+
level: 'info',
|
|
554
|
+
source: { file: 'lib/lock-manager.ts' }
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
];
|
|
558
|
+
|
|
559
|
+
const events = processor.processLogs(logs);
|
|
560
|
+
|
|
561
|
+
expect(events).toHaveLength(3);
|
|
562
|
+
expect((events[0] as any).instanceId).toBe('client-1');
|
|
563
|
+
expect((events[1] as any).instanceId).toBe('client-2');
|
|
564
|
+
expect((events[2] as any).instanceId).toBeUndefined();
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
});
|