42 lines
3.5 KiB
JavaScript
42 lines
3.5 KiB
JavaScript
/** Own ASAR fixture exercises the official local REA provider/public CLI only. */
|
|
import assert from 'node:assert/strict'
|
|
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import crypto from 'node:crypto'
|
|
import { createRequire } from 'node:module'
|
|
import { pathToFileURL } from 'node:url'
|
|
import { spawnSync } from 'node:child_process'
|
|
const root = await fs.mkdtemp(path.resolve('.tmp/rea-expert-package-fixture-'))
|
|
const requireRuntime = createRequire(path.resolve('.tmp/rea-css-runtime/package.json'))
|
|
const {createPackageWithOptions} = await import(pathToFileURL(requireRuntime.resolve('@electron/asar')).href)
|
|
const digest = bytes => crypto.createHash('sha256').update(bytes).digest('hex')
|
|
const source = path.join(root,'source'), archive = path.join(root,'fixture.asar')
|
|
const files = {'expert/SKILL.md':'---\nname: local-test\n---\n# 本地专家\n', 'expert/schema.yaml':'name: local-test\n',
|
|
'expert/schema.yml':'version: 1\n','expert/README.txt':'strict UTF-8\n', 'expert/register.py':'# retained, never executed\nprint("中文")\n', 'expert/bad.md':Buffer.from([0xc3,0x28]),
|
|
'expert/large.txt':Buffer.alloc(8*1024*1024+1,97),'expert/one.md':Buffer.alloc(8*1024*1024,97),
|
|
'expert/two.md':Buffer.alloc(8*1024*1024,98),'expert/extra.txt':'x','expert/unselected.md':'not selected'}
|
|
for(const [p,content] of Object.entries(files)){await fs.mkdir(path.dirname(path.join(source,p)),{recursive:true});await fs.writeFile(path.join(source,p),content)}
|
|
await createPackageWithOptions(source,archive,{unpackDir:'expert'})
|
|
await fs.rm(path.join(archive+'.unpacked','expert/unselected.md'))
|
|
const pin=digest(await fs.readFile(archive)), checks=[]
|
|
function cli(paths,expected=pin){return spawnSync(process.execPath,['.tmp/rea-css-runtime/scripts/rea.mjs','export-selected-artifact-text',archive,expected,JSON.stringify(paths)],{encoding:'utf8',maxBuffer:32*1024*1024})}
|
|
function reject(label,paths,pattern,expected=pin){const r=cli(paths,expected);assert.notEqual(r.status,0,label);assert.match(r.stderr,pattern,label);checks.push({label,pass:true})}
|
|
const selected=Object.keys(files).slice(0,5), r=cli(selected)
|
|
assert.equal(r.status,0,r.stderr)
|
|
const evidence=JSON.parse(r.stdout)
|
|
assert.equal(evidence.normalized_result.root_sha256,pin)
|
|
assert.deepEqual(evidence.normalized_result.files.map(f=>f.path),selected)
|
|
for(const f of evidence.normalized_result.files){assert.equal(f.text,files[f.path]);assert.equal(f.sha256,digest(files[f.path]));assert.equal(f.size_bytes,Buffer.byteLength(files[f.path]))}
|
|
checks.push({label:'five new suffixes, exact bytes/SHA/root and missing unselected unpacked ignored; Python not executed',pass:true})
|
|
reject('root mismatch',selected,/changed before interpretation/,'0'.repeat(64))
|
|
reject('canonical selection',['../expert/SKILL.md'],/not canonical/)
|
|
reject('unknown suffix',['expert/private.db'],/supported UTF-8/)
|
|
reject('strict UTF-8',['expert/bad.md'],/strict UTF-8/)
|
|
reject('per-file budget',['expert/large.txt'],/per-file budget/)
|
|
reject('aggregate budget',['expert/one.md','expert/two.md','expert/extra.txt'],/aggregate budget/)
|
|
reject('absent selected member',['expert/absent.md'],/absent from/)
|
|
reject('duplicate selected member',['expert/SKILL.md','expert/SKILL.md'],/Duplicate selected path/)
|
|
await fs.writeFile(path.join(root,'evidence.json'),JSON.stringify(evidence,null,2))
|
|
await fs.writeFile(path.join(root,'report.json'),JSON.stringify({result:'PASS',rootPin:pin,evidenceId:evidence.evidence_id,checks},null,2))
|
|
console.log(JSON.stringify({result:'PASS',root,checks:checks.length,evidenceId:evidence.evidence_id}))
|