Files
ycode/scripts/test-expert-preloading.mjs
T

48 lines
6.6 KiB
JavaScript

/** Focused production-module regression using only explicitly test-owned expert packages. */
import fs from 'node:fs/promises'
import path from 'node:path'
import assert from 'node:assert/strict'
import {createHash} from 'node:crypto'
import {pathToFileURL} from 'node:url'
import {build} from 'esbuild'
const output=await fs.mkdtemp(path.resolve('.tmp/imported-expert-qa-preloading-')),userData=path.join(output,'YCode')
await fs.mkdir(userData)
await fs.writeFile(path.join(userData,'mcp-servers.json'),JSON.stringify({version:1,servers:[]}))
const source='src/main/capabilities/experts.ts',sha=value=>createHash('sha256').update(value).digest('hex'),sourceSha256=sha(await fs.readFile(source))
await build({stdin:{contents:`export * from './src/main/capabilities/experts';`,resolveDir:process.cwd(),loader:'ts'},outfile:path.join(output,'experts.mjs'),bundle:true,platform:'node',format:'esm',packages:'external',logLevel:'warning',plugins:[{name:'own-profile',setup(builder){builder.onResolve({filter:/^electron$/},()=>({path:'electron',namespace:'qa'}));builder.onLoad({filter:/.*/,namespace:'qa'},()=>({contents:`export const app={getPath:()=>${JSON.stringify(userData)}};export const safeStorage={isEncryptionAvailable:()=>false};`}))}}]})
const api=await import(pathToFileURL(path.join(output,'experts.mjs')).href),checks=[]
function check(label,actual,expected=true){assert.deepEqual(actual,expected,label);checks.push({label,pass:true})}
async function rejected(label,run,pattern){await assert.rejects(run,pattern,label);checks.push({label,pass:true})}
const skill=(name,body)=>`---\nname: ${name}\ndescription: Controlled test skill ${name}\n---\n${body}\n`
async function install(id,preload,options={}){
const plugin={name:id,version:'1.0.0',description:'Explicit test-owned local role fixture',expertType:'agent',agentName:id,agents:[`./agents/${id}.md`],skills:['./skills/directory-alias','./skills/second-directory'],displayName:{zh:'测试角色',en:'Test Role'},profession:{zh:'测试角色',en:'Test Role'},displayDescription:{zh:'测试角色',en:'Test Role'},defaultInitPrompt:{zh:'测试输入',en:'Test input'},tags:[1,2,3].map(n=>({zh:'标签'+n,en:'Tag '+n})),quickPrompts:[{zh:'测试输入',en:'Test input'},...[2,3].map(n=>({zh:'测试'+n,en:'Test '+n}))]}
const first=skill(options.unsafeName??'real-skill','FIRST_BODY_SENTINEL'),second=skill(options.duplicateName?'real-skill':'other-skill','SECOND_BODY_SENTINEL')
const agent=`---\nname: ${id}\ndescription: Explicit test-owned role\n${preload===undefined?'':'skills: '+JSON.stringify(preload)+'\n'}---\nTEST_ROLE_SENTINEL\n`
const files={'.codebuddy-plugin/plugin.json':JSON.stringify(plugin),[`agents/${id}.md`]:agent,'skills/directory-alias/SKILL.md':first,'skills/second-directory/SKILL.md':second,'skills/unregistered/SKILL.md':skill('not-registered','UNREGISTERED_BODY_SENTINEL')}
await api.expertPackageOperation('test-'+id,{id,action:'write',files});const result=await api.expertPackageOperation('test-'+id,{id,action:'register'});assert.equal(result.success,true)
return{files,first,second}
}
try{
const absent=await install('absent-skills',undefined),absentTurn=await api.expertTurnContext('absent-skills')
check('missing Agent skills injects no registered skill full body',absentTurn.context.includes(absent.first)||absentTurn.context.includes(absent.second),false)
check('missing Agent skills still exposes both registered skill names',absentTurn.context.includes('"name":"real-skill"')&&absentTurn.context.includes('"name":"other-skill"'))
check('all original resources remain available including nonregistered text',Object.keys(absentTurn.resources).sort(),Object.keys(absent.files).sort().concat('.created-by-session').sort())
const named=await install('named-skills',['real-skill']),namedTurn=await api.expertTurnContext('named-skills')
check('selection resolves SKILL frontmatter name rather than directory alias',namedTurn.context.includes(named.first))
check('unselected registered skill is not silently preloaded',namedTurn.context.includes(named.second),false)
check('directory explicitly marks selected and unselected state',namedTurn.context.includes('"path":"skills/directory-alias/SKILL.md","description":"Controlled test skill real-skill","preloaded":true')&&namedTurn.context.includes('"path":"skills/second-directory/SKILL.md","description":"Controlled test skill other-skill","preloaded":false'))
const duplicate=await install('duplicate-selection',['real-skill','real-skill']),duplicateTurn=await api.expertTurnContext('duplicate-selection')
check('duplicate YAML skill names preload one full body',duplicateTurn.context.split(duplicate.first).length-1,1)
await install('unknown-selection',['not-registered']);await rejected('existing but unregistered skill cannot be preloaded',()=>api.expertTurnContext('unknown-selection'),/未在 plugin.skills 注册/)
await install('inherited-selection',['constructor']);await rejected('inherited property cannot resolve as a registered skill',()=>api.expertTurnContext('inherited-selection'),/未在 plugin.skills 注册/)
await install('traversal-selection',['../real-skill']);await rejected('Agent skill path is not accepted as a legal name',()=>api.expertTurnContext('traversal-selection'),/合法技能名称/)
await install('scalar-selection','real-skill');await rejected('scalar Agent skills is rejected rather than all-loaded',()=>api.expertTurnContext('scalar-selection'),/合法技能名称/)
await install('ambiguous-selection',['real-skill'],{duplicateName:true});await rejected('ambiguous registered skill name cannot choose arbitrary directory',()=>api.expertTurnContext('ambiguous-selection'),/名称不唯一/)
await install('empty-selection',[]);check('explicit empty Agent skills has no preloads',(await api.expertTurnContext('empty-selection')).context.includes('<expert-skill path='),false)
await install('invalid-skill-name',undefined,{unsafeName:'../unsafe'});await rejected('registered Skill metadata cannot introduce a path name',()=>api.expertTurnContext('invalid-skill-name'),/技能名称无效/)
check('production source stays unchanged during focused test',sha(await fs.readFile(source)),sourceSha256)
await fs.writeFile(path.join(output,'report.json'),JSON.stringify({result:'PASS',checks,source,sourceSha256,limitations:'Only host preload semantics; no model request or expert business script executed.'},null,2))
console.log(JSON.stringify({result:'PASS',checks:checks.length,output}))
}catch(error){await fs.writeFile(path.join(output,'report.json'),JSON.stringify({result:'FAIL',checks,error:error.stack,source,sourceSha256},null,2));console.error(JSON.stringify({result:'FAIL',error:error.message,output}));process.exitCode=1}