@aj-archipelago/cortex 1.3.63 → 1.3.65

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.
@@ -0,0 +1,768 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+ import PathwayManager from '../../../lib/pathwayManager.js';
4
+ import { Prompt } from '../../../server/prompt.js';
5
+
6
+ // Mock config
7
+ const mockConfig = {
8
+ storageType: 'local',
9
+ filePath: '/tmp/test-pathways.json',
10
+ publishKey: 'test-key'
11
+ };
12
+
13
+ test.beforeEach(t => {
14
+ t.context.pathwayManager = new PathwayManager(mockConfig);
15
+ });
16
+
17
+ test('transformPrompts handles array of strings (legacy format)', async t => {
18
+ const pathway = {
19
+ prompt: [
20
+ 'First prompt text',
21
+ 'Second prompt text'
22
+ ],
23
+ systemPrompt: 'You are a helpful assistant'
24
+ };
25
+
26
+ const result = await t.context.pathwayManager.transformPrompts(pathway);
27
+
28
+ t.is(result.prompt.length, 2);
29
+ t.true(result.prompt[0] instanceof Prompt);
30
+ t.true(result.prompt[1] instanceof Prompt);
31
+
32
+ // Check that the prompt text is correctly embedded
33
+ t.is(result.prompt[0].messages[1].content, '{{text}}\n\nFirst prompt text');
34
+ t.is(result.prompt[1].messages[1].content, '{{text}}\n\nSecond prompt text');
35
+
36
+ // Check system prompt is included
37
+ t.is(result.prompt[0].messages[0].content, 'You are a helpful assistant');
38
+ t.is(result.prompt[1].messages[0].content, 'You are a helpful assistant');
39
+
40
+ // Names should be null for legacy format
41
+ t.is(result.prompt[0].name, null);
42
+ t.is(result.prompt[1].name, null);
43
+ });
44
+
45
+ test('transformPrompts handles array of objects (new format)', async t => {
46
+ const pathway = {
47
+ prompt: [
48
+ { name: 'First Prompt', prompt: 'First prompt text' },
49
+ { name: 'Second Prompt', prompt: 'Second prompt text' }
50
+ ],
51
+ systemPrompt: 'You are a helpful assistant'
52
+ };
53
+
54
+ const result = await t.context.pathwayManager.transformPrompts(pathway);
55
+
56
+ t.is(result.prompt.length, 2);
57
+ t.true(result.prompt[0] instanceof Prompt);
58
+ t.true(result.prompt[1] instanceof Prompt);
59
+
60
+ // Check that the prompt text is correctly embedded
61
+ t.is(result.prompt[0].messages[1].content, '{{text}}\n\nFirst prompt text');
62
+ t.is(result.prompt[1].messages[1].content, '{{text}}\n\nSecond prompt text');
63
+
64
+ // Check system prompt is included
65
+ t.is(result.prompt[0].messages[0].content, 'You are a helpful assistant');
66
+ t.is(result.prompt[1].messages[0].content, 'You are a helpful assistant');
67
+
68
+ // Names should be preserved for new format
69
+ t.is(result.prompt[0].name, 'First Prompt');
70
+ t.is(result.prompt[1].name, 'Second Prompt');
71
+ });
72
+
73
+ test('transformPrompts handles mixed format arrays', async t => {
74
+ const pathway = {
75
+ prompt: [
76
+ 'Legacy prompt text',
77
+ { name: 'Named Prompt', prompt: 'Named prompt text' }
78
+ ],
79
+ systemPrompt: 'You are a helpful assistant'
80
+ };
81
+
82
+ const result = await t.context.pathwayManager.transformPrompts(pathway);
83
+
84
+ t.is(result.prompt.length, 2);
85
+
86
+ // First prompt (legacy format)
87
+ t.is(result.prompt[0].messages[1].content, '{{text}}\n\nLegacy prompt text');
88
+ t.is(result.prompt[0].name, null);
89
+
90
+ // Second prompt (new format)
91
+ t.is(result.prompt[1].messages[1].content, '{{text}}\n\nNamed prompt text');
92
+ t.is(result.prompt[1].name, 'Named Prompt');
93
+ });
94
+
95
+ test('transformPrompts preserves other pathway properties', async t => {
96
+ const pathway = {
97
+ prompt: [{ name: 'Test', prompt: 'Test prompt' }],
98
+ systemPrompt: 'System prompt',
99
+ name: 'Test Pathway',
100
+ model: 'gpt-4',
101
+ otherProperty: 'value'
102
+ };
103
+
104
+ const result = await t.context.pathwayManager.transformPrompts(pathway);
105
+
106
+ t.is(result.name, 'Test Pathway');
107
+ t.is(result.model, 'gpt-4');
108
+ t.is(result.otherProperty, 'value');
109
+ t.is(result.systemPrompt, 'System prompt');
110
+ });
111
+
112
+ test('transformPrompts handles empty prompt array', async t => {
113
+ const pathway = {
114
+ prompt: [],
115
+ systemPrompt: 'You are a helpful assistant'
116
+ };
117
+
118
+ const result = await t.context.pathwayManager.transformPrompts(pathway);
119
+
120
+ t.is(result.prompt.length, 0);
121
+ t.true(Array.isArray(result.prompt));
122
+ });
123
+
124
+ test('_createPromptObject handles string prompt with default name', t => {
125
+ const promptItem = 'Test prompt text';
126
+ const systemPrompt = 'You are a helpful assistant';
127
+ const defaultName = 'test_prompt';
128
+
129
+ const result = t.context.pathwayManager._createPromptObject(promptItem, systemPrompt, defaultName);
130
+
131
+ t.true(result instanceof Prompt);
132
+ t.is(result.name, 'test_prompt');
133
+ t.is(result.messages.length, 2);
134
+ t.is(result.messages[0].role, 'system');
135
+ t.is(result.messages[0].content, 'You are a helpful assistant');
136
+ t.is(result.messages[1].role, 'user');
137
+ t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
138
+ });
139
+
140
+ test('_createPromptObject handles string prompt without default name', t => {
141
+ const promptItem = 'Test prompt text';
142
+ const systemPrompt = 'You are a helpful assistant';
143
+
144
+ const result = t.context.pathwayManager._createPromptObject(promptItem, systemPrompt);
145
+
146
+ t.true(result instanceof Prompt);
147
+ t.is(result.name, null);
148
+ t.is(result.messages.length, 2);
149
+ t.is(result.messages[0].role, 'system');
150
+ t.is(result.messages[0].content, 'You are a helpful assistant');
151
+ t.is(result.messages[1].role, 'user');
152
+ t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
153
+ });
154
+
155
+ test('_createPromptObject handles object prompt with name', t => {
156
+ const promptItem = { name: 'Custom Prompt', prompt: 'Test prompt text' };
157
+ const systemPrompt = 'You are a helpful assistant';
158
+ const defaultName = 'fallback_name';
159
+
160
+ const result = t.context.pathwayManager._createPromptObject(promptItem, systemPrompt, defaultName);
161
+
162
+ t.true(result instanceof Prompt);
163
+ t.is(result.name, 'Custom Prompt');
164
+ t.is(result.messages.length, 2);
165
+ t.is(result.messages[0].role, 'system');
166
+ t.is(result.messages[0].content, 'You are a helpful assistant');
167
+ t.is(result.messages[1].role, 'user');
168
+ t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
169
+ });
170
+
171
+ test('_createPromptObject handles object prompt without name', t => {
172
+ const promptItem = { prompt: 'Test prompt text' };
173
+ const systemPrompt = 'You are a helpful assistant';
174
+ const defaultName = 'fallback_name';
175
+
176
+ const result = t.context.pathwayManager._createPromptObject(promptItem, systemPrompt, defaultName);
177
+
178
+ t.true(result instanceof Prompt);
179
+ t.is(result.name, 'fallback_name'); // Uses defaultName when promptItem.name is undefined
180
+ t.is(result.messages.length, 2);
181
+ t.is(result.messages[0].role, 'system');
182
+ t.is(result.messages[0].content, 'You are a helpful assistant');
183
+ t.is(result.messages[1].role, 'user');
184
+ t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
185
+ });
186
+
187
+ test('_createPromptObject handles empty system prompt', t => {
188
+ const promptItem = 'Test prompt text';
189
+ const systemPrompt = '';
190
+ const defaultName = 'test_prompt';
191
+
192
+ const result = t.context.pathwayManager._createPromptObject(promptItem, systemPrompt, defaultName);
193
+
194
+ t.true(result instanceof Prompt);
195
+ t.is(result.name, 'test_prompt');
196
+ t.is(result.messages[0].content, '');
197
+ t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
198
+ });
199
+
200
+ test('_createPromptObject handles null system prompt', t => {
201
+ const promptItem = 'Test prompt text';
202
+ const systemPrompt = null;
203
+ const defaultName = 'test_prompt';
204
+
205
+ const result = t.context.pathwayManager._createPromptObject(promptItem, systemPrompt, defaultName);
206
+
207
+ t.true(result instanceof Prompt);
208
+ t.is(result.name, 'test_prompt');
209
+ t.is(result.messages[0].content, '');
210
+ t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
211
+ });
212
+
213
+ test('putPathway requires userId and secret', async t => {
214
+ const pathway = { prompt: ['test'] };
215
+
216
+ // Missing both
217
+ await t.throwsAsync(
218
+ () => t.context.pathwayManager.putPathway('test', pathway),
219
+ { message: 'Both userId and secret are mandatory for adding or updating a pathway' }
220
+ );
221
+
222
+ // Missing secret
223
+ await t.throwsAsync(
224
+ () => t.context.pathwayManager.putPathway('test', pathway, 'user123'),
225
+ { message: 'Both userId and secret are mandatory for adding or updating a pathway' }
226
+ );
227
+
228
+ // Missing userId
229
+ await t.throwsAsync(
230
+ () => t.context.pathwayManager.putPathway('test', pathway, null, 'secret123'),
231
+ { message: 'Both userId and secret are mandatory for adding or updating a pathway' }
232
+ );
233
+ });
234
+
235
+ test('putPathway stores pathway with correct format', async t => {
236
+ // Mock the storage and loading methods
237
+ const mockPathways = {};
238
+ t.context.pathwayManager.pathways = mockPathways;
239
+ t.context.pathwayManager.getLatestPathways = sinon.stub().resolves(mockPathways);
240
+ t.context.pathwayManager.savePathways = sinon.stub().resolves();
241
+ t.context.pathwayManager.loadPathways = sinon.stub().resolves();
242
+
243
+ const pathway = {
244
+ prompt: [
245
+ { name: 'Test Prompt', prompt: 'Test prompt text' }
246
+ ],
247
+ systemPrompt: 'System prompt',
248
+ model: 'gpt-4'
249
+ };
250
+
251
+ const result = await t.context.pathwayManager.putPathway(
252
+ 'testPathway',
253
+ pathway,
254
+ 'user123',
255
+ 'secret123',
256
+ 'Test Pathway Display'
257
+ );
258
+
259
+ t.is(result, 'testPathway');
260
+ t.truthy(mockPathways['user123']);
261
+ t.truthy(mockPathways['user123']['testPathway']);
262
+
263
+ const storedPathway = mockPathways['user123']['testPathway'];
264
+ t.is(storedPathway.secret, 'secret123');
265
+ t.is(storedPathway.displayName, 'Test Pathway Display');
266
+ t.deepEqual(storedPathway.prompt, pathway.prompt);
267
+ t.is(storedPathway.systemPrompt, 'System prompt');
268
+ t.is(storedPathway.model, 'gpt-4');
269
+ });
270
+
271
+ test('getPathways returns array of pathways for each prompt (string format)', async t => {
272
+ const pathwayTemplate = {
273
+ prompt: [
274
+ 'First prompt text',
275
+ 'Second prompt text',
276
+ 'Third prompt text'
277
+ ],
278
+ systemPrompt: 'You are a helpful assistant',
279
+ model: 'gpt-4',
280
+ enableCache: true,
281
+ customProperty: 'test-value'
282
+ };
283
+
284
+ const result = await t.context.pathwayManager.getPathways(pathwayTemplate);
285
+
286
+ t.is(result.length, 3);
287
+
288
+ // Check each pathway has the correct structure
289
+ result.forEach((pathway, index) => {
290
+ t.is(pathway.systemPrompt, 'You are a helpful assistant');
291
+ t.is(pathway.model, 'gpt-4');
292
+ t.is(pathway.enableCache, true);
293
+ t.is(pathway.customProperty, 'test-value');
294
+ t.is(pathway.prompt.length, 1);
295
+ t.true(pathway.prompt[0] instanceof Prompt);
296
+
297
+ // Check the prompt content
298
+ const expectedContent = `{{text}}\n\n${pathwayTemplate.prompt[index]}`;
299
+ t.is(pathway.prompt[0].messages[1].content, expectedContent);
300
+ t.is(pathway.prompt[0].messages[0].content, 'You are a helpful assistant');
301
+ t.is(pathway.prompt[0].name, `prompt_${index}`);
302
+ });
303
+ });
304
+
305
+ test('getPathways returns array of pathways for each prompt (object format)', async t => {
306
+ const pathwayTemplate = {
307
+ prompt: [
308
+ { name: 'Grammar Check', prompt: 'Check the grammar of this text' },
309
+ { name: 'Tone Analysis', prompt: 'Analyze the tone of this text' },
310
+ { name: 'Summary', prompt: 'Summarize this text' }
311
+ ],
312
+ systemPrompt: 'You are an expert editor',
313
+ model: 'gpt-4-turbo'
314
+ };
315
+
316
+ const result = await t.context.pathwayManager.getPathways(pathwayTemplate);
317
+
318
+ t.is(result.length, 3);
319
+
320
+ // Check each pathway has the correct structure and names
321
+ const expectedNames = ['Grammar Check', 'Tone Analysis', 'Summary'];
322
+ const expectedPrompts = [
323
+ 'Check the grammar of this text',
324
+ 'Analyze the tone of this text',
325
+ 'Summarize this text'
326
+ ];
327
+
328
+ result.forEach((pathway, index) => {
329
+ t.is(pathway.systemPrompt, 'You are an expert editor');
330
+ t.is(pathway.model, 'gpt-4-turbo');
331
+ t.is(pathway.prompt.length, 1);
332
+ t.true(pathway.prompt[0] instanceof Prompt);
333
+
334
+ // Check the prompt content and name
335
+ const expectedContent = `{{text}}\n\n${expectedPrompts[index]}`;
336
+ t.is(pathway.prompt[0].messages[1].content, expectedContent);
337
+ t.is(pathway.prompt[0].messages[0].content, 'You are an expert editor');
338
+ t.is(pathway.prompt[0].name, expectedNames[index]);
339
+ });
340
+ });
341
+
342
+ test('getPathways handles mixed format prompt arrays', async t => {
343
+ const pathwayTemplate = {
344
+ prompt: [
345
+ 'Legacy string prompt',
346
+ { name: 'Named Prompt', prompt: 'Named prompt text' },
347
+ { prompt: 'Unnamed object prompt' }
348
+ ],
349
+ systemPrompt: 'Mixed format system'
350
+ };
351
+
352
+ const result = await t.context.pathwayManager.getPathways(pathwayTemplate);
353
+
354
+ t.is(result.length, 3);
355
+
356
+ // First pathway (string format)
357
+ t.is(result[0].prompt[0].messages[1].content, '{{text}}\n\nLegacy string prompt');
358
+ t.is(result[0].prompt[0].name, 'prompt_0');
359
+
360
+ // Second pathway (named object)
361
+ t.is(result[1].prompt[0].messages[1].content, '{{text}}\n\nNamed prompt text');
362
+ t.is(result[1].prompt[0].name, 'Named Prompt');
363
+
364
+ // Third pathway (unnamed object)
365
+ t.is(result[2].prompt[0].messages[1].content, '{{text}}\n\nUnnamed object prompt');
366
+ t.is(result[2].prompt[0].name, 'prompt_2');
367
+ });
368
+
369
+ test('getPathways throws error for non-array prompt', async t => {
370
+ const pathwayTemplate = {
371
+ prompt: 'This should be an array',
372
+ systemPrompt: 'Test system prompt'
373
+ };
374
+
375
+ await t.throwsAsync(
376
+ () => t.context.pathwayManager.getPathways(pathwayTemplate),
377
+ { message: 'pathwayTemplate.prompt must be an array' }
378
+ );
379
+ });
380
+
381
+ test('getPathways handles empty prompt array', async t => {
382
+ const pathwayTemplate = {
383
+ prompt: [],
384
+ systemPrompt: 'Empty array test'
385
+ };
386
+
387
+ const result = await t.context.pathwayManager.getPathways(pathwayTemplate);
388
+
389
+ t.is(result.length, 0);
390
+ t.true(Array.isArray(result));
391
+ });
392
+
393
+ test('getPathways preserves all template properties', async t => {
394
+ const pathwayTemplate = {
395
+ prompt: [
396
+ { name: 'Test Prompt', prompt: 'Test content' }
397
+ ],
398
+ systemPrompt: 'System test',
399
+ model: 'gpt-4',
400
+ enableCache: false,
401
+ inputParameters: { temperature: 0.7 },
402
+ customProperty: 'preserved',
403
+ displayName: 'Test Display Name'
404
+ };
405
+
406
+ const result = await t.context.pathwayManager.getPathways(pathwayTemplate);
407
+
408
+ t.is(result.length, 1);
409
+ const pathway = result[0];
410
+
411
+ t.is(pathway.systemPrompt, 'System test');
412
+ t.is(pathway.model, 'gpt-4');
413
+ t.is(pathway.enableCache, false);
414
+ t.deepEqual(pathway.inputParameters, { temperature: 0.7 });
415
+ t.is(pathway.customProperty, 'preserved');
416
+ t.is(pathway.displayName, 'Test Display Name');
417
+ });
418
+
419
+ test('getPathways filters by promptNames (object format)', async t => {
420
+ const pathwayTemplate = {
421
+ prompt: [
422
+ { name: 'Grammar Check', prompt: 'Check the grammar of this text' },
423
+ { name: 'Tone Analysis', prompt: 'Analyze the tone of this text' },
424
+ { name: 'Summary', prompt: 'Summarize this text' },
425
+ { name: 'Translation', prompt: 'Translate this text' }
426
+ ],
427
+ systemPrompt: 'You are an expert editor'
428
+ };
429
+
430
+ const result = await t.context.pathwayManager.getPathways(
431
+ pathwayTemplate,
432
+ ['Grammar Check', 'Summary']
433
+ );
434
+
435
+ t.is(result.length, 2);
436
+
437
+ // Check that only the requested prompts are included
438
+ t.is(result[0].prompt[0].name, 'Grammar Check');
439
+ t.is(result[0].prompt[0].messages[1].content, '{{text}}\n\nCheck the grammar of this text');
440
+
441
+ t.is(result[1].prompt[0].name, 'Summary');
442
+ t.is(result[1].prompt[0].messages[1].content, '{{text}}\n\nSummarize this text');
443
+
444
+ // Ensure no _originalPromptName property remains
445
+ t.false('_originalPromptName' in result[0]);
446
+ t.false('_originalPromptName' in result[1]);
447
+ });
448
+
449
+ test('getPathways filters by promptNames (string format)', async t => {
450
+ const pathwayTemplate = {
451
+ prompt: [
452
+ 'First prompt text',
453
+ 'Second prompt text',
454
+ 'Third prompt text'
455
+ ],
456
+ systemPrompt: 'You are a helpful assistant'
457
+ };
458
+
459
+ const result = await t.context.pathwayManager.getPathways(
460
+ pathwayTemplate,
461
+ ['prompt_0', 'prompt_2']
462
+ );
463
+
464
+ t.is(result.length, 2);
465
+
466
+ // Check that only the requested prompts are included
467
+ t.is(result[0].prompt[0].name, 'prompt_0');
468
+ t.is(result[0].prompt[0].messages[1].content, '{{text}}\n\nFirst prompt text');
469
+
470
+ t.is(result[1].prompt[0].name, 'prompt_2');
471
+ t.is(result[1].prompt[0].messages[1].content, '{{text}}\n\nThird prompt text');
472
+ });
473
+
474
+ test('getPathways filters by promptNames (mixed format)', async t => {
475
+ const pathwayTemplate = {
476
+ prompt: [
477
+ 'Legacy string prompt',
478
+ { name: 'Named Prompt', prompt: 'Named prompt text' },
479
+ { prompt: 'Unnamed object prompt' }
480
+ ],
481
+ systemPrompt: 'Mixed format system'
482
+ };
483
+
484
+ const result = await t.context.pathwayManager.getPathways(
485
+ pathwayTemplate,
486
+ ['prompt_0', 'Named Prompt']
487
+ );
488
+
489
+ t.is(result.length, 2);
490
+
491
+ // Check first filtered result (string format)
492
+ t.is(result[0].prompt[0].name, 'prompt_0');
493
+ t.is(result[0].prompt[0].messages[1].content, '{{text}}\n\nLegacy string prompt');
494
+
495
+ // Check second filtered result (named object)
496
+ t.is(result[1].prompt[0].name, 'Named Prompt');
497
+ t.is(result[1].prompt[0].messages[1].content, '{{text}}\n\nNamed prompt text');
498
+ });
499
+
500
+ test('getPathways returns empty array when no promptNames match', async t => {
501
+ const pathwayTemplate = {
502
+ prompt: [
503
+ { name: 'Grammar Check', prompt: 'Check grammar' },
504
+ { name: 'Tone Analysis', prompt: 'Analyze tone' }
505
+ ],
506
+ systemPrompt: 'System prompt'
507
+ };
508
+
509
+ const result = await t.context.pathwayManager.getPathways(
510
+ pathwayTemplate,
511
+ ['Non-existent Prompt', 'Another Missing Prompt']
512
+ );
513
+
514
+ t.is(result.length, 0);
515
+ t.true(Array.isArray(result));
516
+ });
517
+
518
+ test('getPathways returns all pathways when promptNames is empty array', async t => {
519
+ const pathwayTemplate = {
520
+ prompt: [
521
+ { name: 'First', prompt: 'First prompt' },
522
+ { name: 'Second', prompt: 'Second prompt' }
523
+ ],
524
+ systemPrompt: 'System prompt'
525
+ };
526
+
527
+ const result = await t.context.pathwayManager.getPathways(
528
+ pathwayTemplate,
529
+ []
530
+ );
531
+
532
+ t.is(result.length, 2);
533
+ });
534
+
535
+ test('getPathways returns all pathways when promptNames is null', async t => {
536
+ const pathwayTemplate = {
537
+ prompt: [
538
+ { name: 'First', prompt: 'First prompt' },
539
+ { name: 'Second', prompt: 'Second prompt' }
540
+ ],
541
+ systemPrompt: 'System prompt'
542
+ };
543
+
544
+ const result = await t.context.pathwayManager.getPathways(pathwayTemplate, null);
545
+
546
+ t.is(result.length, 2);
547
+ });
548
+
549
+ test('isLegacyPromptFormat identifies legacy format (array of strings)', t => {
550
+ const userId = 'testUser';
551
+ const pathwayName = 'testPathway';
552
+
553
+ // Set up pathway with legacy prompts
554
+ t.context.pathwayManager.pathways = {
555
+ [userId]: {
556
+ [pathwayName]: {
557
+ prompt: [
558
+ 'First prompt text',
559
+ 'Second prompt text',
560
+ 'Third prompt text'
561
+ ]
562
+ }
563
+ }
564
+ };
565
+
566
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
567
+ t.true(result);
568
+ });
569
+
570
+ test('isLegacyPromptFormat identifies new format (array of objects)', t => {
571
+ const userId = 'testUser';
572
+ const pathwayName = 'testPathway';
573
+
574
+ // Set up pathway with new format prompts
575
+ t.context.pathwayManager.pathways = {
576
+ [userId]: {
577
+ [pathwayName]: {
578
+ prompt: [
579
+ { name: 'First Prompt', prompt: 'First prompt text' },
580
+ { name: 'Second Prompt', prompt: 'Second prompt text' },
581
+ { prompt: 'Third prompt text' } // name is optional
582
+ ]
583
+ }
584
+ }
585
+ };
586
+
587
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
588
+ t.false(result);
589
+ });
590
+
591
+ test('isLegacyPromptFormat handles empty array (defaults to new format)', t => {
592
+ const userId = 'testUser';
593
+ const pathwayName = 'testPathway';
594
+
595
+ // Set up pathway with empty prompts array
596
+ t.context.pathwayManager.pathways = {
597
+ [userId]: {
598
+ [pathwayName]: {
599
+ prompt: []
600
+ }
601
+ }
602
+ };
603
+
604
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
605
+ t.false(result);
606
+ });
607
+
608
+ test('isLegacyPromptFormat handles mixed format (treats as legacy)', t => {
609
+ const userId = 'testUser';
610
+ const pathwayName = 'testPathway';
611
+
612
+ // Set up pathway with mixed format prompts
613
+ t.context.pathwayManager.pathways = {
614
+ [userId]: {
615
+ [pathwayName]: {
616
+ prompt: [
617
+ 'Legacy string prompt',
618
+ { name: 'New format prompt', prompt: 'New format text' }
619
+ ]
620
+ }
621
+ }
622
+ };
623
+
624
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
625
+ t.true(result);
626
+ });
627
+
628
+ test('isLegacyPromptFormat throws error for invalid parameters', t => {
629
+ t.throws(() => {
630
+ t.context.pathwayManager.isLegacyPromptFormat('', 'pathwayName');
631
+ }, { message: 'userId must be a non-empty string' });
632
+
633
+ t.throws(() => {
634
+ t.context.pathwayManager.isLegacyPromptFormat(null, 'pathwayName');
635
+ }, { message: 'userId must be a non-empty string' });
636
+
637
+ t.throws(() => {
638
+ t.context.pathwayManager.isLegacyPromptFormat('userId', '');
639
+ }, { message: 'pathwayName must be a non-empty string' });
640
+
641
+ t.throws(() => {
642
+ t.context.pathwayManager.isLegacyPromptFormat('userId', null);
643
+ }, { message: 'pathwayName must be a non-empty string' });
644
+ });
645
+
646
+ test('isLegacyPromptFormat handles objects with missing prompt property (treats as legacy)', t => {
647
+ const userId = 'testUser';
648
+ const pathwayName = 'testPathway';
649
+
650
+ // Set up pathway with invalid objects
651
+ t.context.pathwayManager.pathways = {
652
+ [userId]: {
653
+ [pathwayName]: {
654
+ prompt: [
655
+ { name: 'Missing prompt property' },
656
+ { name: 'Another invalid object', notPrompt: 'invalid' }
657
+ ]
658
+ }
659
+ }
660
+ };
661
+
662
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
663
+ t.true(result);
664
+ });
665
+
666
+ test('isLegacyPromptFormat handles objects with null prompt property (treats as legacy)', t => {
667
+ const userId = 'testUser';
668
+ const pathwayName = 'testPathway';
669
+
670
+ // Set up pathway with null prompt properties
671
+ t.context.pathwayManager.pathways = {
672
+ [userId]: {
673
+ [pathwayName]: {
674
+ prompt: [
675
+ { name: 'Null prompt', prompt: null },
676
+ { name: 'Another null prompt', prompt: null }
677
+ ]
678
+ }
679
+ }
680
+ };
681
+
682
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
683
+ t.true(result);
684
+ });
685
+
686
+ test('isLegacyPromptFormat handles array with null elements (treats as legacy)', t => {
687
+ const userId = 'testUser';
688
+ const pathwayName = 'testPathway';
689
+
690
+ // Set up pathway with null elements
691
+ t.context.pathwayManager.pathways = {
692
+ [userId]: {
693
+ [pathwayName]: {
694
+ prompt: [
695
+ null,
696
+ 'Some string prompt',
697
+ null
698
+ ]
699
+ }
700
+ }
701
+ };
702
+
703
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
704
+ t.true(result);
705
+ });
706
+
707
+ test('isLegacyPromptFormat handles single string element', t => {
708
+ const userId = 'testUser';
709
+ const pathwayName = 'testPathway';
710
+
711
+ // Set up pathway with single string element
712
+ t.context.pathwayManager.pathways = {
713
+ [userId]: {
714
+ [pathwayName]: {
715
+ prompt: ['Only prompt']
716
+ }
717
+ }
718
+ };
719
+
720
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
721
+ t.true(result);
722
+ });
723
+
724
+ test('isLegacyPromptFormat handles single object element', t => {
725
+ const userId = 'testUser';
726
+ const pathwayName = 'testPathway';
727
+
728
+ // Set up pathway with single object element
729
+ t.context.pathwayManager.pathways = {
730
+ [userId]: {
731
+ [pathwayName]: {
732
+ prompt: [{ name: 'Only prompt', prompt: 'Only prompt text' }]
733
+ }
734
+ }
735
+ };
736
+
737
+ const result = t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
738
+ t.false(result);
739
+ });
740
+
741
+ test('isLegacyPromptFormat throws error when pathway not found', t => {
742
+ const userId = 'testUser';
743
+ const pathwayName = 'nonExistentPathway';
744
+
745
+ // Set up empty pathways
746
+ t.context.pathwayManager.pathways = {};
747
+
748
+ t.throws(() => {
749
+ t.context.pathwayManager.isLegacyPromptFormat(userId, pathwayName);
750
+ }, { message: `Pathway 'nonExistentPathway' not found for user 'testUser'` });
751
+ });
752
+
753
+ test('getPathways throws error for non-array promptNames', async t => {
754
+ const pathwayTemplate = {
755
+ prompt: [
756
+ { name: 'Test', prompt: 'Test prompt' }
757
+ ],
758
+ systemPrompt: 'System prompt'
759
+ };
760
+
761
+ await t.throwsAsync(
762
+ () => t.context.pathwayManager.getPathways(pathwayTemplate, 'not-an-array'),
763
+ { message: 'promptNames must be an array if provided' }
764
+ );
765
+ });
766
+
767
+ // Note: executeSpecificPrompts tests have been moved to GraphQL integration tests
768
+ // since the function is now part of the GraphQL resolver layer, not the PathwayManager