返回 CodeWhale
activation-cleanup-host.test.mjs
根目录 / crates / tui / extension-host / test / activation-cleanup-host.test.mjs
1 // Real Node host/wire; fake Core authority. Production cleanup regression receipts.
2 import test from 'node:test'
3 import assert from 'node:assert/strict'
4 import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
5 import { tmpdir } from 'node:os'
6 import { join } from 'node:path'
7 import { startHost, owner, sha256File } from './harness.mjs'
8 function entry(dir, name, source) { const path = join(dir, name + '.mjs'); writeFileSync(path, source); return { path, sha256: sha256File(path) } }
9 async function setup(t) { const dir = mkdtempSync(join(tmpdir(), 'cw-activation-cleanup-')); const host = await startHost(); t.after(async () => { await host.stop(); rmSync(dir, {recursive:true,force:true}) }); return {dir,host,ref:owner('native-cleanup')} }
10 const scoped = (ref, path) => ({owner:ref,plugin_name:'native-cleanup',entry:path,scope:path,config:{}})
11
12 test('scoped failure preserves original refusal and a working sibling', async t => {
13 const {dir,host,ref}=await setup(t)
14 const good=entry(dir,'good',`export const inject=['tools']; export function apply(ctx){ctx.tools.register({name:'sibling',description:'live',parameters:{type:'object'},execute:()=> 'sibling live'})}`)
15 const bad=entry(dir,'bad',`export function apply(ctx){ctx.provide('approval',{})}`)
16 assert.equal((await host.call('ext/activate',scoped(ref,good))).status,'ok')
17 const result=await host.call('ext/activate',scoped(ref,bad)); assert.equal(result.status,'failed'); assert.match(result.diagnostic,/approval/)
18 assert.deepEqual(await host.call('ext/deactivate',{owner:ref,entry:bad}),{disposed:true,leaked:[]})
19 const registration=host.registry.find(row=>row.op==='register' && row.spec.name==='sibling')
20 const answer=await host.call('tool/call',{handle:registration.handle,call_id:'sibling-call',input:{},deadline_ms:1000})
21 assert.equal(answer.content[0].text,'sibling live')
22 })
23 test('failed scoped activation retains incomplete disposer for teardown receipt', {timeout:15000}, async t => {
24 const {dir,host,ref}=await setup(t)
25 const bad=entry(dir,'dirty',`export function apply(ctx){ctx.effect(()=>()=>new Promise(()=>{}),'unfinished failed activation');ctx.provide('approval',{})}`)
26 const result=await host.call('ext/activate',scoped(ref,bad)); assert.equal(result.status,'failed')
27 const receipt=await host.call('ext/deactivate',{owner:ref,entry:bad}); assert.equal(receipt.disposed,false,'a missing owner must not manufacture clean teardown')
28 assert.deepEqual(await host.call('ext/deactivate',{owner:ref}),{disposed:true,leaked:[]})
29 })
30 test('withdrawal during async activation cannot return late success', async t => {
31 const {dir,host,ref}=await setup(t)
32 const late=entry(dir,'late',`export async function apply(ctx){ctx.logger.info('late activation reached await');await new Promise(resolve=>setTimeout(resolve,100));}`)
33 const begun=host.request('ext/activate',scoped(ref,late))
34 await host.waitFor(message=>message.method==='log' && JSON.stringify(message.params).includes('late activation reached await'))
35 await host.call('ext/deactivate',{owner:ref,entry:late})
36 const answer=await begun.promise; assert.equal(answer.status,'failed'); assert.match(answer.diagnostic,/withdrawn/)
37 })
38
38 lines Plain Text