返回 CodeWhale
harness.test.mjs
根目录 / crates / tui / extension-host / test / harness.test.mjs
1 import test from 'node:test'
2 import assert from 'node:assert/strict'
3 import { createHarnessModule, normalizeOutput } from '../dist/builtin/harness.mjs'
4 const owner = { plugin_id: 'host:harness', generation: 3, owner_token: 'fixture-token' }
5 const params = (execution_id = 'one', extra = {}) => ({ owner, execution_id, ticket: 'opaque-exact-fixture-ticket', deadline_ms: 1000, ...extra })
6 const signal = () => new AbortController().signal
7 const tick = () => new Promise(resolve => setImmediate(resolve))
8
9 test('script result JSON preserves soft failure and metadata', () => {
10 assert.deepEqual(normalizeOutput({ success: true, stdout: JSON.stringify({ content: 'soft', success: false, metadata: { structured: [1, 2] } }), stderr: '' }), { ok: true, result: { content: 'soft', success: false, metadata: { structured: [1, 2] } } })
11 })
12 test('plain and non-ToolResult JSON retain original stdout', () => {
13 for (const stdout of ['hello\n', '{"other":true}', 'null']) assert.deepEqual(normalizeOutput({ success: true, stdout, stderr: '' }), { ok: true, result: { content: stdout, success: true } })
14 })
15 test('nonzero process exit keeps both streams in failure', () => { assert.deepEqual(normalizeOutput({ success: false, stdout: 'out', stderr: 'err' }), { ok: false, error: 'out\nerr' }) })
16 test('real Node Builtin redeems only opaque exact operation fields', async () => {
17 const calls = []; const runner = createHarnessModule({ async request(method, args) { calls.push([method, args]); return { success: true, stdout: 'ok', stderr: '' } } }, owner)
18 assert.equal((await runner.run(params(), signal())).result.content, 'ok')
19 assert.deepEqual(calls, [['exec/redeem', { owner, execution_id: 'one', ticket: 'opaque-exact-fixture-ticket' }]])
20 await runner.dispose()
21 })
22 test('wrong owner, generation and token never reach Rust', async () => {
23 let calls = 0; const runner = createHarnessModule({ async request() { calls++; return {} } }, owner)
24 for (const wrong of [{ ...owner, plugin_id: 'native' }, { ...owner, generation: 4 }, { ...owner, owner_token: 'old' }]) await assert.rejects(runner.run(params('one', { owner: wrong }), signal()), /owner is stale/)
25 assert.equal(calls, 0); await runner.dispose()
26 })
27 test('duplicate pending operation refuses without redeeming twice', async () => {
28 let finish; let calls = 0; const runner = createHarnessModule({ request() { calls++; return new Promise(resolve => { finish = resolve }) } }, owner)
29 const first = runner.run(params(), signal()); await tick()
30 await assert.rejects(runner.run(params(), signal()), /not admitted/)
31 finish({ success: true, stdout: '', stderr: '' }); await first; assert.equal(calls, 1); await runner.dispose()
32 })
33 test('caller cancel settles even if a fake Rust peer ignores abort', async () => {
34 const controller = new AbortController(); let remoteSignal
35 const runner = createHarnessModule({ request(_method, _params, supplied) { remoteSignal = supplied; return new Promise(() => {}) } }, owner)
36 const pending = runner.run(params(), controller.signal); controller.abort()
37 await assert.rejects(pending, /cancelled/); assert.equal(remoteSignal.aborted, true); await runner.dispose()
38 })
39 test('owner disposal cancels admitted work and refuses reuse', async () => {
40 const runner = createHarnessModule({ request() { return new Promise(() => {}) } }, owner)
41 const pending = runner.run(params(), signal()); const rejected = assert.rejects(pending, /cancelled/)
42 await runner.dispose(); await rejected; await assert.rejects(runner.run(params('two'), signal()), /stale/)
43 })
44 test('deadline reaches broker cancellation without a second execution', async () => {
45 let remote; const runner = createHarnessModule({ request(_method, _params, supplied) { remote = supplied; return new Promise(() => {}) } }, owner)
46 await assert.rejects(runner.run(params('deadline', { deadline_ms: 5 }), signal()), /cancelled/); assert.equal(remote.aborted, true); await runner.dispose()
47 })
48 test('runner refuses malformed and oversized output without truncating', () => {
49 for (const output of [{ success: true, stdout: 1, stderr: '' }, { success: true, stdout: 'x'.repeat(1024 * 1024 + 1), stderr: '' }, { success: true, stdout: '', stderr: 'x'.repeat(64 * 1024 + 1) }]) assert.throws(() => normalizeOutput(output), /invalid or oversized/)
50 })
51 test('bounded pending operations cancel completely on disposal', async () => {
52 let count = 0; const runner = createHarnessModule({ request() { count++; return new Promise(() => {}) } }, owner)
53 const pending = Array.from({ length: 32 }, (_, i) => runner.run(params(String(i)), signal()).catch(error => error.message))
54 await assert.rejects(runner.run(params('overflow'), signal()), /not admitted/); assert.equal(count, 32)
55 await runner.dispose(); assert.equal((await Promise.all(pending)).every(value => value === 'execution cancelled'), true)
56 })
57
57 lines Plain Text