Files
dsh-web/image/office-preview-smoke.mjs

48 lines
4.1 KiB
JavaScript

// Run inside the built image as UID 1000. No downloads or fixture dependencies.
import assert from 'node:assert/strict';
import { mkdtemp, writeFile, readFile, rm, readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { pathToFileURL } from 'node:url';
import { runProcess } from './native-office.mjs';
const installed = '/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-office-to-pdf/lib/';
const { createConverter } = await import(installed + 'dsh-native-office.mjs');
const { OfficeToPdf } = await import(installed + 'index.js');
assert.notEqual(process.getuid(), 0, 'smoke test must run as non-root');
const dir = await mkdtemp(join(tmpdir(), 'office-smoke-'));
const signal = new AbortController().signal;
const ns = 'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"';
const converter = await createConverter();
try {
await writeFile(join(dir, 'word.fodt'), '<?xml version="1.0"?><office:document ' + ns + ' office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text"><office:body><office:text><text:p>中文预览 Word conversion test</text:p></office:text></office:body></office:document>');
await writeFile(join(dir, 'slides.fodp'), '<?xml version="1.0"?><office:document ' + ns + ' office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.presentation"><office:body><office:presentation><draw:page draw:name="slide1"><draw:frame svg:x="2cm" svg:y="2cm" svg:width="20cm" svg:height="5cm"><draw:text-box><text:p>中文预览 PPT conversion test</text:p></draw:text-box></draw:frame></draw:page></office:presentation></office:body></office:document>');
for (const [source, format] of [['word.fodt', 'docx'], ['slides.fodp', 'pptx'], ['word.fodt', 'doc'], ['slides.fodp', 'ppt']]) {
await runProcess('/usr/bin/libreoffice', ['-env:UserInstallation=' + pathToFileURL(join(dir, 'fixture-profile')).href, '--headless', '--convert-to', format, '--outdir', dir, join(dir, source)], signal);
}
await Promise.all(['word.docx', 'slides.pptx', 'word.doc', 'slides.ppt'].map(async name => {
const start = performance.now();
const outputPath = join(dir, name + '.pdf');
const result = await converter.render({ inputPath: join(dir, name), outputPath });
assert.equal(result.backend, 'native-system');
assert.match((await readFile(outputPath)).subarray(0, 8).toString(), /^%PDF-/);
console.log(name + ': native PDF in ' + Math.round(performance.now() - start) + 'ms');
}));
const small = await createConverter({ maxOutputBytes: 1 });
await assert.rejects(small.render({ inputPath: join(dir, 'word.docx'), outputPath: join(dir, 'small.pdf') }), { code: 'output-too-large' });
await small.dispose();
const timeout = await createConverter({ timeoutMs: 1 });
await assert.rejects(timeout.render({ inputPath: join(dir, 'word.docx'), outputPath: join(dir, 'timeout.pdf') }), { code: 'timeout' });
await timeout.dispose();
const limited = await createConverter({ maxArchiveEntries: 1 });
await assert.rejects(limited.render({ inputPath: join(dir, 'word.docx'), outputPath: join(dir, 'limit.pdf') }), { code: 'invalid-document' });
await limited.dispose();
const provider = { slots: [], options: {}, config: { maxOutputBytes: 100 * 1024 ** 2 } };
const converted = await OfficeToPdf.prototype.convertBytes.call(provider, await readFile(join(dir, 'slides.pptx')), 'pptx', signal);
assert.match(Buffer.from(converted.pdf).subarray(0, 8).toString(), /^%PDF-/);
await Promise.all(provider.slots.map(async slot => (await slot.converter).dispose()));
console.log('installed DSH convertBytes integration passed');
assert.equal((await readdir(tmpdir())).filter(name => name.startsWith('dsh-native-office-')).length, 0);
console.log('concurrent DOCX/PPTX/DOC/PPT, output/ZIP limits, timeout and cleanup passed');
} finally { await converter.dispose(); await rm(dir, { recursive: true, force: true }); }