@principles/core 1.240.1 → 1.240.3
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/dist/runtime-v2/__tests__/bridge-result-shaper.test.d.ts +26 -0
- package/dist/runtime-v2/__tests__/bridge-result-shaper.test.d.ts.map +1 -0
- package/dist/runtime-v2/__tests__/bridge-result-shaper.test.js +402 -0
- package/dist/runtime-v2/__tests__/bridge-result-shaper.test.js.map +1 -0
- package/dist/runtime-v2/__tests__/pruning-mask.test.d.ts +25 -0
- package/dist/runtime-v2/__tests__/pruning-mask.test.d.ts.map +1 -0
- package/dist/runtime-v2/__tests__/pruning-mask.test.js +281 -0
- package/dist/runtime-v2/__tests__/pruning-mask.test.js.map +1 -0
- package/dist/runtime-v2/__tests__/workflow-funnel-loader.test.d.ts +29 -0
- package/dist/runtime-v2/__tests__/workflow-funnel-loader.test.d.ts.map +1 -0
- package/dist/runtime-v2/__tests__/workflow-funnel-loader.test.js +431 -0
- package/dist/runtime-v2/__tests__/workflow-funnel-loader.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge Result Shaper Tests — Core Package
|
|
3
|
+
*
|
|
4
|
+
* Direct unit tests for the pure shapeBridgeResult() function.
|
|
5
|
+
*
|
|
6
|
+
* This module centralizes the status decision tree for PainSignalBridge,
|
|
7
|
+
* used by both the fresh diagnosis path and the existing-task idempotent path.
|
|
8
|
+
*
|
|
9
|
+
* Tests verify every branch in the decision tree:
|
|
10
|
+
* - Fresh path: no candidates → failed
|
|
11
|
+
* - Fresh path: admitted but no ledger → failed
|
|
12
|
+
* - Fresh path: all gated → degraded
|
|
13
|
+
* - Fresh path: partial admission → degraded
|
|
14
|
+
* - Fresh path: success with seed failure note → degraded
|
|
15
|
+
* - Fresh path: clean success → succeeded
|
|
16
|
+
* - Existing path: no candidates → failed
|
|
17
|
+
* - Existing path: no ledger → failed
|
|
18
|
+
* - Existing path: clean success → succeeded
|
|
19
|
+
*
|
|
20
|
+
* ERR checklist:
|
|
21
|
+
* - ERR-007 / EP-02: single source for status decision (unit test proves all branches)
|
|
22
|
+
* - ERR-002 / EP-03: every degraded/failed branch carries a structured message
|
|
23
|
+
* - ERR-004 / ERR-008 / EP-07: lineage fields pass through untouched
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=bridge-result-shaper.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-result-shaper.test.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/bridge-result-shaper.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge Result Shaper Tests — Core Package
|
|
3
|
+
*
|
|
4
|
+
* Direct unit tests for the pure shapeBridgeResult() function.
|
|
5
|
+
*
|
|
6
|
+
* This module centralizes the status decision tree for PainSignalBridge,
|
|
7
|
+
* used by both the fresh diagnosis path and the existing-task idempotent path.
|
|
8
|
+
*
|
|
9
|
+
* Tests verify every branch in the decision tree:
|
|
10
|
+
* - Fresh path: no candidates → failed
|
|
11
|
+
* - Fresh path: admitted but no ledger → failed
|
|
12
|
+
* - Fresh path: all gated → degraded
|
|
13
|
+
* - Fresh path: partial admission → degraded
|
|
14
|
+
* - Fresh path: success with seed failure note → degraded
|
|
15
|
+
* - Fresh path: clean success → succeeded
|
|
16
|
+
* - Existing path: no candidates → failed
|
|
17
|
+
* - Existing path: no ledger → failed
|
|
18
|
+
* - Existing path: clean success → succeeded
|
|
19
|
+
*
|
|
20
|
+
* ERR checklist:
|
|
21
|
+
* - ERR-007 / EP-02: single source for status decision (unit test proves all branches)
|
|
22
|
+
* - ERR-002 / EP-03: every degraded/failed branch carries a structured message
|
|
23
|
+
* - ERR-004 / ERR-008 / EP-07: lineage fields pass through untouched
|
|
24
|
+
*/
|
|
25
|
+
import { describe, it, expect } from 'vitest';
|
|
26
|
+
import { shapeBridgeResult } from '../bridge-result-shaper.js';
|
|
27
|
+
const PAIN_ID = 'pain-test-001';
|
|
28
|
+
const TASK_ID = 'diagnosis_pain-test-001';
|
|
29
|
+
const RUN_ID = 'run-test-1';
|
|
30
|
+
const ARTIFACT_ID = 'artifact-test-1';
|
|
31
|
+
function makeAdmissionResult(candidateId, decision) {
|
|
32
|
+
return {
|
|
33
|
+
candidateId,
|
|
34
|
+
recommendationKind: 'principle',
|
|
35
|
+
admission: {
|
|
36
|
+
decision,
|
|
37
|
+
reason: decision === 'admitted' ? 'evidence_sufficient' : 'input_evidence_empty',
|
|
38
|
+
nextAction: decision === 'admitted' ? 'none' : 'collect_evidence_before_diagnosis',
|
|
39
|
+
evidenceStatus: 'automatic_hook',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
describe('shapeBridgeResult — fresh path', () => {
|
|
44
|
+
it('returns failed when no candidates produced', () => {
|
|
45
|
+
const input = {
|
|
46
|
+
path: 'fresh',
|
|
47
|
+
painId: PAIN_ID,
|
|
48
|
+
taskId: TASK_ID,
|
|
49
|
+
runId: RUN_ID,
|
|
50
|
+
candidateIds: [],
|
|
51
|
+
ledgerEntryIds: [],
|
|
52
|
+
admissionResults: [],
|
|
53
|
+
seedFailureNote: '',
|
|
54
|
+
autoIntakeEnabled: true,
|
|
55
|
+
};
|
|
56
|
+
const result = shapeBridgeResult(input);
|
|
57
|
+
expect(result.status).toBe('failed');
|
|
58
|
+
expect(result.painId).toBe(PAIN_ID);
|
|
59
|
+
expect(result.taskId).toBe(TASK_ID);
|
|
60
|
+
expect(result.runId).toBe(RUN_ID);
|
|
61
|
+
expect(result.candidateIds).toEqual([]);
|
|
62
|
+
expect(result.ledgerEntryIds).toEqual([]);
|
|
63
|
+
expect(result.admissionResults).toEqual([]);
|
|
64
|
+
expect(result.message).toBe('Diagnostician succeeded but produced no principle candidates');
|
|
65
|
+
});
|
|
66
|
+
it('returns failed when candidates admitted but intake produced no ledger entries', () => {
|
|
67
|
+
const admissionResults = [
|
|
68
|
+
makeAdmissionResult('cand-1', 'admitted'),
|
|
69
|
+
makeAdmissionResult('cand-2', 'admitted'),
|
|
70
|
+
];
|
|
71
|
+
const input = {
|
|
72
|
+
path: 'fresh',
|
|
73
|
+
painId: PAIN_ID,
|
|
74
|
+
taskId: TASK_ID,
|
|
75
|
+
runId: RUN_ID,
|
|
76
|
+
artifactId: ARTIFACT_ID,
|
|
77
|
+
candidateIds: ['cand-1', 'cand-2'],
|
|
78
|
+
ledgerEntryIds: [],
|
|
79
|
+
admissionResults,
|
|
80
|
+
seedFailureNote: '',
|
|
81
|
+
autoIntakeEnabled: true,
|
|
82
|
+
};
|
|
83
|
+
const result = shapeBridgeResult(input);
|
|
84
|
+
expect(result.status).toBe('failed');
|
|
85
|
+
expect(result.painId).toBe(PAIN_ID);
|
|
86
|
+
expect(result.artifactId).toBe(ARTIFACT_ID);
|
|
87
|
+
expect(result.candidateIds).toEqual(['cand-1', 'cand-2']);
|
|
88
|
+
expect(result.ledgerEntryIds).toEqual([]);
|
|
89
|
+
expect(result.message).toBe('Candidate intake did not produce a ledger entry');
|
|
90
|
+
});
|
|
91
|
+
it('does NOT fail on no-ledger when autoIntake is disabled', () => {
|
|
92
|
+
const admissionResults = [
|
|
93
|
+
makeAdmissionResult('cand-1', 'admitted'),
|
|
94
|
+
];
|
|
95
|
+
const input = {
|
|
96
|
+
path: 'fresh',
|
|
97
|
+
painId: PAIN_ID,
|
|
98
|
+
taskId: TASK_ID,
|
|
99
|
+
runId: RUN_ID,
|
|
100
|
+
artifactId: ARTIFACT_ID,
|
|
101
|
+
candidateIds: ['cand-1'],
|
|
102
|
+
ledgerEntryIds: [],
|
|
103
|
+
admissionResults,
|
|
104
|
+
seedFailureNote: '',
|
|
105
|
+
autoIntakeEnabled: false,
|
|
106
|
+
};
|
|
107
|
+
const result = shapeBridgeResult(input);
|
|
108
|
+
expect(result.status).toBe('succeeded');
|
|
109
|
+
expect(result.ledgerEntryIds).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
it('returns degraded when all candidates are gated (none admitted)', () => {
|
|
112
|
+
const admissionResults = [
|
|
113
|
+
makeAdmissionResult('cand-1', 'needs_evidence'),
|
|
114
|
+
makeAdmissionResult('cand-2', 'deferred'),
|
|
115
|
+
];
|
|
116
|
+
const input = {
|
|
117
|
+
path: 'fresh',
|
|
118
|
+
painId: PAIN_ID,
|
|
119
|
+
taskId: TASK_ID,
|
|
120
|
+
runId: RUN_ID,
|
|
121
|
+
artifactId: ARTIFACT_ID,
|
|
122
|
+
candidateIds: ['cand-1', 'cand-2'],
|
|
123
|
+
ledgerEntryIds: [],
|
|
124
|
+
admissionResults,
|
|
125
|
+
seedFailureNote: '',
|
|
126
|
+
autoIntakeEnabled: true,
|
|
127
|
+
};
|
|
128
|
+
const result = shapeBridgeResult(input);
|
|
129
|
+
expect(result.status).toBe('degraded');
|
|
130
|
+
expect(result.message).toContain('all_candidates_gated');
|
|
131
|
+
expect(result.message).toContain('cand-1=needs_evidence');
|
|
132
|
+
expect(result.message).toContain('cand-2=deferred');
|
|
133
|
+
});
|
|
134
|
+
it('returns degraded with seed failure note appended when all gated + seed failed', () => {
|
|
135
|
+
const admissionResults = [
|
|
136
|
+
makeAdmissionResult('cand-1', 'needs_evidence'),
|
|
137
|
+
];
|
|
138
|
+
const input = {
|
|
139
|
+
path: 'fresh',
|
|
140
|
+
painId: PAIN_ID,
|
|
141
|
+
taskId: TASK_ID,
|
|
142
|
+
runId: RUN_ID,
|
|
143
|
+
artifactId: ARTIFACT_ID,
|
|
144
|
+
candidateIds: ['cand-1'],
|
|
145
|
+
ledgerEntryIds: [],
|
|
146
|
+
admissionResults,
|
|
147
|
+
seedFailureNote: 'dreamer seed failed: timeout',
|
|
148
|
+
autoIntakeEnabled: true,
|
|
149
|
+
};
|
|
150
|
+
const result = shapeBridgeResult(input);
|
|
151
|
+
expect(result.status).toBe('degraded');
|
|
152
|
+
expect(result.message).toContain('all_candidates_gated');
|
|
153
|
+
expect(result.message).toContain('dreamer seed failed: timeout');
|
|
154
|
+
});
|
|
155
|
+
it('returns degraded when partial admission (some admitted, some gated)', () => {
|
|
156
|
+
const admissionResults = [
|
|
157
|
+
makeAdmissionResult('cand-1', 'admitted'),
|
|
158
|
+
makeAdmissionResult('cand-2', 'needs_evidence'),
|
|
159
|
+
makeAdmissionResult('cand-3', 'deferred'),
|
|
160
|
+
];
|
|
161
|
+
const input = {
|
|
162
|
+
path: 'fresh',
|
|
163
|
+
painId: PAIN_ID,
|
|
164
|
+
taskId: TASK_ID,
|
|
165
|
+
runId: RUN_ID,
|
|
166
|
+
artifactId: ARTIFACT_ID,
|
|
167
|
+
candidateIds: ['cand-1', 'cand-2', 'cand-3'],
|
|
168
|
+
ledgerEntryIds: ['ledger-1'],
|
|
169
|
+
admissionResults,
|
|
170
|
+
seedFailureNote: '',
|
|
171
|
+
autoIntakeEnabled: true,
|
|
172
|
+
};
|
|
173
|
+
const result = shapeBridgeResult(input);
|
|
174
|
+
expect(result.status).toBe('degraded');
|
|
175
|
+
expect(result.message).toContain('partial_admission');
|
|
176
|
+
expect(result.message).toContain('1_admitted_2_gated');
|
|
177
|
+
});
|
|
178
|
+
it('returns degraded with seed failure note on partial admission', () => {
|
|
179
|
+
const admissionResults = [
|
|
180
|
+
makeAdmissionResult('cand-1', 'admitted'),
|
|
181
|
+
makeAdmissionResult('cand-2', 'needs_evidence'),
|
|
182
|
+
];
|
|
183
|
+
const input = {
|
|
184
|
+
path: 'fresh',
|
|
185
|
+
painId: PAIN_ID,
|
|
186
|
+
taskId: TASK_ID,
|
|
187
|
+
runId: RUN_ID,
|
|
188
|
+
artifactId: ARTIFACT_ID,
|
|
189
|
+
candidateIds: ['cand-1', 'cand-2'],
|
|
190
|
+
ledgerEntryIds: ['ledger-1'],
|
|
191
|
+
admissionResults,
|
|
192
|
+
seedFailureNote: 'some seeds failed: rate limit',
|
|
193
|
+
autoIntakeEnabled: true,
|
|
194
|
+
};
|
|
195
|
+
const result = shapeBridgeResult(input);
|
|
196
|
+
expect(result.status).toBe('degraded');
|
|
197
|
+
expect(result.message).toContain('partial_admission');
|
|
198
|
+
expect(result.message).toContain('some seeds failed: rate limit');
|
|
199
|
+
});
|
|
200
|
+
it('returns succeeded when all candidates admitted and ledger produced', () => {
|
|
201
|
+
const admissionResults = [
|
|
202
|
+
makeAdmissionResult('cand-1', 'admitted'),
|
|
203
|
+
makeAdmissionResult('cand-2', 'admitted'),
|
|
204
|
+
];
|
|
205
|
+
const input = {
|
|
206
|
+
path: 'fresh',
|
|
207
|
+
painId: PAIN_ID,
|
|
208
|
+
taskId: TASK_ID,
|
|
209
|
+
runId: RUN_ID,
|
|
210
|
+
artifactId: ARTIFACT_ID,
|
|
211
|
+
candidateIds: ['cand-1', 'cand-2'],
|
|
212
|
+
ledgerEntryIds: ['ledger-1', 'ledger-2'],
|
|
213
|
+
admissionResults,
|
|
214
|
+
seedFailureNote: '',
|
|
215
|
+
autoIntakeEnabled: true,
|
|
216
|
+
};
|
|
217
|
+
const result = shapeBridgeResult(input);
|
|
218
|
+
expect(result.status).toBe('succeeded');
|
|
219
|
+
expect(result.painId).toBe(PAIN_ID);
|
|
220
|
+
expect(result.taskId).toBe(TASK_ID);
|
|
221
|
+
expect(result.runId).toBe(RUN_ID);
|
|
222
|
+
expect(result.artifactId).toBe(ARTIFACT_ID);
|
|
223
|
+
expect(result.candidateIds).toEqual(['cand-1', 'cand-2']);
|
|
224
|
+
expect(result.ledgerEntryIds).toEqual(['ledger-1', 'ledger-2']);
|
|
225
|
+
expect(result.admissionResults).toEqual(admissionResults);
|
|
226
|
+
expect(result.message).toBeUndefined();
|
|
227
|
+
});
|
|
228
|
+
it('returns degraded when seed failure note is present (even with full admission)', () => {
|
|
229
|
+
const admissionResults = [
|
|
230
|
+
makeAdmissionResult('cand-1', 'admitted'),
|
|
231
|
+
];
|
|
232
|
+
const input = {
|
|
233
|
+
path: 'fresh',
|
|
234
|
+
painId: PAIN_ID,
|
|
235
|
+
taskId: TASK_ID,
|
|
236
|
+
runId: RUN_ID,
|
|
237
|
+
artifactId: ARTIFACT_ID,
|
|
238
|
+
candidateIds: ['cand-1'],
|
|
239
|
+
ledgerEntryIds: ['ledger-1'],
|
|
240
|
+
admissionResults,
|
|
241
|
+
seedFailureNote: 'dreamer seed failed: LLM error',
|
|
242
|
+
autoIntakeEnabled: true,
|
|
243
|
+
};
|
|
244
|
+
const result = shapeBridgeResult(input);
|
|
245
|
+
expect(result.status).toBe('degraded');
|
|
246
|
+
expect(result.message).toBe('dreamer seed failed: LLM error');
|
|
247
|
+
});
|
|
248
|
+
it('passes through lineage fields untouched (fresh path)', () => {
|
|
249
|
+
const admissionResults = [makeAdmissionResult('cand-1', 'admitted')];
|
|
250
|
+
const input = {
|
|
251
|
+
path: 'fresh',
|
|
252
|
+
painId: PAIN_ID,
|
|
253
|
+
taskId: TASK_ID,
|
|
254
|
+
runId: RUN_ID,
|
|
255
|
+
artifactId: ARTIFACT_ID,
|
|
256
|
+
candidateIds: ['cand-1'],
|
|
257
|
+
ledgerEntryIds: ['ledger-1'],
|
|
258
|
+
admissionResults,
|
|
259
|
+
seedFailureNote: '',
|
|
260
|
+
autoIntakeEnabled: true,
|
|
261
|
+
};
|
|
262
|
+
const result = shapeBridgeResult(input);
|
|
263
|
+
expect(result.painId).toBe(PAIN_ID);
|
|
264
|
+
expect(result.taskId).toBe(TASK_ID);
|
|
265
|
+
expect(result.runId).toBe(RUN_ID);
|
|
266
|
+
expect(result.artifactId).toBe(ARTIFACT_ID);
|
|
267
|
+
expect(result.candidateIds).toBe(input.candidateIds);
|
|
268
|
+
expect(result.ledgerEntryIds).toBe(input.ledgerEntryIds);
|
|
269
|
+
expect(result.admissionResults).toBe(input.admissionResults);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
describe('shapeBridgeResult — existing path', () => {
|
|
273
|
+
it('returns failed when no candidates', () => {
|
|
274
|
+
const input = {
|
|
275
|
+
path: 'existing',
|
|
276
|
+
painId: PAIN_ID,
|
|
277
|
+
taskId: TASK_ID,
|
|
278
|
+
runId: RUN_ID,
|
|
279
|
+
candidateIds: [],
|
|
280
|
+
ledgerEntryIds: [],
|
|
281
|
+
autoIntakeEnabled: true,
|
|
282
|
+
};
|
|
283
|
+
const result = shapeBridgeResult(input);
|
|
284
|
+
expect(result.status).toBe('failed');
|
|
285
|
+
expect(result.message).toBe('Task has no principle candidates — treating as failed');
|
|
286
|
+
expect(result.admissionResults).toBeUndefined();
|
|
287
|
+
});
|
|
288
|
+
it('returns failed when candidates exist but no ledger entries (autoIntake enabled)', () => {
|
|
289
|
+
const input = {
|
|
290
|
+
path: 'existing',
|
|
291
|
+
painId: PAIN_ID,
|
|
292
|
+
taskId: TASK_ID,
|
|
293
|
+
runId: RUN_ID,
|
|
294
|
+
artifactId: ARTIFACT_ID,
|
|
295
|
+
candidateIds: ['cand-1', 'cand-2'],
|
|
296
|
+
ledgerEntryIds: [],
|
|
297
|
+
autoIntakeEnabled: true,
|
|
298
|
+
};
|
|
299
|
+
const result = shapeBridgeResult(input);
|
|
300
|
+
expect(result.status).toBe('failed');
|
|
301
|
+
expect(result.message).toBe('Candidate intake did not produce a ledger entry — treating as failed');
|
|
302
|
+
});
|
|
303
|
+
it('returns succeeded when candidates exist but no ledger (autoIntake disabled)', () => {
|
|
304
|
+
const input = {
|
|
305
|
+
path: 'existing',
|
|
306
|
+
painId: PAIN_ID,
|
|
307
|
+
taskId: TASK_ID,
|
|
308
|
+
runId: RUN_ID,
|
|
309
|
+
artifactId: ARTIFACT_ID,
|
|
310
|
+
candidateIds: ['cand-1'],
|
|
311
|
+
ledgerEntryIds: [],
|
|
312
|
+
autoIntakeEnabled: false,
|
|
313
|
+
};
|
|
314
|
+
const result = shapeBridgeResult(input);
|
|
315
|
+
expect(result.status).toBe('succeeded');
|
|
316
|
+
expect(result.message).toBe('Task already succeeded');
|
|
317
|
+
});
|
|
318
|
+
it('returns succeeded when candidates and ledger entries exist', () => {
|
|
319
|
+
const input = {
|
|
320
|
+
path: 'existing',
|
|
321
|
+
painId: PAIN_ID,
|
|
322
|
+
taskId: TASK_ID,
|
|
323
|
+
runId: RUN_ID,
|
|
324
|
+
artifactId: ARTIFACT_ID,
|
|
325
|
+
candidateIds: ['cand-1', 'cand-2'],
|
|
326
|
+
ledgerEntryIds: ['ledger-1', 'ledger-2'],
|
|
327
|
+
autoIntakeEnabled: true,
|
|
328
|
+
};
|
|
329
|
+
const result = shapeBridgeResult(input);
|
|
330
|
+
expect(result.status).toBe('succeeded');
|
|
331
|
+
expect(result.message).toBe('Task already succeeded');
|
|
332
|
+
expect(result.admissionResults).toBeUndefined();
|
|
333
|
+
});
|
|
334
|
+
it('passes through lineage fields untouched (existing path)', () => {
|
|
335
|
+
const input = {
|
|
336
|
+
path: 'existing',
|
|
337
|
+
painId: PAIN_ID,
|
|
338
|
+
taskId: TASK_ID,
|
|
339
|
+
runId: RUN_ID,
|
|
340
|
+
artifactId: ARTIFACT_ID,
|
|
341
|
+
candidateIds: ['cand-1'],
|
|
342
|
+
ledgerEntryIds: ['ledger-1'],
|
|
343
|
+
autoIntakeEnabled: true,
|
|
344
|
+
};
|
|
345
|
+
const result = shapeBridgeResult(input);
|
|
346
|
+
expect(result.painId).toBe(PAIN_ID);
|
|
347
|
+
expect(result.taskId).toBe(TASK_ID);
|
|
348
|
+
expect(result.runId).toBe(RUN_ID);
|
|
349
|
+
expect(result.artifactId).toBe(ARTIFACT_ID);
|
|
350
|
+
expect(result.candidateIds).toBe(input.candidateIds);
|
|
351
|
+
expect(result.ledgerEntryIds).toBe(input.ledgerEntryIds);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
describe('shapeBridgeResult — cross-path consistency', () => {
|
|
355
|
+
it('fresh and existing paths use different messages for same semantic state', () => {
|
|
356
|
+
const freshNoCandidates = shapeBridgeResult({
|
|
357
|
+
path: 'fresh',
|
|
358
|
+
painId: PAIN_ID,
|
|
359
|
+
taskId: TASK_ID,
|
|
360
|
+
candidateIds: [],
|
|
361
|
+
ledgerEntryIds: [],
|
|
362
|
+
admissionResults: [],
|
|
363
|
+
seedFailureNote: '',
|
|
364
|
+
autoIntakeEnabled: true,
|
|
365
|
+
});
|
|
366
|
+
const existingNoCandidates = shapeBridgeResult({
|
|
367
|
+
path: 'existing',
|
|
368
|
+
painId: PAIN_ID,
|
|
369
|
+
taskId: TASK_ID,
|
|
370
|
+
candidateIds: [],
|
|
371
|
+
ledgerEntryIds: [],
|
|
372
|
+
autoIntakeEnabled: true,
|
|
373
|
+
});
|
|
374
|
+
expect(freshNoCandidates.status).toBe('failed');
|
|
375
|
+
expect(existingNoCandidates.status).toBe('failed');
|
|
376
|
+
expect(freshNoCandidates.message).not.toBe(existingNoCandidates.message);
|
|
377
|
+
});
|
|
378
|
+
it('fresh path includes admissionResults, existing path does not', () => {
|
|
379
|
+
const admissionResults = [makeAdmissionResult('cand-1', 'admitted')];
|
|
380
|
+
const freshSuccess = shapeBridgeResult({
|
|
381
|
+
path: 'fresh',
|
|
382
|
+
painId: PAIN_ID,
|
|
383
|
+
taskId: TASK_ID,
|
|
384
|
+
candidateIds: ['cand-1'],
|
|
385
|
+
ledgerEntryIds: ['ledger-1'],
|
|
386
|
+
admissionResults,
|
|
387
|
+
seedFailureNote: '',
|
|
388
|
+
autoIntakeEnabled: true,
|
|
389
|
+
});
|
|
390
|
+
const existingSuccess = shapeBridgeResult({
|
|
391
|
+
path: 'existing',
|
|
392
|
+
painId: PAIN_ID,
|
|
393
|
+
taskId: TASK_ID,
|
|
394
|
+
candidateIds: ['cand-1'],
|
|
395
|
+
ledgerEntryIds: ['ledger-1'],
|
|
396
|
+
autoIntakeEnabled: true,
|
|
397
|
+
});
|
|
398
|
+
expect(freshSuccess.admissionResults).toBeDefined();
|
|
399
|
+
expect(existingSuccess.admissionResults).toBeUndefined();
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
//# sourceMappingURL=bridge-result-shaper.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-result-shaper.test.js","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/bridge-result-shaper.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAI/D,MAAM,OAAO,GAAG,eAAe,CAAC;AAChC,MAAM,OAAO,GAAG,yBAAyB,CAAC;AAC1C,MAAM,MAAM,GAAG,YAAY,CAAC;AAC5B,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAEtC,SAAS,mBAAmB,CAC1B,WAAmB,EACnB,QAAoD;IAEpD,OAAO;QACL,WAAW;QACX,kBAAkB,EAAE,WAAW;QAC/B,SAAS,EAAE;YACT,QAAQ;YACR,MAAM,EAAE,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,sBAAsB;YAChF,UAAU,EAAE,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,mCAAmC;YAClF,cAAc,EAAE,gBAAgB;SACjC;KACF,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE,EAAE;YACpB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;YACzC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC1C,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAClC,cAAc,EAAE,EAAE;YAClB,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC1C,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,EAAE;YAClB,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,KAAK;SACzB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,CAAC;YAC/C,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC1C,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAClC,cAAc,EAAE,EAAE;YAClB,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAChD,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,EAAE;YAClB,gBAAgB;YAChB,eAAe,EAAE,8BAA8B;YAC/C,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;YACzC,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,CAAC;YAC/C,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC1C,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YAC5C,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;YACzC,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAChD,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAClC,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,gBAAgB;YAChB,eAAe,EAAE,+BAA+B;YAChD,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;YACzC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC1C,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAClC,cAAc,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;YACxC,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,gBAAgB,GAAG;YACvB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC1C,CAAC;QAEF,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,gBAAgB;YAChB,eAAe,EAAE,gCAAgC;YACjD,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,gBAAgB,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;QACrE,MAAM,KAAK,GAAgC;YACzC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,KAAK,GAAmC;YAC5C,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,EAAE;YAClB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,KAAK,GAAmC;YAC5C,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAClC,cAAc,EAAE,EAAE;YAClB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,KAAK,GAAmC;YAC5C,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,EAAE;YAClB,iBAAiB,EAAE,KAAK;SACzB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,KAAK,GAAmC;YAC5C,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;YAClC,cAAc,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;YACxC,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,KAAK,GAAmC;YAC5C,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IAC1D,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;YAC1C,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE,EAAE;YACpB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QAEH,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;YAC7C,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,EAAE;YAClB,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,gBAAgB,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;QAErE,MAAM,YAAY,GAAG,iBAAiB,CAAC;YACrC,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,gBAAgB;YAChB,eAAe,EAAE,EAAE;YACnB,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,iBAAiB,CAAC;YACxC,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,OAAO;YACf,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,cAAc,EAAE,CAAC,UAAU,CAAC;YAC5B,iBAAiB,EAAE,IAAI;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pruning Mask Tests — Core Package
|
|
3
|
+
*
|
|
4
|
+
* Direct unit tests for the pruning mask module:
|
|
5
|
+
* - buildMaskedPrincipleSet() — pure LWW (Last Write Wins) function
|
|
6
|
+
* - getCachedMaskedPrincipleSet() — TTL-cached wrapper
|
|
7
|
+
* - clearPruningMaskCache() — cache invalidation
|
|
8
|
+
*
|
|
9
|
+
* Tests verify:
|
|
10
|
+
* - Empty/null input → empty set
|
|
11
|
+
* - Single review → correct masking
|
|
12
|
+
* - Multiple reviews for same principle → LWW semantics
|
|
13
|
+
* - Corrupt records → skipped silently
|
|
14
|
+
* - Archive-candidate → masked; keep/defer → not masked
|
|
15
|
+
* - TTL cache: returns cached value within TTL, re-reads after expiry
|
|
16
|
+
* - Cache keyed by workspaceDir
|
|
17
|
+
* - clearPruningMaskCache resets cache state
|
|
18
|
+
*
|
|
19
|
+
* ERR checklist:
|
|
20
|
+
* - ERR-002 / EP-03: corrupt records skipped (fail-safe)
|
|
21
|
+
* - ERR-007 / EP-02: single source for LWW decision
|
|
22
|
+
* - EP-07: cache keyed by workspaceDir prevents cross-workspace leakage
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=pruning-mask.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pruning-mask.test.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/pruning-mask.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pruning Mask Tests — Core Package
|
|
3
|
+
*
|
|
4
|
+
* Direct unit tests for the pruning mask module:
|
|
5
|
+
* - buildMaskedPrincipleSet() — pure LWW (Last Write Wins) function
|
|
6
|
+
* - getCachedMaskedPrincipleSet() — TTL-cached wrapper
|
|
7
|
+
* - clearPruningMaskCache() — cache invalidation
|
|
8
|
+
*
|
|
9
|
+
* Tests verify:
|
|
10
|
+
* - Empty/null input → empty set
|
|
11
|
+
* - Single review → correct masking
|
|
12
|
+
* - Multiple reviews for same principle → LWW semantics
|
|
13
|
+
* - Corrupt records → skipped silently
|
|
14
|
+
* - Archive-candidate → masked; keep/defer → not masked
|
|
15
|
+
* - TTL cache: returns cached value within TTL, re-reads after expiry
|
|
16
|
+
* - Cache keyed by workspaceDir
|
|
17
|
+
* - clearPruningMaskCache resets cache state
|
|
18
|
+
*
|
|
19
|
+
* ERR checklist:
|
|
20
|
+
* - ERR-002 / EP-03: corrupt records skipped (fail-safe)
|
|
21
|
+
* - ERR-007 / EP-02: single source for LWW decision
|
|
22
|
+
* - EP-07: cache keyed by workspaceDir prevents cross-workspace leakage
|
|
23
|
+
*/
|
|
24
|
+
/* eslint-disable @typescript-eslint/max-params */
|
|
25
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
26
|
+
import * as fs from 'fs';
|
|
27
|
+
import * as path from 'path';
|
|
28
|
+
import * as os from 'os';
|
|
29
|
+
import { buildMaskedPrincipleSet, getCachedMaskedPrincipleSet, clearPruningMaskCache, } from '../pruning-mask.js';
|
|
30
|
+
function makeReview(principleId, decision, reviewedAt, overrides = {}) {
|
|
31
|
+
return {
|
|
32
|
+
reviewId: `review-${principleId}-${Date.now()}`,
|
|
33
|
+
principleId,
|
|
34
|
+
decision,
|
|
35
|
+
note: 'test note',
|
|
36
|
+
reviewer: 'test-reviewer',
|
|
37
|
+
reviewedAt,
|
|
38
|
+
signalSnapshot: {
|
|
39
|
+
principleId,
|
|
40
|
+
status: 'active',
|
|
41
|
+
createdAt: reviewedAt,
|
|
42
|
+
updatedAt: reviewedAt,
|
|
43
|
+
derivedCandidateIds: [],
|
|
44
|
+
derivedPainCount: 0,
|
|
45
|
+
matchedCandidateCount: 0,
|
|
46
|
+
recentCandidateCount: 0,
|
|
47
|
+
orphanCandidateCount: 0,
|
|
48
|
+
ageDays: 1,
|
|
49
|
+
riskLevel: 'watch',
|
|
50
|
+
reasons: [],
|
|
51
|
+
},
|
|
52
|
+
...overrides,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
describe('buildMaskedPrincipleSet', () => {
|
|
56
|
+
it('returns empty set for null input', () => {
|
|
57
|
+
const result = buildMaskedPrincipleSet(null);
|
|
58
|
+
expect(result.size).toBe(0);
|
|
59
|
+
});
|
|
60
|
+
it('returns empty set for empty array', () => {
|
|
61
|
+
const result = buildMaskedPrincipleSet([]);
|
|
62
|
+
expect(result.size).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
it('masks principles with archive-candidate decision', () => {
|
|
65
|
+
const reviews = [
|
|
66
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
67
|
+
makeReview('p2', 'keep', '2026-07-01T00:00:00.000Z'),
|
|
68
|
+
makeReview('p3', 'defer', '2026-07-01T00:00:00.000Z'),
|
|
69
|
+
];
|
|
70
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
71
|
+
expect(result.size).toBe(1);
|
|
72
|
+
expect(result.has('p1')).toBe(true);
|
|
73
|
+
expect(result.has('p2')).toBe(false);
|
|
74
|
+
expect(result.has('p3')).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
it('applies LWW semantics: later review overrides earlier one', () => {
|
|
77
|
+
const reviews = [
|
|
78
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
79
|
+
makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z'),
|
|
80
|
+
];
|
|
81
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
82
|
+
expect(result.size).toBe(0);
|
|
83
|
+
expect(result.has('p1')).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
it('applies LWW semantics: later archive-candidate overrides earlier keep', () => {
|
|
86
|
+
const reviews = [
|
|
87
|
+
makeReview('p1', 'keep', '2026-07-01T00:00:00.000Z'),
|
|
88
|
+
makeReview('p1', 'archive-candidate', '2026-07-02T00:00:00.000Z'),
|
|
89
|
+
];
|
|
90
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
91
|
+
expect(result.size).toBe(1);
|
|
92
|
+
expect(result.has('p1')).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it('handles same timestamp: later array entry wins (stable ordering)', () => {
|
|
95
|
+
const reviews = [
|
|
96
|
+
makeReview('p1', 'keep', '2026-07-01T00:00:00.000Z'),
|
|
97
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
98
|
+
];
|
|
99
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
100
|
+
expect(result.has('p1')).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it('skips corrupt records with missing principleId', () => {
|
|
103
|
+
const reviews = [
|
|
104
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
105
|
+
{ ...makeReview('p2', 'archive-candidate', '2026-07-01T00:00:00.000Z'), principleId: '' },
|
|
106
|
+
{ ...makeReview('p3', 'archive-candidate', '2026-07-01T00:00:00.000Z'), principleId: undefined },
|
|
107
|
+
];
|
|
108
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
109
|
+
expect(result.size).toBe(1);
|
|
110
|
+
expect(result.has('p1')).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it('skips corrupt records with missing decision', () => {
|
|
113
|
+
const reviews = [
|
|
114
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
115
|
+
{ ...makeReview('p2', 'archive-candidate', '2026-07-01T00:00:00.000Z'), decision: '' },
|
|
116
|
+
{ ...makeReview('p3', 'archive-candidate', '2026-07-01T00:00:00.000Z'), decision: undefined },
|
|
117
|
+
];
|
|
118
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
119
|
+
expect(result.size).toBe(1);
|
|
120
|
+
expect(result.has('p1')).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
it('skips corrupt records with missing reviewedAt', () => {
|
|
123
|
+
const reviews = [
|
|
124
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
125
|
+
{ ...makeReview('p2', 'archive-candidate', '2026-07-01T00:00:00.000Z'), reviewedAt: '' },
|
|
126
|
+
{ ...makeReview('p3', 'archive-candidate', '2026-07-01T00:00:00.000Z'), reviewedAt: undefined },
|
|
127
|
+
];
|
|
128
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
129
|
+
expect(result.size).toBe(1);
|
|
130
|
+
expect(result.has('p1')).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
it('skips null/undefined entries in the array', () => {
|
|
133
|
+
const reviews = [
|
|
134
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
135
|
+
null,
|
|
136
|
+
undefined,
|
|
137
|
+
];
|
|
138
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
139
|
+
expect(result.size).toBe(1);
|
|
140
|
+
expect(result.has('p1')).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
it('handles multiple principles with mixed decisions', () => {
|
|
143
|
+
const reviews = [
|
|
144
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
145
|
+
makeReview('p2', 'keep', '2026-07-01T00:00:00.000Z'),
|
|
146
|
+
makeReview('p3', 'defer', '2026-07-01T00:00:00.000Z'),
|
|
147
|
+
makeReview('p4', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
148
|
+
makeReview('p2', 'archive-candidate', '2026-07-02T00:00:00.000Z'),
|
|
149
|
+
makeReview('p4', 'keep', '2026-07-03T00:00:00.000Z'),
|
|
150
|
+
];
|
|
151
|
+
const result = buildMaskedPrincipleSet(reviews);
|
|
152
|
+
expect(result.size).toBe(2);
|
|
153
|
+
expect(result.has('p1')).toBe(true);
|
|
154
|
+
expect(result.has('p2')).toBe(true);
|
|
155
|
+
expect(result.has('p3')).toBe(false);
|
|
156
|
+
expect(result.has('p4')).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
it('returns a new Set instance each call (no shared mutable state)', () => {
|
|
159
|
+
const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
|
|
160
|
+
const result1 = buildMaskedPrincipleSet(reviews);
|
|
161
|
+
const result2 = buildMaskedPrincipleSet(reviews);
|
|
162
|
+
expect(result1).not.toBe(result2);
|
|
163
|
+
result1.add('p999');
|
|
164
|
+
expect(result2.has('p999')).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
describe('getCachedMaskedPrincipleSet', () => {
|
|
168
|
+
let tmpDir;
|
|
169
|
+
let stateDir;
|
|
170
|
+
let logPath;
|
|
171
|
+
beforeEach(() => {
|
|
172
|
+
clearPruningMaskCache();
|
|
173
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pruning-mask-test-'));
|
|
174
|
+
stateDir = path.join(tmpDir, '.state');
|
|
175
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
176
|
+
logPath = path.join(stateDir, 'pruning_reviews.jsonl');
|
|
177
|
+
});
|
|
178
|
+
afterEach(() => {
|
|
179
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
180
|
+
});
|
|
181
|
+
function writeReviews(reviews) {
|
|
182
|
+
const lines = reviews.map(r => JSON.stringify(r));
|
|
183
|
+
fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf-8');
|
|
184
|
+
}
|
|
185
|
+
it('reads from file and returns correct mask on first call', () => {
|
|
186
|
+
const reviews = [
|
|
187
|
+
makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z'),
|
|
188
|
+
makeReview('p2', 'keep', '2026-07-01T00:00:00.000Z'),
|
|
189
|
+
];
|
|
190
|
+
writeReviews(reviews);
|
|
191
|
+
const result = getCachedMaskedPrincipleSet(tmpDir);
|
|
192
|
+
expect(result.size).toBe(1);
|
|
193
|
+
expect(result.has('p1')).toBe(true);
|
|
194
|
+
expect(result.has('p2')).toBe(false);
|
|
195
|
+
});
|
|
196
|
+
it('returns cached value within TTL (no re-read)', () => {
|
|
197
|
+
const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
|
|
198
|
+
writeReviews(reviews);
|
|
199
|
+
const result1 = getCachedMaskedPrincipleSet(tmpDir, 60_000);
|
|
200
|
+
const updatedReviews = [makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z')];
|
|
201
|
+
writeReviews(updatedReviews);
|
|
202
|
+
const result2 = getCachedMaskedPrincipleSet(tmpDir, 60_000);
|
|
203
|
+
expect(result2.has('p1')).toBe(true);
|
|
204
|
+
expect(result2).toBe(result1);
|
|
205
|
+
});
|
|
206
|
+
it('re-reads after TTL expires', () => {
|
|
207
|
+
vi.useFakeTimers();
|
|
208
|
+
const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
|
|
209
|
+
writeReviews(reviews);
|
|
210
|
+
const result1 = getCachedMaskedPrincipleSet(tmpDir, 1000);
|
|
211
|
+
expect(result1.has('p1')).toBe(true);
|
|
212
|
+
const updatedReviews = [makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z')];
|
|
213
|
+
writeReviews(updatedReviews);
|
|
214
|
+
vi.advanceTimersByTime(500);
|
|
215
|
+
const result2 = getCachedMaskedPrincipleSet(tmpDir, 1000);
|
|
216
|
+
expect(result2.has('p1')).toBe(true);
|
|
217
|
+
vi.advanceTimersByTime(600);
|
|
218
|
+
const result3 = getCachedMaskedPrincipleSet(tmpDir, 1000);
|
|
219
|
+
expect(result3.has('p1')).toBe(false);
|
|
220
|
+
vi.useRealTimers();
|
|
221
|
+
});
|
|
222
|
+
it('cache is keyed by workspaceDir', () => {
|
|
223
|
+
const otherDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pruning-mask-other-'));
|
|
224
|
+
const otherStateDir = path.join(otherDir, '.state');
|
|
225
|
+
fs.mkdirSync(otherStateDir, { recursive: true });
|
|
226
|
+
const otherLogPath = path.join(otherStateDir, 'pruning_reviews.jsonl');
|
|
227
|
+
try {
|
|
228
|
+
const reviews1 = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
|
|
229
|
+
writeReviews(reviews1);
|
|
230
|
+
const reviews2 = [makeReview('p1', 'keep', '2026-07-01T00:00:00.000Z')];
|
|
231
|
+
fs.writeFileSync(otherLogPath, reviews2.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf-8');
|
|
232
|
+
const result1 = getCachedMaskedPrincipleSet(tmpDir);
|
|
233
|
+
const result2 = getCachedMaskedPrincipleSet(otherDir);
|
|
234
|
+
expect(result1.has('p1')).toBe(true);
|
|
235
|
+
expect(result2.has('p1')).toBe(false);
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
fs.rmSync(otherDir, { recursive: true, force: true });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
it('returns empty set when no pruning_reviews.jsonl exists', () => {
|
|
242
|
+
fs.rmSync(logPath, { force: true });
|
|
243
|
+
const result = getCachedMaskedPrincipleSet(tmpDir);
|
|
244
|
+
expect(result.size).toBe(0);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
describe('clearPruningMaskCache', () => {
|
|
248
|
+
let tmpDir;
|
|
249
|
+
let stateDir;
|
|
250
|
+
let logPath;
|
|
251
|
+
beforeEach(() => {
|
|
252
|
+
clearPruningMaskCache();
|
|
253
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-pruning-mask-clear-'));
|
|
254
|
+
stateDir = path.join(tmpDir, '.state');
|
|
255
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
256
|
+
logPath = path.join(stateDir, 'pruning_reviews.jsonl');
|
|
257
|
+
});
|
|
258
|
+
afterEach(() => {
|
|
259
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
260
|
+
});
|
|
261
|
+
function writeReviews(reviews) {
|
|
262
|
+
const lines = reviews.map(r => JSON.stringify(r));
|
|
263
|
+
fs.writeFileSync(logPath, lines.join('\n') + '\n', 'utf-8');
|
|
264
|
+
}
|
|
265
|
+
it('clears cache so next call re-reads from disk', () => {
|
|
266
|
+
const reviews = [makeReview('p1', 'archive-candidate', '2026-07-01T00:00:00.000Z')];
|
|
267
|
+
writeReviews(reviews);
|
|
268
|
+
const result1 = getCachedMaskedPrincipleSet(tmpDir);
|
|
269
|
+
expect(result1.has('p1')).toBe(true);
|
|
270
|
+
const updatedReviews = [makeReview('p1', 'keep', '2026-07-02T00:00:00.000Z')];
|
|
271
|
+
writeReviews(updatedReviews);
|
|
272
|
+
clearPruningMaskCache();
|
|
273
|
+
const result2 = getCachedMaskedPrincipleSet(tmpDir);
|
|
274
|
+
expect(result2.has('p1')).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
it('is safe to call when cache is already empty', () => {
|
|
277
|
+
expect(() => clearPruningMaskCache()).not.toThrow();
|
|
278
|
+
expect(() => clearPruningMaskCache()).not.toThrow();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=pruning-mask.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pruning-mask.test.js","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/pruning-mask.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowFunnelLoader Tests — Core Package
|
|
3
|
+
*
|
|
4
|
+
* Direct unit tests for the WorkflowFunnelLoader class.
|
|
5
|
+
*
|
|
6
|
+
* Tests verify:
|
|
7
|
+
* - Constructor loads workflows.yaml from stateDir
|
|
8
|
+
* - Missing file → empty funnels + warning
|
|
9
|
+
* - Malformed YAML → preserves last known-good config + warning
|
|
10
|
+
* - Invalid schema (missing version/funnels) → preserves last known-good + warning
|
|
11
|
+
* - Invalid funnel entries (missing workflowId/stages) → skipped + warning
|
|
12
|
+
* - getStages returns correct stages for known workflow
|
|
13
|
+
* - getStages returns empty array for unknown workflow
|
|
14
|
+
* - getFunnel returns full funnel with policy for known workflow
|
|
15
|
+
* - getFunnel returns undefined for unknown workflow
|
|
16
|
+
* - getFunnel returns deep clone (mutations don't affect internal state)
|
|
17
|
+
* - getAllFunnels returns deep clone of all stages
|
|
18
|
+
* - getAllFunnelsWithPolicy returns deep clone of all funnels
|
|
19
|
+
* - watch() starts file watching, dispose() cleans up
|
|
20
|
+
* - getWarnings returns warnings from last load
|
|
21
|
+
* - getConfigPath returns correct path
|
|
22
|
+
*
|
|
23
|
+
* ERR checklist:
|
|
24
|
+
* - ERR-002 / EP-03: malformed YAML preserves last known-good (fail-safe)
|
|
25
|
+
* - ERR-007 / EP-02: single source for funnels table
|
|
26
|
+
* - EP-07: deep clones prevent consumer mutation of internal state
|
|
27
|
+
*/
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=workflow-funnel-loader.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-funnel-loader.test.d.ts","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/workflow-funnel-loader.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG"}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowFunnelLoader Tests — Core Package
|
|
3
|
+
*
|
|
4
|
+
* Direct unit tests for the WorkflowFunnelLoader class.
|
|
5
|
+
*
|
|
6
|
+
* Tests verify:
|
|
7
|
+
* - Constructor loads workflows.yaml from stateDir
|
|
8
|
+
* - Missing file → empty funnels + warning
|
|
9
|
+
* - Malformed YAML → preserves last known-good config + warning
|
|
10
|
+
* - Invalid schema (missing version/funnels) → preserves last known-good + warning
|
|
11
|
+
* - Invalid funnel entries (missing workflowId/stages) → skipped + warning
|
|
12
|
+
* - getStages returns correct stages for known workflow
|
|
13
|
+
* - getStages returns empty array for unknown workflow
|
|
14
|
+
* - getFunnel returns full funnel with policy for known workflow
|
|
15
|
+
* - getFunnel returns undefined for unknown workflow
|
|
16
|
+
* - getFunnel returns deep clone (mutations don't affect internal state)
|
|
17
|
+
* - getAllFunnels returns deep clone of all stages
|
|
18
|
+
* - getAllFunnelsWithPolicy returns deep clone of all funnels
|
|
19
|
+
* - watch() starts file watching, dispose() cleans up
|
|
20
|
+
* - getWarnings returns warnings from last load
|
|
21
|
+
* - getConfigPath returns correct path
|
|
22
|
+
*
|
|
23
|
+
* ERR checklist:
|
|
24
|
+
* - ERR-002 / EP-03: malformed YAML preserves last known-good (fail-safe)
|
|
25
|
+
* - ERR-007 / EP-02: single source for funnels table
|
|
26
|
+
* - EP-07: deep clones prevent consumer mutation of internal state
|
|
27
|
+
*/
|
|
28
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
29
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
30
|
+
import * as fs from 'fs';
|
|
31
|
+
import * as path from 'path';
|
|
32
|
+
import * as os from 'os';
|
|
33
|
+
import * as yamlLib from 'js-yaml';
|
|
34
|
+
import { WorkflowFunnelLoader } from '../../workflow-funnel-loader.js';
|
|
35
|
+
describe('WorkflowFunnelLoader', () => {
|
|
36
|
+
let tmpDir;
|
|
37
|
+
let stateDir;
|
|
38
|
+
let configPath;
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-workflow-loader-test-'));
|
|
41
|
+
stateDir = path.join(tmpDir, '.state');
|
|
42
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
43
|
+
configPath = path.join(stateDir, 'workflows.yaml');
|
|
44
|
+
});
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
47
|
+
});
|
|
48
|
+
function writeConfig(config) {
|
|
49
|
+
const yamlStr = yamlLib.dump(config, { schema: yamlLib.DEFAULT_SCHEMA });
|
|
50
|
+
fs.writeFileSync(configPath, yamlStr, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
describe('constructor and initial load', () => {
|
|
53
|
+
it('loads valid workflows.yaml on construction', () => {
|
|
54
|
+
writeConfig({
|
|
55
|
+
version: '1.0',
|
|
56
|
+
funnels: [
|
|
57
|
+
{
|
|
58
|
+
workflowId: 'test-funnel',
|
|
59
|
+
stages: [
|
|
60
|
+
{ name: 'stage1', eventType: 'created', eventCategory: 'created', statsField: 'test.created' },
|
|
61
|
+
{ name: 'stage2', eventType: 'completed', eventCategory: 'completed', statsField: 'test.completed' },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
67
|
+
const stages = loader.getStages('test-funnel');
|
|
68
|
+
expect(stages.length).toBe(2);
|
|
69
|
+
expect(stages[0].name).toBe('stage1');
|
|
70
|
+
expect(stages[1].name).toBe('stage2');
|
|
71
|
+
});
|
|
72
|
+
it('handles missing workflows.yaml with empty funnels + warning', () => {
|
|
73
|
+
fs.rmSync(configPath, { force: true });
|
|
74
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
75
|
+
expect(loader.getStages('any-workflow')).toEqual([]);
|
|
76
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
77
|
+
const warnings = loader.getWarnings();
|
|
78
|
+
expect(warnings.length).toBeGreaterThan(0);
|
|
79
|
+
expect(warnings[0]).toContain('not found');
|
|
80
|
+
});
|
|
81
|
+
it('handles malformed YAML with empty funnels + warning', () => {
|
|
82
|
+
fs.writeFileSync(configPath, 'not: valid: yaml: [broken', 'utf-8');
|
|
83
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
84
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
85
|
+
const warnings = loader.getWarnings();
|
|
86
|
+
expect(warnings.length).toBeGreaterThan(0);
|
|
87
|
+
expect(warnings[0]).toContain('Failed to parse');
|
|
88
|
+
});
|
|
89
|
+
it('handles missing version field with warning', () => {
|
|
90
|
+
fs.writeFileSync(configPath, 'funnels:\n - workflowId: test\n stages: []\n', 'utf-8');
|
|
91
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
92
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
93
|
+
const warnings = loader.getWarnings();
|
|
94
|
+
expect(warnings.some(w => w.includes('validation failed'))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
it('handles missing funnels array with warning', () => {
|
|
97
|
+
fs.writeFileSync(configPath, 'version: "1.0"\n', 'utf-8');
|
|
98
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
99
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
100
|
+
const warnings = loader.getWarnings();
|
|
101
|
+
expect(warnings.some(w => w.includes('validation failed'))).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('getStages', () => {
|
|
105
|
+
it('returns empty array for unknown workflow', () => {
|
|
106
|
+
writeConfig({
|
|
107
|
+
version: '1.0',
|
|
108
|
+
funnels: [
|
|
109
|
+
{
|
|
110
|
+
workflowId: 'known-funnel',
|
|
111
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
});
|
|
115
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
116
|
+
expect(loader.getStages('unknown-funnel')).toEqual([]);
|
|
117
|
+
});
|
|
118
|
+
it('returns stages in order for known workflow', () => {
|
|
119
|
+
writeConfig({
|
|
120
|
+
version: '1.0',
|
|
121
|
+
funnels: [
|
|
122
|
+
{
|
|
123
|
+
workflowId: 'ordered-funnel',
|
|
124
|
+
stages: [
|
|
125
|
+
{ name: 'first', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' },
|
|
126
|
+
{ name: 'second', eventType: 'e2', eventCategory: 'c2', statsField: 'f2' },
|
|
127
|
+
{ name: 'third', eventType: 'e3', eventCategory: 'c3', statsField: 'f3' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
133
|
+
const stages = loader.getStages('ordered-funnel');
|
|
134
|
+
expect(stages.map(s => s.name)).toEqual(['first', 'second', 'third']);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('getFunnel', () => {
|
|
138
|
+
it('returns undefined for unknown workflow', () => {
|
|
139
|
+
writeConfig({
|
|
140
|
+
version: '1.0',
|
|
141
|
+
funnels: [],
|
|
142
|
+
});
|
|
143
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
144
|
+
expect(loader.getFunnel('nonexistent')).toBeUndefined();
|
|
145
|
+
});
|
|
146
|
+
it('returns full funnel with policy for known workflow', () => {
|
|
147
|
+
writeConfig({
|
|
148
|
+
version: '1.0',
|
|
149
|
+
funnels: [
|
|
150
|
+
{
|
|
151
|
+
workflowId: 'policy-funnel',
|
|
152
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
153
|
+
policy: {
|
|
154
|
+
timeoutMs: 30000,
|
|
155
|
+
stageOrder: 'strict',
|
|
156
|
+
runtimeKind: 'pi-ai',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
162
|
+
const funnel = loader.getFunnel('policy-funnel');
|
|
163
|
+
expect(funnel).toBeDefined();
|
|
164
|
+
expect(funnel?.workflowId).toBe('policy-funnel');
|
|
165
|
+
expect(funnel?.stages.length).toBe(1);
|
|
166
|
+
expect(funnel?.policy?.timeoutMs).toBe(30000);
|
|
167
|
+
expect(funnel?.policy?.stageOrder).toBe('strict');
|
|
168
|
+
});
|
|
169
|
+
it('returns deep clone — mutations do not affect internal state', () => {
|
|
170
|
+
writeConfig({
|
|
171
|
+
version: '1.0',
|
|
172
|
+
funnels: [
|
|
173
|
+
{
|
|
174
|
+
workflowId: 'clone-test',
|
|
175
|
+
stages: [{ name: 'original', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
176
|
+
policy: { timeoutMs: 1000 },
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
181
|
+
const funnel1 = loader.getFunnel('clone-test');
|
|
182
|
+
funnel1.stages[0].name = 'mutated';
|
|
183
|
+
funnel1.policy.timeoutMs = 99999;
|
|
184
|
+
const funnel2 = loader.getFunnel('clone-test');
|
|
185
|
+
expect(funnel2.stages[0].name).toBe('original');
|
|
186
|
+
expect(funnel2.policy?.timeoutMs).toBe(1000);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('getAllFunnels', () => {
|
|
190
|
+
it('returns all funnels as a Map', () => {
|
|
191
|
+
writeConfig({
|
|
192
|
+
version: '1.0',
|
|
193
|
+
funnels: [
|
|
194
|
+
{
|
|
195
|
+
workflowId: 'funnel-a',
|
|
196
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
workflowId: 'funnel-b',
|
|
200
|
+
stages: [
|
|
201
|
+
{ name: 's2', eventType: 'e2', eventCategory: 'c2', statsField: 'f2' },
|
|
202
|
+
{ name: 's3', eventType: 'e3', eventCategory: 'c3', statsField: 'f3' },
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
208
|
+
const all = loader.getAllFunnels();
|
|
209
|
+
expect(all.size).toBe(2);
|
|
210
|
+
expect(all.get('funnel-a')?.length).toBe(1);
|
|
211
|
+
expect(all.get('funnel-b')?.length).toBe(2);
|
|
212
|
+
});
|
|
213
|
+
it('returns deep clone — mutations do not affect internal state', () => {
|
|
214
|
+
writeConfig({
|
|
215
|
+
version: '1.0',
|
|
216
|
+
funnels: [
|
|
217
|
+
{
|
|
218
|
+
workflowId: 'mutate-test',
|
|
219
|
+
stages: [{ name: 'original', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
});
|
|
223
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
224
|
+
const all1 = loader.getAllFunnels();
|
|
225
|
+
all1.get('mutate-test')[0].name = 'mutated';
|
|
226
|
+
const all2 = loader.getAllFunnels();
|
|
227
|
+
expect(all2.get('mutate-test')[0].name).toBe('original');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
describe('getAllFunnelsWithPolicy', () => {
|
|
231
|
+
it('returns all funnels with policy as a Map', () => {
|
|
232
|
+
writeConfig({
|
|
233
|
+
version: '1.0',
|
|
234
|
+
funnels: [
|
|
235
|
+
{
|
|
236
|
+
workflowId: 'funnel-x',
|
|
237
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
238
|
+
policy: { timeoutMs: 5000 },
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
});
|
|
242
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
243
|
+
const all = loader.getAllFunnelsWithPolicy();
|
|
244
|
+
expect(all.size).toBe(1);
|
|
245
|
+
const funnel = all.get('funnel-x');
|
|
246
|
+
expect(funnel.workflowId).toBe('funnel-x');
|
|
247
|
+
expect(funnel.policy?.timeoutMs).toBe(5000);
|
|
248
|
+
});
|
|
249
|
+
it('returns deep clone — policy mutations do not affect internal state', () => {
|
|
250
|
+
writeConfig({
|
|
251
|
+
version: '1.0',
|
|
252
|
+
funnels: [
|
|
253
|
+
{
|
|
254
|
+
workflowId: 'policy-clone',
|
|
255
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
256
|
+
policy: {
|
|
257
|
+
timeoutMs: 1000,
|
|
258
|
+
observability: {
|
|
259
|
+
enabled: true,
|
|
260
|
+
emitEvents: ['event1', 'event2'],
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
});
|
|
266
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
267
|
+
const all1 = loader.getAllFunnelsWithPolicy();
|
|
268
|
+
const funnel1 = all1.get('policy-clone');
|
|
269
|
+
funnel1.policy.timeoutMs = 99999;
|
|
270
|
+
funnel1.policy.observability.emitEvents.push('malicious');
|
|
271
|
+
const all2 = loader.getAllFunnelsWithPolicy();
|
|
272
|
+
const funnel2 = all2.get('policy-clone');
|
|
273
|
+
expect(funnel2.policy?.timeoutMs).toBe(1000);
|
|
274
|
+
expect(funnel2.policy?.observability?.emitEvents).toEqual(['event1', 'event2']);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
describe('load() reload', () => {
|
|
278
|
+
it('reloads from disk on explicit load() call', () => {
|
|
279
|
+
writeConfig({
|
|
280
|
+
version: '1.0',
|
|
281
|
+
funnels: [
|
|
282
|
+
{
|
|
283
|
+
workflowId: 'reload-test',
|
|
284
|
+
stages: [{ name: 'v1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
});
|
|
288
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
289
|
+
expect(loader.getStages('reload-test')[0].name).toBe('v1');
|
|
290
|
+
writeConfig({
|
|
291
|
+
version: '1.0',
|
|
292
|
+
funnels: [
|
|
293
|
+
{
|
|
294
|
+
workflowId: 'reload-test',
|
|
295
|
+
stages: [{ name: 'v2', eventType: 'e2', eventCategory: 'c2', statsField: 'f2' }],
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
});
|
|
299
|
+
loader.load();
|
|
300
|
+
expect(loader.getStages('reload-test')[0].name).toBe('v2');
|
|
301
|
+
});
|
|
302
|
+
it('preserves last known-good config on malformed reload', () => {
|
|
303
|
+
writeConfig({
|
|
304
|
+
version: '1.0',
|
|
305
|
+
funnels: [
|
|
306
|
+
{
|
|
307
|
+
workflowId: 'stable-funnel',
|
|
308
|
+
stages: [{ name: 'good', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
});
|
|
312
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
313
|
+
expect(loader.getStages('stable-funnel')[0].name).toBe('good');
|
|
314
|
+
fs.writeFileSync(configPath, '::: invalid yaml :::', 'utf-8');
|
|
315
|
+
loader.load();
|
|
316
|
+
expect(loader.getStages('stable-funnel')[0].name).toBe('good');
|
|
317
|
+
expect(loader.getWarnings().some(w => w.includes('Failed to parse'))).toBe(true);
|
|
318
|
+
});
|
|
319
|
+
it('clears funnels when file is deleted and reloaded', () => {
|
|
320
|
+
writeConfig({
|
|
321
|
+
version: '1.0',
|
|
322
|
+
funnels: [
|
|
323
|
+
{
|
|
324
|
+
workflowId: 'will-disappear',
|
|
325
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
});
|
|
329
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
330
|
+
expect(loader.getStages('will-disappear').length).toBe(1);
|
|
331
|
+
fs.rmSync(configPath);
|
|
332
|
+
loader.load();
|
|
333
|
+
expect(loader.getStages('will-disappear')).toEqual([]);
|
|
334
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
describe('invalid funnel entries', () => {
|
|
338
|
+
it('skips funnels missing workflowId', () => {
|
|
339
|
+
const yamlContent = `version: "1.0"\nfunnels:\n - stages:\n - name: s1\n eventType: e1\n eventCategory: c1\n statsField: f1\n`;
|
|
340
|
+
fs.writeFileSync(configPath, yamlContent, 'utf-8');
|
|
341
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
342
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
343
|
+
expect(loader.getWarnings().some(w => w.includes('Skipping invalid funnel'))).toBe(true);
|
|
344
|
+
});
|
|
345
|
+
it('skips funnels missing stages array', () => {
|
|
346
|
+
const yamlContent = `version: "1.0"\nfunnels:\n - workflowId: no-stages\n`;
|
|
347
|
+
fs.writeFileSync(configPath, yamlContent, 'utf-8');
|
|
348
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
349
|
+
expect(loader.getAllFunnels().size).toBe(0);
|
|
350
|
+
expect(loader.getWarnings().some(w => w.includes('Skipping invalid funnel'))).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
it('keeps valid funnels when some are invalid', () => {
|
|
353
|
+
const yamlContent = `version: "1.0"\nfunnels:\n - workflowId: valid-1\n stages:\n - name: s1\n eventType: e1\n eventCategory: c1\n statsField: f1\n - workflowId: valid-2\n stages:\n - name: s2\n eventType: e2\n eventCategory: c2\n statsField: f2\n - stages: []\n`;
|
|
354
|
+
fs.writeFileSync(configPath, yamlContent, 'utf-8');
|
|
355
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
356
|
+
expect(loader.getAllFunnels().size).toBe(2);
|
|
357
|
+
expect(loader.getStages('valid-1').length).toBe(1);
|
|
358
|
+
expect(loader.getStages('valid-2').length).toBe(1);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
describe('getWarnings', () => {
|
|
362
|
+
it('returns empty array for successful load', () => {
|
|
363
|
+
writeConfig({
|
|
364
|
+
version: '1.0',
|
|
365
|
+
funnels: [{
|
|
366
|
+
workflowId: 'clean',
|
|
367
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
368
|
+
}],
|
|
369
|
+
});
|
|
370
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
371
|
+
const warnings = loader.getWarnings();
|
|
372
|
+
expect(Array.isArray(warnings)).toBe(true);
|
|
373
|
+
expect(warnings).toEqual([]);
|
|
374
|
+
});
|
|
375
|
+
it('warnings are cleared and refreshed on each load', () => {
|
|
376
|
+
fs.writeFileSync(configPath, 'bad yaml [[[', 'utf-8');
|
|
377
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
378
|
+
const firstWarnings = loader.getWarnings();
|
|
379
|
+
expect(firstWarnings.length).toBeGreaterThan(0);
|
|
380
|
+
writeConfig({
|
|
381
|
+
version: '1.0',
|
|
382
|
+
funnels: [{
|
|
383
|
+
workflowId: 'clean',
|
|
384
|
+
stages: [{ name: 's1', eventType: 'e1', eventCategory: 'c1', statsField: 'f1' }],
|
|
385
|
+
}],
|
|
386
|
+
});
|
|
387
|
+
loader.load();
|
|
388
|
+
const secondWarnings = loader.getWarnings();
|
|
389
|
+
expect(secondWarnings).toEqual([]);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
describe('getConfigPath', () => {
|
|
393
|
+
it('returns the correct config path', () => {
|
|
394
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
395
|
+
expect(loader.getConfigPath()).toBe(configPath);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
describe('watch and dispose', () => {
|
|
399
|
+
it('watch() does not throw when file does not exist', () => {
|
|
400
|
+
fs.rmSync(configPath, { force: true });
|
|
401
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
402
|
+
expect(() => loader.watch()).not.toThrow();
|
|
403
|
+
loader.dispose();
|
|
404
|
+
});
|
|
405
|
+
it('dispose() is safe to call multiple times', () => {
|
|
406
|
+
writeConfig({
|
|
407
|
+
version: '1.0',
|
|
408
|
+
funnels: [],
|
|
409
|
+
});
|
|
410
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
411
|
+
loader.watch();
|
|
412
|
+
expect(() => loader.dispose()).not.toThrow();
|
|
413
|
+
expect(() => loader.dispose()).not.toThrow();
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
describe('stage-level fields', () => {
|
|
417
|
+
it('preserves optional stage fields: timeoutMs, successCriteria, legacyDisabled', () => {
|
|
418
|
+
const yamlContent = `version: "1.0"\nfunnels:\n - workflowId: stage-fields\n stages:\n - name: full-stage\n eventType: ev1\n eventCategory: cat1\n statsField: stats.ev1\n timeoutMs: 5000\n successCriteria: "count > 0"\n legacyDisabled: true\n observability:\n enabled: true\n emitEvents:\n - stage_started\n`;
|
|
419
|
+
fs.writeFileSync(configPath, yamlContent, 'utf-8');
|
|
420
|
+
const loader = new WorkflowFunnelLoader(stateDir);
|
|
421
|
+
const stages = loader.getStages('stage-fields');
|
|
422
|
+
expect(stages.length).toBe(1);
|
|
423
|
+
expect(stages[0].timeoutMs).toBe(5000);
|
|
424
|
+
expect(stages[0].successCriteria).toBe('count > 0');
|
|
425
|
+
expect(stages[0].legacyDisabled).toBe(true);
|
|
426
|
+
expect(stages[0].observability?.enabled).toBe(true);
|
|
427
|
+
expect(stages[0].observability?.emitEvents).toEqual(['stage_started']);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
//# sourceMappingURL=workflow-funnel-loader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-funnel-loader.test.js","sourceRoot":"","sources":["../../../src/runtime-v2/__tests__/workflow-funnel-loader.test.ts"],"names":[],"mappings":"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"}
|