96 lines
4.7 KiB
JavaScript
96 lines
4.7 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { runInNewContext } from 'node:vm';
|
|
|
|
test('installer accepts the release host and rejects unverified hosts before writing', () => {
|
|
const source = readFileSync(new URL('./install-preview-download.mjs', import.meta.url), 'utf8')
|
|
.replace(/^import .*;$/gm, '').replaceAll('import.meta.url', JSON.stringify(import.meta.url));
|
|
const release = JSON.parse(readFileSync(new URL('../release.json', import.meta.url)));
|
|
for (const version of [release.version, '0.2.1-rc.2', '0.2.1-rc.4', 'next']) {
|
|
const calls = [];
|
|
const run = () => runInNewContext(source, {
|
|
URL, readFileSync: () => JSON.stringify({ version }),
|
|
mkdirSync: (...args) => calls.push(['mkdir', ...args]),
|
|
cpSync: (...args) => calls.push(['copy', ...args]),
|
|
execFileSync: (...args) => calls.push(['exec', ...args]),
|
|
});
|
|
if (version === release.version) {
|
|
run();
|
|
assert.deepEqual(calls.map(call => call[0]), ['mkdir', 'copy', 'exec']);
|
|
assert.equal(calls[2][1], 'tar');
|
|
assert.equal(calls[2][2][1], '/usr/local/lib/dsh-mgr/dsh-preview-download-1.0.1.tgz');
|
|
} else {
|
|
assert.throws(run, /Unverified preview download host/);
|
|
assert.equal(calls.length, 0);
|
|
}
|
|
}
|
|
});
|
|
|
|
function load() {
|
|
let plugin;
|
|
runInNewContext(readFileSync(new URL('./preview-download/client.js', import.meta.url), 'utf8'), {
|
|
Uint8Array, window: { __ModuleLoader__: { load({ id, factory }) { assert.equal(id, 'dsh-preview-download'); plugin = factory(() => ({})); } } }
|
|
});
|
|
return plugin;
|
|
}
|
|
test('download passes exact session/path/signal to authorized complete-file RPC', async () => {
|
|
const plugin = load();
|
|
const data = new Uint8Array([0, 255, 10]);
|
|
const signal = new AbortController().signal;
|
|
const remote = { workspaceFiles: { async readBytes(...args) {
|
|
assert.equal(args[0], 'session-A'); assert.equal(args[1], '/workspace/中文.pptx');
|
|
assert.equal(Object.keys(args[2]).length, 0); assert.equal(args[3], signal);
|
|
return { ok: true, value: { offset: 0, eof: true, bytes: 3, data } };
|
|
} } };
|
|
assert.equal(await plugin.readOriginal(remote, 'session-A', '/workspace/中文.pptx', signal), data);
|
|
assert.equal(plugin.filename('/workspace/中文.pptx'), '中文.pptx');
|
|
assert.equal(plugin.filename('C:' + String.fromCharCode(92) + 'folder' + String.fromCharCode(92) + 'file.docx'), 'file.docx');
|
|
});
|
|
test('denied, partial, size mismatch and cancelled reads never produce download data', async () => {
|
|
const plugin = load();
|
|
for (const result of [
|
|
{ ok: false, error: { code: 'gateway/forbidden' } },
|
|
{ ok: true, value: { offset: 0, eof: false, data: new Uint8Array() } },
|
|
{ ok: true, value: { offset: 0, eof: true, bytes: 5, data: new Uint8Array(2) } }
|
|
]) {
|
|
await assert.rejects(plugin.readOriginal({ workspaceFiles: { readBytes: async () => result } }, 's', '/file', new AbortController().signal));
|
|
}
|
|
const controller = new AbortController(); controller.abort();
|
|
await assert.rejects(plugin.readOriginal({ workspaceFiles: { readBytes: async () => ({ ok: true, value: {} }) } }, 's', '/file', controller.signal));
|
|
});
|
|
test('plugin registers only the supported preview action slot', () => {
|
|
const plugin = load();
|
|
let registration;
|
|
plugin.apply({ effect: fn => fn(), locale: { register: () => () => {} }, remote: {}, slots: {
|
|
inject(name, fn) { assert.equal(name, 'sidebar.right.tab.document.actions'); return fn(); },
|
|
register(options, component) { registration = options; assert.equal(typeof component, 'function'); return () => {}; }
|
|
} });
|
|
assert.equal(registration.id, 'dsh-preview-download');
|
|
assert.equal(typeof registration.inject('session-A').download, 'function');
|
|
});
|
|
|
|
test('registered download declares every service used by its callback', async () => {
|
|
const plugin = load();
|
|
assert.ok(plugin.inject.includes('remote'));
|
|
assert.ok(plugin.inject.includes('remote.workspaceFiles'));
|
|
let registration;
|
|
let calls = 0;
|
|
const data = new Uint8Array([1, 2, 255]);
|
|
const ctx = {
|
|
effect: fn => fn(),
|
|
locale: { register: () => () => {} },
|
|
slots: { inject: (_name, fn) => fn(), register(options) { registration = options; return () => {}; } },
|
|
get remote() {
|
|
assert.ok(plugin.inject.includes('remote'), 'Cordis requires explicit remote injection');
|
|
return { workspaceFiles: { async readBytes(session, path) {
|
|
assert.equal(session, 'session-A'); assert.equal(path, '/file.bin'); calls++;
|
|
return { ok: true, value: { offset: 0, eof: true, bytes: 3, data } };
|
|
} } };
|
|
}
|
|
};
|
|
plugin.apply(ctx);
|
|
assert.equal(await registration.inject('session-A').download('/file.bin', new AbortController().signal), data);
|
|
assert.equal(calls, 1);
|
|
});
|