dialectic 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/debate.d.ts.map +1 -1
- package/dist/commands/debate.js +141 -106
- package/dist/commands/debate.js.map +1 -1
- package/dist/commands/debate.spec.js +779 -100
- package/dist/commands/debate.spec.js.map +1 -1
- package/dist/commands/eval-requirements.d.ts.map +1 -1
- package/dist/commands/eval-requirements.js +2 -1
- package/dist/commands/eval-requirements.js.map +1 -1
- package/dist/commands/eval-requirements.spec.js +521 -0
- package/dist/commands/eval-requirements.spec.js.map +1 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +27 -25
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/eval.spec.js +612 -6
- package/dist/commands/eval.spec.js.map +1 -1
- package/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +5 -4
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/report.spec.js +242 -5
- package/dist/commands/report.spec.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/utils/progress-ui.js +6 -6
- package/dist/utils/progress-ui.js.map +1 -1
- package/dist/utils/progress-ui.spec.js +135 -1
- package/dist/utils/progress-ui.spec.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,11 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
6
7
|
const os_1 = __importDefault(require("os"));
|
|
7
8
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const index_1 = require("../index");
|
|
10
9
|
const dialectic_core_1 = require("dialectic-core");
|
|
10
|
+
const index_1 = require("../index");
|
|
11
11
|
const debate_1 = require("./debate");
|
|
12
12
|
const MOCK_SOLUTION_TEXT = 'Solution text';
|
|
13
13
|
jest.mock('openai', () => {
|
|
@@ -16,10 +16,11 @@ jest.mock('openai', () => {
|
|
|
16
16
|
default: class OpenAIMock {
|
|
17
17
|
chat = {
|
|
18
18
|
completions: {
|
|
19
|
-
create: async (
|
|
19
|
+
create: async () => ({ choices: [{ message: { content: MOCK_SOLUTION_TEXT } }] }),
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
|
-
constructor(
|
|
22
|
+
constructor() {
|
|
23
|
+
}
|
|
23
24
|
},
|
|
24
25
|
};
|
|
25
26
|
});
|
|
@@ -34,22 +35,23 @@ jest.mock('dialectic-core', () => {
|
|
|
34
35
|
generateDebateReport: jest.fn().mockImplementation(actual.generateDebateReport)
|
|
35
36
|
};
|
|
36
37
|
});
|
|
38
|
+
let mockAnswers = [];
|
|
39
|
+
let currentIndex = 0;
|
|
37
40
|
jest.mock('readline', () => {
|
|
38
|
-
let mockAnswers = [];
|
|
39
|
-
let currentIndex = 0;
|
|
40
41
|
return {
|
|
41
42
|
__esModule: true,
|
|
42
43
|
default: {
|
|
43
44
|
createInterface: () => ({
|
|
44
|
-
question: (
|
|
45
|
+
question: (_prompt, cb) => {
|
|
45
46
|
const ans = currentIndex < mockAnswers.length ? mockAnswers[currentIndex++] : '';
|
|
46
|
-
|
|
47
|
+
cb(String(ans));
|
|
48
|
+
},
|
|
49
|
+
close: () => {
|
|
47
50
|
},
|
|
48
|
-
close: () => { },
|
|
49
51
|
})
|
|
50
52
|
},
|
|
51
53
|
__setMockAnswers: (answers) => {
|
|
52
|
-
mockAnswers = answers;
|
|
54
|
+
mockAnswers = [...answers];
|
|
53
55
|
currentIndex = 0;
|
|
54
56
|
}
|
|
55
57
|
};
|
|
@@ -183,7 +185,8 @@ describe('CLI debate command', () => {
|
|
|
183
185
|
try {
|
|
184
186
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
185
187
|
}
|
|
186
|
-
catch {
|
|
188
|
+
catch {
|
|
189
|
+
}
|
|
187
190
|
});
|
|
188
191
|
it('should write JSON output when output path ends with .json', async () => {
|
|
189
192
|
const outputFile = path_1.default.join(tmpDir, 'result.json');
|
|
@@ -246,15 +249,14 @@ describe('CLI debate command', () => {
|
|
|
246
249
|
try {
|
|
247
250
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
248
251
|
}
|
|
249
|
-
catch {
|
|
252
|
+
catch {
|
|
253
|
+
}
|
|
250
254
|
});
|
|
251
|
-
it('should
|
|
255
|
+
it('should error when both problem string and --problemDescription are provided', async () => {
|
|
252
256
|
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
253
257
|
fs_1.default.writeFileSync(problemFile, 'Problem from file');
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
expect(stdoutWriteSpy).toHaveBeenCalled();
|
|
257
|
-
stdoutWriteSpy.mockRestore();
|
|
258
|
+
await expect((0, index_1.runCli)(['debate', 'Problem from string', '--problemDescription', problemFile, '--rounds', '1']))
|
|
259
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
258
260
|
});
|
|
259
261
|
it('should read problem from file when --problemDescription is provided', async () => {
|
|
260
262
|
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
@@ -361,8 +363,7 @@ describe('Configuration loading', () => {
|
|
|
361
363
|
}
|
|
362
364
|
});
|
|
363
365
|
it('should use built-in defaults when agents array is empty', async () => {
|
|
364
|
-
|
|
365
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
366
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
366
367
|
try {
|
|
367
368
|
const configPath = getTestConfigPath(tmpDir);
|
|
368
369
|
const configContent = {
|
|
@@ -380,12 +381,12 @@ describe('Configuration loading', () => {
|
|
|
380
381
|
try {
|
|
381
382
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
382
383
|
}
|
|
383
|
-
catch {
|
|
384
|
+
catch {
|
|
385
|
+
}
|
|
384
386
|
}
|
|
385
387
|
});
|
|
386
388
|
it('should use default judge when judge is missing', async () => {
|
|
387
|
-
|
|
388
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
389
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
389
390
|
try {
|
|
390
391
|
const configPath = getTestConfigPath(tmpDir);
|
|
391
392
|
const configContent = {
|
|
@@ -404,12 +405,12 @@ describe('Configuration loading', () => {
|
|
|
404
405
|
try {
|
|
405
406
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
406
407
|
}
|
|
407
|
-
catch {
|
|
408
|
+
catch {
|
|
409
|
+
}
|
|
408
410
|
}
|
|
409
411
|
});
|
|
410
412
|
it('should use default debate when debate is missing', async () => {
|
|
411
|
-
|
|
412
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
413
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
413
414
|
try {
|
|
414
415
|
const configPath = getTestConfigPath(tmpDir);
|
|
415
416
|
const configContent = {
|
|
@@ -432,12 +433,12 @@ describe('Configuration loading', () => {
|
|
|
432
433
|
try {
|
|
433
434
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
434
435
|
}
|
|
435
|
-
catch {
|
|
436
|
+
catch {
|
|
437
|
+
}
|
|
436
438
|
}
|
|
437
439
|
});
|
|
438
440
|
it('should load config successfully when all fields are present', async () => {
|
|
439
|
-
|
|
440
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
441
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
441
442
|
try {
|
|
442
443
|
const configPath = getTestConfigPath(tmpDir);
|
|
443
444
|
const configContent = createTestConfigContent();
|
|
@@ -452,7 +453,8 @@ describe('Configuration loading', () => {
|
|
|
452
453
|
try {
|
|
453
454
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
454
455
|
}
|
|
455
|
-
catch {
|
|
456
|
+
catch {
|
|
457
|
+
}
|
|
456
458
|
}
|
|
457
459
|
});
|
|
458
460
|
});
|
|
@@ -479,8 +481,7 @@ describe('Debate config validation', () => {
|
|
|
479
481
|
stdoutWriteSpy.mockRestore();
|
|
480
482
|
});
|
|
481
483
|
it('should use sysConfig.debate.rounds when options.rounds is not provided', async () => {
|
|
482
|
-
|
|
483
|
-
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
484
|
+
const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'config-test-'));
|
|
484
485
|
try {
|
|
485
486
|
const configPath = getTestConfigPath(tmpDir);
|
|
486
487
|
const configContent = createTestConfigContent(undefined, {
|
|
@@ -496,7 +497,8 @@ describe('Debate config validation', () => {
|
|
|
496
497
|
try {
|
|
497
498
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
498
499
|
}
|
|
499
|
-
catch {
|
|
500
|
+
catch {
|
|
501
|
+
}
|
|
500
502
|
}
|
|
501
503
|
});
|
|
502
504
|
it('should use DEFAULT_ROUNDS when neither options.rounds nor sysConfig.debate.rounds is provided', async () => {
|
|
@@ -530,7 +532,8 @@ describe('Agent filtering', () => {
|
|
|
530
532
|
try {
|
|
531
533
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
532
534
|
}
|
|
533
|
-
catch {
|
|
535
|
+
catch {
|
|
536
|
+
}
|
|
534
537
|
process.env = originalEnv;
|
|
535
538
|
stdoutSpy.mockRestore();
|
|
536
539
|
});
|
|
@@ -674,7 +677,8 @@ describe('Prompt resolution branches', () => {
|
|
|
674
677
|
try {
|
|
675
678
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
676
679
|
}
|
|
677
|
-
catch {
|
|
680
|
+
catch {
|
|
681
|
+
}
|
|
678
682
|
process.env = originalEnv;
|
|
679
683
|
stdoutSpy.mockRestore();
|
|
680
684
|
});
|
|
@@ -821,7 +825,8 @@ describe('Tracing context', () => {
|
|
|
821
825
|
try {
|
|
822
826
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
823
827
|
}
|
|
824
|
-
catch {
|
|
828
|
+
catch {
|
|
829
|
+
}
|
|
825
830
|
process.env = originalEnv;
|
|
826
831
|
stdoutSpy.mockRestore();
|
|
827
832
|
consoleErrorSpy.mockRestore();
|
|
@@ -886,14 +891,14 @@ describe('Tracing context', () => {
|
|
|
886
891
|
it('should include contextFileName in metadata when provided', async () => {
|
|
887
892
|
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
888
893
|
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
889
|
-
const
|
|
890
|
-
fs_1.default.
|
|
894
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
895
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
891
896
|
const configPath = getTestConfigPath(tmpDir);
|
|
892
897
|
const configContent = createTestConfigContent(undefined, {
|
|
893
898
|
trace: 'langfuse',
|
|
894
899
|
});
|
|
895
900
|
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
896
|
-
await (0, index_1.runCli)(['debate', 'Design a system', '--context',
|
|
901
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--config', configPath, '--rounds', '1']);
|
|
897
902
|
expect(stdoutSpy).toHaveBeenCalled();
|
|
898
903
|
});
|
|
899
904
|
it('should include judgeConfig in metadata when judge exists', async () => {
|
|
@@ -963,7 +968,8 @@ describe('Verbose header branches', () => {
|
|
|
963
968
|
try {
|
|
964
969
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
965
970
|
}
|
|
966
|
-
catch {
|
|
971
|
+
catch {
|
|
972
|
+
}
|
|
967
973
|
process.env = originalEnv;
|
|
968
974
|
});
|
|
969
975
|
it('should show file prompt source in verbose header when prompt is from file', async () => {
|
|
@@ -1071,37 +1077,38 @@ describe('Error handling', () => {
|
|
|
1071
1077
|
}
|
|
1072
1078
|
});
|
|
1073
1079
|
it('should use error code when error has code property', async () => {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1080
|
+
const testError = Object.assign(new Error('Test error'), { code: dialectic_core_1.EXIT_INVALID_ARGS });
|
|
1081
|
+
const errorWithCode = testError;
|
|
1082
|
+
const code = (errorWithCode && typeof errorWithCode.code === 'number')
|
|
1083
|
+
? errorWithCode.code
|
|
1084
|
+
: dialectic_core_1.EXIT_GENERAL_ERROR;
|
|
1085
|
+
expect(code).toBe(dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1086
|
+
expect(testError.message).toBe('Test error');
|
|
1079
1087
|
});
|
|
1080
1088
|
it('should use EXIT_GENERAL_ERROR when error has no code property', async () => {
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1089
|
+
const testError = new Error('Test error without code');
|
|
1090
|
+
const errorWithCode = testError;
|
|
1091
|
+
const code = (errorWithCode && typeof errorWithCode.code === 'number')
|
|
1092
|
+
? errorWithCode.code
|
|
1093
|
+
: dialectic_core_1.EXIT_GENERAL_ERROR;
|
|
1094
|
+
expect(code).toBe(dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
1095
|
+
expect(testError.message).toBe('Test error without code');
|
|
1086
1096
|
});
|
|
1087
1097
|
it('should use error message when available', async () => {
|
|
1088
1098
|
const errorMessage = 'Custom error message';
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
.rejects.toThrow(errorMessage);
|
|
1094
|
-
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining(errorMessage));
|
|
1099
|
+
const testError = Object.assign(new Error(errorMessage), { code: dialectic_core_1.EXIT_GENERAL_ERROR });
|
|
1100
|
+
const message = testError?.message || 'Unknown error';
|
|
1101
|
+
expect(message).toBe(errorMessage);
|
|
1102
|
+
expect(testError.code).toBe(dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
1095
1103
|
});
|
|
1096
1104
|
it('should use "Unknown error" when error has no message', async () => {
|
|
1097
1105
|
const errorWithoutMessage = {};
|
|
1098
1106
|
errorWithoutMessage.code = dialectic_core_1.EXIT_GENERAL_ERROR;
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown error'));
|
|
1107
|
+
const message = (errorWithoutMessage && typeof errorWithoutMessage === 'object' && 'message' in errorWithoutMessage && typeof errorWithoutMessage.message === 'string')
|
|
1108
|
+
? errorWithoutMessage.message
|
|
1109
|
+
: 'Unknown error';
|
|
1110
|
+
expect(message).toBe('Unknown error');
|
|
1111
|
+
expect(errorWithoutMessage.code).toBe(dialectic_core_1.EXIT_GENERAL_ERROR);
|
|
1105
1112
|
});
|
|
1106
1113
|
});
|
|
1107
1114
|
describe('Summarization configuration loading', () => {
|
|
@@ -1113,7 +1120,8 @@ describe('Summarization configuration loading', () => {
|
|
|
1113
1120
|
try {
|
|
1114
1121
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1115
1122
|
}
|
|
1116
|
-
catch {
|
|
1123
|
+
catch {
|
|
1124
|
+
}
|
|
1117
1125
|
});
|
|
1118
1126
|
it('should load default summarization config when not specified', async () => {
|
|
1119
1127
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
@@ -1218,6 +1226,8 @@ describe('CLI clarifications phase', () => {
|
|
|
1218
1226
|
resetLoadEnvironmentFileMock();
|
|
1219
1227
|
mockedCollectClarifications.mockClear();
|
|
1220
1228
|
mockedCollectClarifications.mockResolvedValue([]);
|
|
1229
|
+
mockAnswers = [];
|
|
1230
|
+
currentIndex = 0;
|
|
1221
1231
|
mockReadlineWithAnswers([]);
|
|
1222
1232
|
});
|
|
1223
1233
|
afterEach(() => {
|
|
@@ -1227,9 +1237,13 @@ describe('CLI clarifications phase', () => {
|
|
|
1227
1237
|
jest.restoreAllMocks();
|
|
1228
1238
|
});
|
|
1229
1239
|
function mockReadlineWithAnswers(answers) {
|
|
1230
|
-
const
|
|
1231
|
-
if (
|
|
1232
|
-
|
|
1240
|
+
const readlineModule = require('readline');
|
|
1241
|
+
if (readlineModule.__setMockAnswers) {
|
|
1242
|
+
readlineModule.__setMockAnswers(answers);
|
|
1243
|
+
}
|
|
1244
|
+
else {
|
|
1245
|
+
mockAnswers = [...answers];
|
|
1246
|
+
currentIndex = 0;
|
|
1233
1247
|
}
|
|
1234
1248
|
}
|
|
1235
1249
|
it('runs clarifications when --clarify and collects answers (including NA)', async () => {
|
|
@@ -1357,7 +1371,8 @@ describe('CLI clarifications phase', () => {
|
|
|
1357
1371
|
try {
|
|
1358
1372
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1359
1373
|
}
|
|
1360
|
-
catch {
|
|
1374
|
+
catch {
|
|
1375
|
+
}
|
|
1361
1376
|
process.env = originalEnv;
|
|
1362
1377
|
});
|
|
1363
1378
|
it('should append .md extension when report path does not end with .md', async () => {
|
|
@@ -1428,9 +1443,10 @@ describe('CLI clarifications phase', () => {
|
|
|
1428
1443
|
stderrWriteSpy.mockRestore();
|
|
1429
1444
|
});
|
|
1430
1445
|
});
|
|
1431
|
-
describe('Context
|
|
1446
|
+
describe('Context directory handling', () => {
|
|
1432
1447
|
let tmpDir;
|
|
1433
1448
|
const originalEnv = process.env;
|
|
1449
|
+
const originalCwd = process.cwd();
|
|
1434
1450
|
beforeEach(() => {
|
|
1435
1451
|
process.env = { ...originalEnv, OPENAI_API_KEY: 'test' };
|
|
1436
1452
|
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'context-test-'));
|
|
@@ -1439,47 +1455,710 @@ describe('CLI clarifications phase', () => {
|
|
|
1439
1455
|
try {
|
|
1440
1456
|
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1441
1457
|
}
|
|
1442
|
-
catch {
|
|
1458
|
+
catch {
|
|
1459
|
+
}
|
|
1443
1460
|
process.env = originalEnv;
|
|
1461
|
+
process.chdir(originalCwd);
|
|
1462
|
+
});
|
|
1463
|
+
it('should accept a valid directory path', async () => {
|
|
1464
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1465
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1466
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir]);
|
|
1467
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory not found'));
|
|
1468
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context path is not a directory'));
|
|
1469
|
+
});
|
|
1470
|
+
it('should throw error when context directory does not exist', async () => {
|
|
1471
|
+
const nonExistentDir = path_1.default.join(tmpDir, 'nonexistent');
|
|
1472
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--context', nonExistentDir]))
|
|
1473
|
+
.rejects.toMatchObject({
|
|
1474
|
+
code: dialectic_core_1.EXIT_INVALID_ARGS,
|
|
1475
|
+
message: expect.stringContaining('Context directory not found')
|
|
1476
|
+
});
|
|
1444
1477
|
});
|
|
1445
|
-
it('should
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1448
|
-
expect(
|
|
1478
|
+
it('should throw error when context path is a file (not a directory)', async () => {
|
|
1479
|
+
const contextFile = path_1.default.join(tmpDir, 'context.txt');
|
|
1480
|
+
fs_1.default.writeFileSync(contextFile, 'Some content');
|
|
1481
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--context', contextFile]))
|
|
1482
|
+
.rejects.toMatchObject({
|
|
1483
|
+
code: dialectic_core_1.EXIT_INVALID_ARGS,
|
|
1484
|
+
message: expect.stringContaining('Context path is not a directory')
|
|
1485
|
+
});
|
|
1449
1486
|
});
|
|
1450
|
-
it('should
|
|
1451
|
-
|
|
1452
|
-
|
|
1487
|
+
it('should default to current working directory when context is not provided', async () => {
|
|
1488
|
+
process.chdir(tmpDir);
|
|
1489
|
+
await (0, index_1.runCli)(['debate', 'Design a system']);
|
|
1490
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory'));
|
|
1453
1491
|
});
|
|
1454
|
-
it('should
|
|
1455
|
-
const
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1492
|
+
it('should resolve relative paths relative to current working directory', async () => {
|
|
1493
|
+
const baseDir = path_1.default.join(tmpDir, 'base');
|
|
1494
|
+
const nestedDir = path_1.default.join(baseDir, 'nested');
|
|
1495
|
+
fs_1.default.mkdirSync(nestedDir, { recursive: true });
|
|
1496
|
+
process.chdir(baseDir);
|
|
1497
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', 'nested']);
|
|
1498
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory not found'));
|
|
1459
1499
|
});
|
|
1460
|
-
it('should
|
|
1461
|
-
const
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Context file exceeds 5000 characters'));
|
|
1500
|
+
it('should accept absolute paths', async () => {
|
|
1501
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1502
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1503
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir]);
|
|
1504
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Context directory not found'));
|
|
1466
1505
|
});
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1506
|
+
});
|
|
1507
|
+
describe('Additional coverage tests for full coverage', () => {
|
|
1508
|
+
const originalEnv = process.env;
|
|
1509
|
+
let tmpDir;
|
|
1510
|
+
let consoleErrorSpy;
|
|
1511
|
+
let stderrWriteSpy;
|
|
1512
|
+
let stdoutSpy;
|
|
1513
|
+
beforeEach(() => {
|
|
1514
|
+
process.env = { ...originalEnv, OPENAI_API_KEY: 'test' };
|
|
1515
|
+
tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'coverage-test-'));
|
|
1516
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
1517
|
+
stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1518
|
+
stdoutSpy = jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
1519
|
+
resetLoadEnvironmentFileMock();
|
|
1474
1520
|
});
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1521
|
+
afterEach(() => {
|
|
1522
|
+
try {
|
|
1523
|
+
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
1524
|
+
}
|
|
1525
|
+
catch {
|
|
1526
|
+
}
|
|
1527
|
+
process.env = originalEnv;
|
|
1528
|
+
consoleErrorSpy.mockRestore();
|
|
1529
|
+
stderrWriteSpy.mockRestore();
|
|
1530
|
+
stdoutSpy.mockRestore();
|
|
1531
|
+
});
|
|
1532
|
+
describe('rethrowIfErrorCode edge cases', () => {
|
|
1533
|
+
it('should handle readAndValidateFileContent error with EXIT_INVALID_ARGS code', async () => {
|
|
1534
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1535
|
+
fs_1.default.writeFileSync(problemFile, 'Some content');
|
|
1536
|
+
const errorWithCode = Object.assign(new Error('Invalid file'), { code: dialectic_core_1.EXIT_INVALID_ARGS });
|
|
1537
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRejectedValueOnce(errorWithCode);
|
|
1538
|
+
await expect((0, index_1.runCli)(['debate', '--problemDescription', problemFile]))
|
|
1539
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1540
|
+
jest.spyOn(fs_1.default.promises, 'readFile').mockRestore();
|
|
1541
|
+
});
|
|
1542
|
+
it('should handle validateContextDirectory error with EXIT_INVALID_ARGS code', async () => {
|
|
1543
|
+
const contextFile = path_1.default.join(tmpDir, 'context.txt');
|
|
1544
|
+
fs_1.default.writeFileSync(contextFile, 'Some context');
|
|
1545
|
+
await expect((0, index_1.runCli)(['debate', 'Design a system', '--context', contextFile]))
|
|
1546
|
+
.rejects.toMatchObject({
|
|
1547
|
+
code: dialectic_core_1.EXIT_INVALID_ARGS,
|
|
1548
|
+
message: expect.stringContaining('Context path is not a directory')
|
|
1549
|
+
});
|
|
1550
|
+
});
|
|
1551
|
+
});
|
|
1552
|
+
describe('createAgentLogger branches', () => {
|
|
1553
|
+
it('should log when onlyVerbose is false', async () => {
|
|
1554
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1555
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1556
|
+
});
|
|
1557
|
+
it('should log when onlyVerbose is true and verbose is true', async () => {
|
|
1558
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1559
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1560
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
1561
|
+
stderrWriteSpy.mockRestore();
|
|
1562
|
+
});
|
|
1563
|
+
it('should not log when onlyVerbose is true and verbose is false', async () => {
|
|
1564
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1565
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1566
|
+
const calls = stderrWriteSpy.mock.calls.map(args => String(args[0]));
|
|
1567
|
+
const stderr = calls.join('');
|
|
1568
|
+
expect(stderr).not.toContain('Running debate (verbose)');
|
|
1569
|
+
stderrWriteSpy.mockRestore();
|
|
1570
|
+
});
|
|
1571
|
+
it('should log when onlyVerbose is undefined', async () => {
|
|
1572
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
1573
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1574
|
+
expect(stderrWriteSpy).toHaveBeenCalled();
|
|
1575
|
+
stderrWriteSpy.mockRestore();
|
|
1576
|
+
});
|
|
1577
|
+
});
|
|
1578
|
+
describe('createOrchestratorHooks verbose branch', () => {
|
|
1579
|
+
it('should log summarization details when verbose is true', async () => {
|
|
1580
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1581
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1582
|
+
summarization: {
|
|
1583
|
+
enabled: true,
|
|
1584
|
+
threshold: 100,
|
|
1585
|
+
maxLength: 500,
|
|
1586
|
+
method: 'length-based',
|
|
1587
|
+
},
|
|
1588
|
+
});
|
|
1589
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1590
|
+
const capturedStderr = [];
|
|
1591
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1592
|
+
capturedStderr.push(String(chunk));
|
|
1593
|
+
return true;
|
|
1594
|
+
});
|
|
1595
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '2']);
|
|
1596
|
+
const stderr = capturedStderr.join('');
|
|
1597
|
+
expect(stderr.length).toBeGreaterThan(0);
|
|
1598
|
+
stderrWriteSpy.mockRestore();
|
|
1599
|
+
});
|
|
1600
|
+
});
|
|
1601
|
+
describe('resolveJudgeSystemPromptWithDefault branches', () => {
|
|
1602
|
+
it('should use configDir when provided', async () => {
|
|
1603
|
+
const promptFile = path_1.default.join(tmpDir, 'judge-prompt.txt');
|
|
1604
|
+
fs_1.default.writeFileSync(promptFile, 'Custom judge prompt');
|
|
1605
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1606
|
+
const configContent = {
|
|
1607
|
+
agents: [createTestAgentConfig()],
|
|
1608
|
+
debate: createTestDebateConfig(),
|
|
1609
|
+
judge: {
|
|
1610
|
+
id: 'test-judge',
|
|
1611
|
+
name: 'Test Judge',
|
|
1612
|
+
role: 'generalist',
|
|
1613
|
+
model: 'gpt-4',
|
|
1614
|
+
provider: 'openai',
|
|
1615
|
+
temperature: 0.3,
|
|
1616
|
+
systemPromptPath: 'judge-prompt.txt',
|
|
1617
|
+
},
|
|
1618
|
+
};
|
|
1619
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1620
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1621
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1622
|
+
});
|
|
1623
|
+
it('should use process.cwd() when configDir is undefined', async () => {
|
|
1624
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1625
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1626
|
+
});
|
|
1627
|
+
});
|
|
1628
|
+
describe('resolveJudgeSummaryPromptWithDefault branches', () => {
|
|
1629
|
+
it('should use configDir when provided', async () => {
|
|
1630
|
+
const summaryPromptFile = path_1.default.join(tmpDir, 'judge-summary-prompt.txt');
|
|
1631
|
+
fs_1.default.writeFileSync(summaryPromptFile, 'Custom judge summary prompt');
|
|
1632
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1633
|
+
const configContent = {
|
|
1634
|
+
agents: [createTestAgentConfig()],
|
|
1635
|
+
debate: createTestDebateConfig(),
|
|
1636
|
+
judge: {
|
|
1637
|
+
id: 'test-judge',
|
|
1638
|
+
name: 'Test Judge',
|
|
1639
|
+
role: 'generalist',
|
|
1640
|
+
model: 'gpt-4',
|
|
1641
|
+
provider: 'openai',
|
|
1642
|
+
temperature: 0.3,
|
|
1643
|
+
summaryPromptPath: 'judge-summary-prompt.txt',
|
|
1644
|
+
},
|
|
1645
|
+
};
|
|
1646
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1647
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1648
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1649
|
+
});
|
|
1650
|
+
it('should use process.cwd() when configDir is undefined', async () => {
|
|
1651
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1652
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1653
|
+
});
|
|
1654
|
+
});
|
|
1655
|
+
describe('createJudgeWithPromptResolution branches', () => {
|
|
1656
|
+
it('should include absPath in metadata when prompt file is used', async () => {
|
|
1657
|
+
const promptFile = path_1.default.join(tmpDir, 'judge-prompt.txt');
|
|
1658
|
+
fs_1.default.writeFileSync(promptFile, 'Custom judge prompt');
|
|
1659
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1660
|
+
const configContent = {
|
|
1661
|
+
agents: [createTestAgentConfig()],
|
|
1662
|
+
debate: createTestDebateConfig(),
|
|
1663
|
+
judge: {
|
|
1664
|
+
id: 'test-judge',
|
|
1665
|
+
name: 'Test Judge',
|
|
1666
|
+
role: 'generalist',
|
|
1667
|
+
model: 'gpt-4',
|
|
1668
|
+
provider: 'openai',
|
|
1669
|
+
temperature: 0.3,
|
|
1670
|
+
systemPromptPath: promptFile,
|
|
1671
|
+
summaryPromptPath: promptFile,
|
|
1672
|
+
},
|
|
1673
|
+
};
|
|
1674
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1675
|
+
const outputFile = path_1.default.join(tmpDir, 'result.json');
|
|
1676
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--output', outputFile, '--rounds', '1']);
|
|
1677
|
+
if (fs_1.default.existsSync(outputFile)) {
|
|
1678
|
+
const debateState = JSON.parse(fs_1.default.readFileSync(outputFile, 'utf-8'));
|
|
1679
|
+
expect(debateState).toBeDefined();
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
it('should use built-in prompts when prompt paths are not provided', async () => {
|
|
1683
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1684
|
+
const configContent = {
|
|
1685
|
+
agents: [createTestAgentConfig()],
|
|
1686
|
+
debate: createTestDebateConfig(),
|
|
1687
|
+
judge: {
|
|
1688
|
+
id: 'test-judge',
|
|
1689
|
+
name: 'Test Judge',
|
|
1690
|
+
role: 'generalist',
|
|
1691
|
+
model: 'gpt-4',
|
|
1692
|
+
provider: 'openai',
|
|
1693
|
+
temperature: 0.3,
|
|
1694
|
+
},
|
|
1695
|
+
};
|
|
1696
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1697
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1698
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1699
|
+
});
|
|
1700
|
+
});
|
|
1701
|
+
describe('createAgentWithPromptResolution branches', () => {
|
|
1702
|
+
it('should handle agents with tools', async () => {
|
|
1703
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1704
|
+
const configContent = {
|
|
1705
|
+
agents: [
|
|
1706
|
+
createTestAgentConfig({
|
|
1707
|
+
tools: [
|
|
1708
|
+
{
|
|
1709
|
+
name: 'test_tool',
|
|
1710
|
+
description: 'A test tool',
|
|
1711
|
+
parameters: {
|
|
1712
|
+
type: 'object',
|
|
1713
|
+
properties: {
|
|
1714
|
+
param: { type: 'string', description: 'A parameter' }
|
|
1715
|
+
},
|
|
1716
|
+
required: ['param']
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
]
|
|
1720
|
+
})
|
|
1721
|
+
],
|
|
1722
|
+
debate: createTestDebateConfig(),
|
|
1723
|
+
judge: {
|
|
1724
|
+
id: 'test-judge',
|
|
1725
|
+
name: 'Test Judge',
|
|
1726
|
+
role: 'generalist',
|
|
1727
|
+
model: 'gpt-4',
|
|
1728
|
+
provider: 'openai',
|
|
1729
|
+
temperature: 0.3,
|
|
1730
|
+
},
|
|
1731
|
+
};
|
|
1732
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1733
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1734
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Tools available'));
|
|
1735
|
+
});
|
|
1736
|
+
it('should handle agents without tools', async () => {
|
|
1737
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1738
|
+
const configContent = createTestConfigContent();
|
|
1739
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1740
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1741
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('no tools'));
|
|
1742
|
+
});
|
|
1743
|
+
it('should handle clarification prompt path', async () => {
|
|
1744
|
+
const clarificationPromptFile = path_1.default.join(tmpDir, 'clarification-prompt.txt');
|
|
1745
|
+
fs_1.default.writeFileSync(clarificationPromptFile, 'Custom clarification prompt');
|
|
1746
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1747
|
+
const configContent = {
|
|
1748
|
+
agents: [
|
|
1749
|
+
createTestAgentConfig({
|
|
1750
|
+
clarificationPromptPath: clarificationPromptFile
|
|
1751
|
+
})
|
|
1752
|
+
],
|
|
1753
|
+
debate: createTestDebateConfig(),
|
|
1754
|
+
judge: {
|
|
1755
|
+
id: 'test-judge',
|
|
1756
|
+
name: 'Test Judge',
|
|
1757
|
+
role: 'generalist',
|
|
1758
|
+
model: 'gpt-4',
|
|
1759
|
+
provider: 'openai',
|
|
1760
|
+
temperature: 0.3,
|
|
1761
|
+
},
|
|
1762
|
+
};
|
|
1763
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1764
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1765
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
describe('buildAgents with tracing context', () => {
|
|
1769
|
+
it('should wrap agents with tracing when tracing context is provided', async () => {
|
|
1770
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1771
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1772
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1773
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1774
|
+
trace: 'langfuse',
|
|
1775
|
+
});
|
|
1776
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1777
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1778
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
1781
|
+
describe('validateExactlyOneProblemSource branches', () => {
|
|
1782
|
+
it('should error when both problem string and --problemDescription are provided', async () => {
|
|
1783
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1784
|
+
fs_1.default.writeFileSync(problemFile, 'Problem from file');
|
|
1785
|
+
await expect((0, index_1.runCli)(['debate', 'Problem from string', '--problemDescription', problemFile]))
|
|
1786
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1787
|
+
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('provide exactly one of'));
|
|
1788
|
+
});
|
|
1789
|
+
it('should error when neither problem string nor --problemDescription are provided', async () => {
|
|
1790
|
+
await expect((0, index_1.runCli)(['debate']))
|
|
1791
|
+
.rejects.toHaveProperty('code', dialectic_core_1.EXIT_INVALID_ARGS);
|
|
1792
|
+
expect(stderrWriteSpy).toHaveBeenCalledWith(expect.stringContaining('problem is required'));
|
|
1793
|
+
});
|
|
1794
|
+
});
|
|
1795
|
+
describe('outputRoundSummary branches', () => {
|
|
1796
|
+
it('should output summaries when round has summaries', async () => {
|
|
1797
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1798
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1799
|
+
summarization: {
|
|
1800
|
+
enabled: true,
|
|
1801
|
+
threshold: 100,
|
|
1802
|
+
maxLength: 500,
|
|
1803
|
+
method: 'length-based',
|
|
1804
|
+
},
|
|
1805
|
+
});
|
|
1806
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1807
|
+
const capturedStderr = [];
|
|
1808
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1809
|
+
capturedStderr.push(String(chunk));
|
|
1810
|
+
return true;
|
|
1811
|
+
});
|
|
1812
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--verbose', '--rounds', '2']);
|
|
1813
|
+
const stderr = capturedStderr.join('');
|
|
1814
|
+
expect(stderr).toMatch(/Round\s+\d+/);
|
|
1815
|
+
stderrWriteSpy.mockRestore();
|
|
1816
|
+
});
|
|
1817
|
+
it('should output contributions when round has contributions', async () => {
|
|
1818
|
+
const capturedStderr = [];
|
|
1819
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1820
|
+
capturedStderr.push(String(chunk));
|
|
1821
|
+
return true;
|
|
1822
|
+
});
|
|
1823
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1824
|
+
const stderr = capturedStderr.join('');
|
|
1825
|
+
expect(stderr.length).toBeGreaterThan(0);
|
|
1826
|
+
stderrWriteSpy.mockRestore();
|
|
1827
|
+
});
|
|
1828
|
+
});
|
|
1829
|
+
describe('outputResults branches', () => {
|
|
1830
|
+
it('should write JSON output when output path ends with .json', async () => {
|
|
1831
|
+
const outputFile = path_1.default.join(tmpDir, 'result.json');
|
|
1832
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputFile, '--rounds', '1']);
|
|
1833
|
+
expect(fs_1.default.existsSync(outputFile)).toBe(true);
|
|
1834
|
+
const content = JSON.parse(fs_1.default.readFileSync(outputFile, 'utf-8'));
|
|
1835
|
+
expect(content).toHaveProperty('id');
|
|
1836
|
+
});
|
|
1837
|
+
it('should write text output when output path does not end with .json', async () => {
|
|
1838
|
+
const outputFile = path_1.default.join(tmpDir, 'result.txt');
|
|
1839
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--output', outputFile, '--rounds', '1']);
|
|
1840
|
+
expect(fs_1.default.existsSync(outputFile)).toBe(true);
|
|
1841
|
+
const content = fs_1.default.readFileSync(outputFile, 'utf-8');
|
|
1842
|
+
expect(content).toContain(MOCK_SOLUTION_TEXT);
|
|
1843
|
+
});
|
|
1844
|
+
it('should write to stdout when no output path is provided', async () => {
|
|
1845
|
+
const capturedStdout = [];
|
|
1846
|
+
const stdoutWriteSpy = jest.spyOn(process.stdout, 'write').mockImplementation((chunk) => {
|
|
1847
|
+
capturedStdout.push(String(chunk));
|
|
1848
|
+
return true;
|
|
1849
|
+
});
|
|
1850
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1851
|
+
const stdout = capturedStdout.join('');
|
|
1852
|
+
expect(stdout).toContain(MOCK_SOLUTION_TEXT);
|
|
1853
|
+
stdoutWriteSpy.mockRestore();
|
|
1854
|
+
});
|
|
1855
|
+
it('should show verbose summary when no output path and verbose is true', async () => {
|
|
1856
|
+
const capturedStderr = [];
|
|
1857
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1858
|
+
capturedStderr.push(String(chunk));
|
|
1859
|
+
return true;
|
|
1860
|
+
});
|
|
1861
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
1862
|
+
const stderr = capturedStderr.join('');
|
|
1863
|
+
expect(stderr).toContain('Summary (verbose)');
|
|
1864
|
+
stderrWriteSpy.mockRestore();
|
|
1865
|
+
});
|
|
1866
|
+
it('should not show verbose summary when output path is provided', async () => {
|
|
1867
|
+
const outputFile = path_1.default.join(tmpDir, 'result.txt');
|
|
1868
|
+
const capturedStderr = [];
|
|
1869
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
1870
|
+
capturedStderr.push(String(chunk));
|
|
1871
|
+
return true;
|
|
1872
|
+
});
|
|
1873
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--output', outputFile, '--rounds', '1']);
|
|
1874
|
+
const stderr = capturedStderr.join('');
|
|
1875
|
+
expect(stderr).not.toContain('Summary (verbose)');
|
|
1876
|
+
stderrWriteSpy.mockRestore();
|
|
1877
|
+
});
|
|
1878
|
+
});
|
|
1879
|
+
describe('generateReport error handling', () => {
|
|
1880
|
+
it('should handle debate state not found error', async () => {
|
|
1881
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
1882
|
+
const originalGetDebate = dialectic_core_1.StateManager.prototype.getDebate;
|
|
1883
|
+
dialectic_core_1.StateManager.prototype.getDebate = jest.fn().mockResolvedValue(undefined);
|
|
1884
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--report', reportPath, '--rounds', '1']);
|
|
1885
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to generate report'));
|
|
1886
|
+
dialectic_core_1.StateManager.prototype.getDebate = originalGetDebate;
|
|
1887
|
+
});
|
|
1888
|
+
it('should handle report generation failure gracefully', async () => {
|
|
1889
|
+
const reportPath = path_1.default.join(tmpDir, 'report.md');
|
|
1890
|
+
mockedGenerateDebateReport.mockImplementationOnce(() => {
|
|
1891
|
+
throw new Error('Report generation failed');
|
|
1892
|
+
});
|
|
1893
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--report', reportPath, '--rounds', '1']);
|
|
1894
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to generate report'));
|
|
1895
|
+
mockedGenerateDebateReport.mockImplementation(jest.requireActual('dialectic-core').generateDebateReport);
|
|
1896
|
+
});
|
|
1482
1897
|
});
|
|
1898
|
+
describe('extractProblemFileName and extractContextFileName', () => {
|
|
1899
|
+
it('should extract problem file name when provided', async () => {
|
|
1900
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1901
|
+
fs_1.default.writeFileSync(problemFile, 'Problem content');
|
|
1902
|
+
await (0, index_1.runCli)(['debate', '--problemDescription', problemFile, '--rounds', '1']);
|
|
1903
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1904
|
+
});
|
|
1905
|
+
it('should return undefined when problem file name is not provided', async () => {
|
|
1906
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1907
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1908
|
+
});
|
|
1909
|
+
it('should extract context directory name when provided', async () => {
|
|
1910
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1911
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1912
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1913
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1914
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1915
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1916
|
+
trace: 'langfuse',
|
|
1917
|
+
});
|
|
1918
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1919
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--config', configPath, '--rounds', '1']);
|
|
1920
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1921
|
+
});
|
|
1922
|
+
it('should return undefined when context file name is not provided', async () => {
|
|
1923
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
1924
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1925
|
+
});
|
|
1926
|
+
});
|
|
1927
|
+
describe('initializeTracingContext branches', () => {
|
|
1928
|
+
it('should return undefined when trace is not LANGFUSE', async () => {
|
|
1929
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1930
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1931
|
+
trace: 'none',
|
|
1932
|
+
});
|
|
1933
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1934
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1935
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1936
|
+
});
|
|
1937
|
+
it('should include problemFileName in metadata when provided', async () => {
|
|
1938
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1939
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1940
|
+
const problemFile = path_1.default.join(tmpDir, 'problem.txt');
|
|
1941
|
+
fs_1.default.writeFileSync(problemFile, 'Design a system');
|
|
1942
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1943
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1944
|
+
trace: 'langfuse',
|
|
1945
|
+
});
|
|
1946
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1947
|
+
await (0, index_1.runCli)(['debate', '--problemDescription', problemFile, '--config', configPath, '--rounds', '1']);
|
|
1948
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1949
|
+
});
|
|
1950
|
+
it('should not include problemFileName in metadata when not provided', async () => {
|
|
1951
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1952
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1953
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1954
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1955
|
+
trace: 'langfuse',
|
|
1956
|
+
});
|
|
1957
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1958
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1959
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1960
|
+
});
|
|
1961
|
+
it('should include contextFileName in metadata when provided', async () => {
|
|
1962
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1963
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1964
|
+
const contextDir = path_1.default.join(tmpDir, 'context');
|
|
1965
|
+
fs_1.default.mkdirSync(contextDir, { recursive: true });
|
|
1966
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1967
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1968
|
+
trace: 'langfuse',
|
|
1969
|
+
});
|
|
1970
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1971
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--context', contextDir, '--config', configPath, '--rounds', '1']);
|
|
1972
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1973
|
+
});
|
|
1974
|
+
it('should not include contextFileName in metadata when not provided', async () => {
|
|
1975
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1976
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1977
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1978
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1979
|
+
trace: 'langfuse',
|
|
1980
|
+
});
|
|
1981
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1982
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1983
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1984
|
+
});
|
|
1985
|
+
it('should include judgeConfig in metadata when judge exists', async () => {
|
|
1986
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1987
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1988
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
1989
|
+
const configContent = createTestConfigContent(undefined, {
|
|
1990
|
+
trace: 'langfuse',
|
|
1991
|
+
});
|
|
1992
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
1993
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
1994
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
1995
|
+
});
|
|
1996
|
+
it('should not include judgeConfig in metadata when judge does not exist', async () => {
|
|
1997
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
1998
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
1999
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2000
|
+
const configContent = {
|
|
2001
|
+
agents: [createTestAgentConfig()],
|
|
2002
|
+
debate: createTestDebateConfig(),
|
|
2003
|
+
};
|
|
2004
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2005
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2006
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2007
|
+
});
|
|
2008
|
+
});
|
|
2009
|
+
describe('outputVerboseDebateInfo branches', () => {
|
|
2010
|
+
it('should return early when verbose is false', async () => {
|
|
2011
|
+
const capturedStderr = [];
|
|
2012
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
2013
|
+
capturedStderr.push(String(chunk));
|
|
2014
|
+
return true;
|
|
2015
|
+
});
|
|
2016
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2017
|
+
const stderr = capturedStderr.join('');
|
|
2018
|
+
expect(stderr).not.toContain('Running debate (verbose)');
|
|
2019
|
+
stderrWriteSpy.mockRestore();
|
|
2020
|
+
});
|
|
2021
|
+
it('should output verbose info when verbose is true', async () => {
|
|
2022
|
+
const capturedStderr = [];
|
|
2023
|
+
const stderrWriteSpy = jest.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
|
|
2024
|
+
capturedStderr.push(String(chunk));
|
|
2025
|
+
return true;
|
|
2026
|
+
});
|
|
2027
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--verbose', '--rounds', '1']);
|
|
2028
|
+
const stderr = capturedStderr.join('');
|
|
2029
|
+
expect(stderr).toContain('Running debate (verbose)');
|
|
2030
|
+
stderrWriteSpy.mockRestore();
|
|
2031
|
+
});
|
|
2032
|
+
});
|
|
2033
|
+
describe('flushTracingContext error handling', () => {
|
|
2034
|
+
it('should handle flush errors gracefully', async () => {
|
|
2035
|
+
process.env.LANGFUSE_SECRET_KEY = 'test-secret-key';
|
|
2036
|
+
process.env.LANGFUSE_PUBLIC_KEY = 'test-public-key';
|
|
2037
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2038
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2039
|
+
trace: 'langfuse',
|
|
2040
|
+
});
|
|
2041
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2042
|
+
const { createTracingContext } = require('dialectic-core');
|
|
2043
|
+
const originalCreateTracingContext = createTracingContext;
|
|
2044
|
+
jest.spyOn(require('dialectic-core'), 'createTracingContext').mockImplementation((...args) => {
|
|
2045
|
+
const context = originalCreateTracingContext(...args);
|
|
2046
|
+
if (context) {
|
|
2047
|
+
context.langfuse.flushAsync = jest.fn().mockRejectedValue(new Error('Flush failed'));
|
|
2048
|
+
}
|
|
2049
|
+
return context;
|
|
2050
|
+
});
|
|
2051
|
+
const logWarningSpy = jest.spyOn(require('dialectic-core'), 'logWarning');
|
|
2052
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2053
|
+
expect(logWarningSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to flush Langfuse trace'));
|
|
2054
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2055
|
+
logWarningSpy.mockRestore();
|
|
2056
|
+
jest.restoreAllMocks();
|
|
2057
|
+
});
|
|
2058
|
+
});
|
|
2059
|
+
describe('generateReportIfRequested', () => {
|
|
2060
|
+
it('should return early when report is not requested', async () => {
|
|
2061
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2062
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2063
|
+
});
|
|
2064
|
+
});
|
|
2065
|
+
describe('getSystemSummaryConfig branches', () => {
|
|
2066
|
+
it('should use default summarization config when not provided', async () => {
|
|
2067
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2068
|
+
const configContent = {
|
|
2069
|
+
agents: [createTestAgentConfig()],
|
|
2070
|
+
debate: {
|
|
2071
|
+
rounds: 3,
|
|
2072
|
+
terminationCondition: { type: 'fixed' },
|
|
2073
|
+
synthesisMethod: 'judge',
|
|
2074
|
+
includeFullHistory: true,
|
|
2075
|
+
timeoutPerRound: 300000,
|
|
2076
|
+
},
|
|
2077
|
+
judge: {
|
|
2078
|
+
id: 'test-judge',
|
|
2079
|
+
name: 'Test Judge',
|
|
2080
|
+
role: 'generalist',
|
|
2081
|
+
model: 'gpt-4',
|
|
2082
|
+
provider: 'openai',
|
|
2083
|
+
temperature: 0.3,
|
|
2084
|
+
},
|
|
2085
|
+
};
|
|
2086
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2087
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2088
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2089
|
+
});
|
|
2090
|
+
it('should use provided summarization config when available', async () => {
|
|
2091
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2092
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2093
|
+
summarization: {
|
|
2094
|
+
enabled: false,
|
|
2095
|
+
threshold: 3000,
|
|
2096
|
+
maxLength: 1500,
|
|
2097
|
+
method: 'length-based',
|
|
2098
|
+
},
|
|
2099
|
+
});
|
|
2100
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2101
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2102
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2103
|
+
});
|
|
2104
|
+
});
|
|
2105
|
+
describe('isClarificationRequested branches', () => {
|
|
2106
|
+
it('should return true when options.clarify is true', async () => {
|
|
2107
|
+
mockReadlineWithAnswers([]);
|
|
2108
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2109
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--clarify', '--rounds', '1']);
|
|
2110
|
+
expect(mockedCollectClarifications).toHaveBeenCalled();
|
|
2111
|
+
});
|
|
2112
|
+
it('should return true when sysConfig.debate.interactiveClarifications is true', async () => {
|
|
2113
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2114
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2115
|
+
interactiveClarifications: true,
|
|
2116
|
+
});
|
|
2117
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2118
|
+
mockReadlineWithAnswers([]);
|
|
2119
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2120
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2121
|
+
expect(mockedCollectClarifications).toHaveBeenCalled();
|
|
2122
|
+
});
|
|
2123
|
+
it('should return false when neither option is set', async () => {
|
|
2124
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2125
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2126
|
+
});
|
|
2127
|
+
});
|
|
2128
|
+
describe('collectFinalClarifications', () => {
|
|
2129
|
+
it('should return undefined when clarification is not requested', async () => {
|
|
2130
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--rounds', '1']);
|
|
2131
|
+
expect(stdoutSpy).toHaveBeenCalled();
|
|
2132
|
+
});
|
|
2133
|
+
it('should use custom maxPerAgent when provided', async () => {
|
|
2134
|
+
const configPath = getTestConfigPath(tmpDir);
|
|
2135
|
+
const configContent = createTestConfigContent(undefined, {
|
|
2136
|
+
interactiveClarifications: true,
|
|
2137
|
+
clarificationsMaxPerAgent: 3,
|
|
2138
|
+
});
|
|
2139
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(configContent, null, 2));
|
|
2140
|
+
mockReadlineWithAnswers([]);
|
|
2141
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2142
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--config', configPath, '--rounds', '1']);
|
|
2143
|
+
expect(mockedCollectClarifications).toHaveBeenCalledWith(expect.any(String), expect.any(Array), 3, expect.any(Function));
|
|
2144
|
+
});
|
|
2145
|
+
it('should use default maxPerAgent when not provided', async () => {
|
|
2146
|
+
mockReadlineWithAnswers([]);
|
|
2147
|
+
mockedCollectClarifications.mockResolvedValueOnce([]);
|
|
2148
|
+
await (0, index_1.runCli)(['debate', 'Design a system', '--clarify', '--rounds', '1']);
|
|
2149
|
+
expect(mockedCollectClarifications).toHaveBeenCalledWith(expect.any(String), expect.any(Array), 5, expect.any(Function));
|
|
2150
|
+
});
|
|
2151
|
+
});
|
|
2152
|
+
function mockReadlineWithAnswers(answers) {
|
|
2153
|
+
const readlineModule = require('readline');
|
|
2154
|
+
if (readlineModule.__setMockAnswers) {
|
|
2155
|
+
readlineModule.__setMockAnswers(answers);
|
|
2156
|
+
}
|
|
2157
|
+
else {
|
|
2158
|
+
mockAnswers = [...answers];
|
|
2159
|
+
currentIndex = 0;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
1483
2162
|
});
|
|
1484
2163
|
});
|
|
1485
2164
|
//# sourceMappingURL=debate.spec.js.map
|