Files
shanxia2.0/miniprogram/scripts/test-contract-document.mjs
T

99 lines
4.8 KiB
JavaScript

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('<script setup>')[1].split('</script>')[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/)
})