import test from 'node:test' import assert from 'node:assert/strict' import { existsSync } from 'node:fs' const file = new URL('../utils/contract-document.js', import.meta.url) test('document links only accept configured storage, PDF/DOCX and safe boundaries', async () => { assert.ok(existsSync(file), 'document validator exists') const { validateContractDocument } = await import(file) const base = 'https://res.example.com/ruoyi' assert.equal(validateContractDocument({url: base+'/a.pdf?signature=x', type:'pdf'}, base).type, 'pdf') for (const url of ['https://evil.com/a.pdf', base+'evil/a.pdf', 'https://res.example.com/ruoyi/../secret', 'javascript:alert(1)', 'https://res.example.com@evil.com/ruoyi/a.pdf']) { assert.throws(() => validateContractDocument({url,type:'pdf'},base)) } assert.throws(() => validateContractDocument({url:base+'/a.exe',type:'exe'},base)) }) import vm from 'node:vm' import { readFileSync } from 'node:fs' async function pageHarness() { const { validateContractDocument, contractDownloadErrorMessage } = await import(file) const source = readFileSync(new URL('../pages/common/contract-document.vue', import.meta.url),'utf8') .split('')[0].replace(/^import .*$/gm,'') const callbacks = {} const calls = {downloads: [], opens: [], removed: [], aborted:0} const context = vm.createContext({ ref: value => ({value}), onLoad: fn => callbacks.load = fn, onUnload: fn => callbacks.unload = fn, MINIO_BASE_URL: 'https://res.example.com/ruoyi', validateContractDocument, contractDownloadErrorMessage, uni: { downloadFile: options => {calls.downloads.push(options); return {abort() {calls.aborted++}}}, openDocument: options => calls.opens.push(options), getFileSystemManager: () => ({unlink: options => calls.removed.push(options.filePath)}), navigateBack() {} } }) vm.runInContext(source+'\nglobalThis.state = { busy, message, fileName, documentInfo, openDocument };',context) return {callbacks,calls,state: context.state} } const tick = () => new Promise(resolve=>setImmediate(resolve)) test('download, preview/share, retry and cleanup use local files without credentials', async () => { const h = await pageHarness() h.callbacks.load({url:'https://res.example.com/ruoyi/a.pdf',type:'pdf',name:'100%合同.pdf'}) assert.equal(h.state.fileName.value,'100%合同.pdf') assert.equal(h.calls.downloads[0].header,undefined) h.calls.downloads[0].fail() await tick() assert.equal(h.state.busy.value,false) void h.state.openDocument() h.calls.downloads[1].success({statusCode:200,tempFilePath:'/tmp/contract.pdf'}) await tick() assert.equal(h.calls.opens[0].filePath,'/tmp/contract.pdf') assert.equal(h.calls.opens[0].showMenu,true) h.calls.opens[0].success() await tick() h.callbacks.unload() assert.deepEqual(h.calls.removed,['/tmp/contract.pdf']) }) test('unload during download prevents opening and removes late temp files', async () => { const h=await pageHarness() h.callbacks.load({url:'https://res.example.com/ruoyi/a.pdf',type:'pdf'}) h.callbacks.unload() h.calls.downloads[0].success({statusCode:200,tempFilePath:'/tmp/late.pdf'}) await tick() assert.equal(h.calls.opens.length,0) assert.deepEqual(h.calls.removed,['/tmp/late.pdf']) }) test('invalid links never start download', async () => { const h=await pageHarness() h.callbacks.load({url:'https://evil.com/a.pdf',type:'pdf'}) assert.equal(h.calls.downloads.length,0) assert.equal(h.state.documentInfo.value,null) }) test('registering contract documents preserves the login launch page', () => { const pages = JSON.parse(readFileSync(new URL('../pages.json',import.meta.url),'utf8').replace(/^\s*\/\/.*$/gm, '')).pages assert.equal(pages[0].path,'pages/login/login') assert.equal(pages.filter(p => p.path === 'pages/common/contract-document').length,1) }) test('native domain rejection identifies the required downloadFile domain', async () => { const h = await pageHarness() h.callbacks.load({url:'https://res.example.com/ruoyi/a.pdf',type:'pdf'}) h.calls.downloads[0].fail({errMsg:'downloadFile:fail url not in domain list',errno:600002}) await tick() assert.match(h.state.message.value, /downloadFile 合法域名/) assert.match(h.state.message.value, /https:\/\/res\.example\.com/) assert.equal(h.calls.opens.length, 0) }) test('native download failures retain diagnostic details without signed URLs', async () => { const h = await pageHarness() h.callbacks.load({url:'https://res.example.com/ruoyi/a.pdf',type:'pdf'}) h.calls.downloads[0].fail({errMsg:'downloadFile:fail connection reset https://res.example.com/a.pdf?X-Amz-Signature=secret',errno:600001}) await tick() assert.match(h.state.message.value, /connection reset/) assert.match(h.state.message.value, /600001/) assert.doesNotMatch(h.state.message.value, /secret|X-Amz/) })