aboutsummaryrefslogtreecommitdiff
path: root/modules/test/test-delegation-reporting.js
blob: 7d4854ef475f604d841ba812e2e81a9d2d935ae4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// node modules/test/test-delegation-reporting.js
// A delegated task must report the worker's answer, and must report FAILURE when there is
// no answer. Three places used to substitute the literal "completed": delegateTask's
// outbox, the delegate_task tool, and orchestrate-parallel's per-sub-task summary.
const fs = require('fs'), path = require('path');
const ROOT = __dirname + '/../core';
const store = {};
global.localStorage = { getItem: k => (k in store ? store[k] : null), setItem: (k, v) => { store[k] = String(v); }, removeItem: k => { delete store[k]; } };
global.indexedDB = { open() { throw new Error('no idb'); }, deleteDatabase() { throw new Error('no idb'); } };
global.window = global;
global.addEventListener = () => {};

const AI = new Function(fs.readFileSync(path.join(ROOT, 'module-aiassist.js'), 'utf8') + '\nreturn WebX11AIAssistantModule;')();

let fails = 0, passes = 0;
const ok = (n, c, d) => { if (c) { passes++; return; } fails++; console.log('FAIL ' + n + (d ? ' — ' + d : '')); };

function mk(orch) {
    const stub = {
        print: () => {}, log: () => {}, setAiBusy: () => {}, markDirty: () => {},
        setPrompt: () => {}, clearInput: () => {}, scrollToBottom: () => {}, refreshPrompt: () => {},
        ollamaManager: null,
        fs: { mkdirRecursive: () => {}, writeFile: () => true, readFile: () => null, exists: () => false, list: () => [] },
        addEventListener: () => {}, removeEventListener: () => {},
    };
    stub.moduleManager = { modules: { OrchestratorModule: orch } };
    const ai = new AI();
    ai.terminal = stub;
    return ai;
}

(async () => {
    // D1/D2: an answer comes back as the answer, and success is true
    const withAnswer = mk({ delegateTask: async () => 'the worker answer' });
    const r1 = JSON.parse(await withAnswer.runTool('delegate_task', JSON.stringify({ task: 'do a thing' })));
    ok('D1 the answer is the worker answer', r1.result === 'the worker answer', JSON.stringify(r1));
    ok('D2 success is true when there is an answer', r1.success === true, JSON.stringify(r1.success));

    // D3/D4: no answer must NOT be reported as completed
    const silent = mk({ delegateTask: async () => '' });
    const r2 = JSON.parse(await silent.runTool('delegate_task', JSON.stringify({ task: 'do a thing' })));
    ok('D3 an empty answer reports success=false', r2.success === false, JSON.stringify(r2));
    ok('D4 an empty answer is not called "completed"', r2.result === null && /no answer/i.test(r2.error || ''), JSON.stringify(r2));

    // D5: undefined (an older orchestrator, or a rejected internal path) is also not success
    const undef = mk({ delegateTask: async () => undefined });
    const r3 = JSON.parse(await undef.runTool('delegate_task', JSON.stringify({ task: 'do a thing' })));
    ok('D5 undefined is a failure, not a completion', r3.success === false, JSON.stringify(r3));

    // D6: an object answer (the shape chat() now returns) is unwrapped, not stringified
    const objAnswer = mk({ delegateTask: async () => 'plain string from orchestrator' });
    const r4 = JSON.parse(await objAnswer.runTool('delegate_task', JSON.stringify({ task: 'x' })));
    ok('D6 the tool keeps the answer verbatim', r4.result === 'plain string from orchestrator', JSON.stringify(r4));

    console.log('test-delegation-reporting: ' + passes + ' passed, ' + fails + ' failed');
    process.exit(fails ? 1 : 0);
})();