| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 |
- import {test} from 'node:test'
- import assert from 'node:assert/strict'
- import {EventEmitter} from 'node:events'
- import {mkdtemp,readFile,writeFile,stat,rm} from 'node:fs/promises'
- import {tmpdir} from 'node:os'
- import {join} from 'node:path'
- import {randomUUID,randomBytes,createCipheriv,createDecipheriv} from 'node:crypto'
- import {DesktopRunnerManager} from './runner-manager.mjs'
- async function fixture(run){
- const home=await mkdtemp(join(tmpdir(),'yaoyao-native-runner-')),key=randomBytes(32),children=[]
- const encrypt=value=>{const iv=randomBytes(12),cipher=createCipheriv('aes-256-gcm',key,iv);const bytes=Buffer.concat([cipher.update(value),cipher.final()]);return Buffer.concat([iv,cipher.getAuthTag(),bytes])}
- const decrypt=bytes=>{const cipher=createDecipheriv('aes-256-gcm',key,bytes.subarray(0,12));cipher.setAuthTag(bytes.subarray(12,28));return Buffer.concat([cipher.update(bytes.subarray(28)),cipher.final()]).toString()}
- const manager=new DesktopRunnerManager({home,encrypt,decrypt,validate:value=>{if(value.protocol!==1)throw new Error('invalid');return value},fork:()=>{
- const child=new EventEmitter();child.messages=[]
- child.postMessage=value=>{child.messages.push(value);if(value.type==='shutdown')queueMicrotask(()=>child.emit('exit',0))}
- child.kill=()=>{queueMicrotask(()=>child.emit('exit',0));return true}
- children.push(child);queueMicrotask(()=>child.emit('spawn'));return child
- }})
- const config={protocol:1,runnerId:randomUUID(),token:randomBytes(32).toString('base64url'),serverURL:'https://fixture.invalid',hermesURL:'http://127.0.0.1:9119',allowedProfiles:['default'],artifactRoots:[]}
- const file=join(home,'download.json');await writeFile(file,JSON.stringify(config))
- try{await run({home,manager,children,config,file})}finally{await manager.stop();await rm(home,{recursive:true,force:true})}
- }
- test('stores only encrypted credentials and restarts from private IPC configuration',()=>fixture(async({manager,children,config,file})=>{
- await manager.importFile(file)
- const encrypted=await readFile(manager.path)
- assert.equal(encrypted.includes(config.token),false)
- assert.equal((await stat(manager.path)).mode&0o077,0)
- assert.deepEqual(children[0].messages.find(m=>m.type==='configure').config,config)
- await manager.stop();await manager.start()
- assert.equal(children.length,2)
- assert.deepEqual(children[1].messages.find(m=>m.type==='configure').config,config)
- await manager.forget()
- await assert.rejects(readFile(manager.path),{code:'ENOENT'})
- await manager.start();assert.equal(children.length,2)
- }))
- test('a Keychain failure preserves a running node and its previous encrypted configuration',()=>fixture(async({manager,children,file})=>{
- await manager.importFile(file);const encrypted=await readFile(manager.path)
- manager.options.encrypt=()=>{throw new Error('keychain unavailable')}
- await assert.rejects(manager.importFile(file),/keychain/)
- assert.equal(children[0].finished,undefined)
- assert.deepEqual(await readFile(manager.path),encrypted)
- }))
- test('a queued import cannot start a node after stop was requested',()=>fixture(async({manager,children,file})=>{
- const imported=manager.importFile(file),stopped=manager.stop()
- await Promise.all([imported,stopped])
- assert.equal(children.length,0)
- }))
|