test: покрыть zip deflate (метод 8), skip в Node 18 (нет deflate-raw)
Deploy drhider / validate (push) Canceled after 0s
Deploy drhider / validate (push) Canceled after 0s
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
// Запуск: node upload/frontend/test_upload_frontend.mjs
|
// Запуск: node upload/frontend/test_upload_frontend.mjs
|
||||||
|
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
|
import { deflateRawSync } from 'node:zlib';
|
||||||
|
|
||||||
// ── Полифилл File (Node 18 не имеет global File) ──
|
// ── Полифилл File (Node 18 не имеет global File) ──
|
||||||
if (typeof globalThis.File === 'undefined') {
|
if (typeof globalThis.File === 'undefined') {
|
||||||
@@ -105,6 +106,72 @@ function makeZip(entries) {
|
|||||||
return Buffer.concat(chunks);
|
return Buffer.concat(chunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Сборка ZIP с методом 8 (deflate) — для проверки inflateRaw ──
|
||||||
|
async function deflateRaw(bytes) {
|
||||||
|
// zlib.deflateRawSync — deflate без zlib-заголовка (то, что ждёт inflateRaw)
|
||||||
|
return deflateRawSync(Buffer.from(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeZipDeflate(entries) {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const chunks = [];
|
||||||
|
const central = [];
|
||||||
|
let offset = 0;
|
||||||
|
const utf8Flag = 0x0800;
|
||||||
|
for (const e of entries) {
|
||||||
|
const nameB = enc.encode(e.name);
|
||||||
|
const raw = enc.encode(e.data);
|
||||||
|
const comp = await deflateRaw(raw);
|
||||||
|
const crc = crc32(raw);
|
||||||
|
const lh = new DataView(new ArrayBuffer(30));
|
||||||
|
lh.setUint32(0, 0x04034b50, true);
|
||||||
|
lh.setUint16(4, 20, true);
|
||||||
|
lh.setUint16(6, utf8Flag, true);
|
||||||
|
lh.setUint16(8, 8, true); // method 8 deflate
|
||||||
|
lh.setUint16(10, 0, true);
|
||||||
|
lh.setUint16(12, 0x21, true);
|
||||||
|
lh.setUint32(14, crc, true);
|
||||||
|
lh.setUint32(18, comp.length, true);
|
||||||
|
lh.setUint32(22, raw.length, true);
|
||||||
|
lh.setUint16(26, nameB.length, true);
|
||||||
|
lh.setUint16(28, 0, true);
|
||||||
|
chunks.push(Buffer.from(lh.buffer), Buffer.from(nameB), Buffer.from(comp));
|
||||||
|
const cd = new DataView(new ArrayBuffer(46));
|
||||||
|
cd.setUint32(0, 0x02014b50, true);
|
||||||
|
cd.setUint16(4, 20, true);
|
||||||
|
cd.setUint16(6, 20, true);
|
||||||
|
cd.setUint16(8, utf8Flag, true);
|
||||||
|
cd.setUint16(10, 8, true);
|
||||||
|
cd.setUint16(12, 0, true);
|
||||||
|
cd.setUint16(14, 0x21, true);
|
||||||
|
cd.setUint32(16, crc, true);
|
||||||
|
cd.setUint32(20, comp.length, true);
|
||||||
|
cd.setUint32(24, raw.length, true);
|
||||||
|
cd.setUint16(28, nameB.length, true);
|
||||||
|
cd.setUint16(30, 0, true);
|
||||||
|
cd.setUint16(32, 0, true);
|
||||||
|
cd.setUint16(34, 0, true);
|
||||||
|
cd.setUint16(36, 0, true);
|
||||||
|
cd.setUint32(38, 0, true);
|
||||||
|
cd.setUint32(42, offset, true);
|
||||||
|
central.push(Buffer.from(cd.buffer), Buffer.from(nameB));
|
||||||
|
offset += 30 + nameB.length + comp.length;
|
||||||
|
}
|
||||||
|
const cdSize = central.reduce((s, x) => s + x.byteLength, 0);
|
||||||
|
const cdStart = offset;
|
||||||
|
const eocd = new DataView(new ArrayBuffer(22));
|
||||||
|
eocd.setUint32(0, 0x06054b50, true);
|
||||||
|
eocd.setUint16(4, 0, true);
|
||||||
|
eocd.setUint16(6, 0, true);
|
||||||
|
eocd.setUint16(8, entries.length, true);
|
||||||
|
eocd.setUint16(10, entries.length, true);
|
||||||
|
eocd.setUint32(12, cdSize, true);
|
||||||
|
eocd.setUint32(16, cdStart, true);
|
||||||
|
eocd.setUint16(20, 0, true);
|
||||||
|
chunks.push(...central, Buffer.from(eocd.buffer));
|
||||||
|
return Buffer.concat(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Тесты decodeZipName ──
|
// ── Тесты decodeZipName ──
|
||||||
function testDecodeZipName() {
|
function testDecodeZipName() {
|
||||||
const utf8 = new TextEncoder().encode('договор.pdf');
|
const utf8 = new TextEncoder().encode('договор.pdf');
|
||||||
@@ -147,6 +214,23 @@ async function testZipNonDoc() {
|
|||||||
console.log(' не-doc фильтр: ok');
|
console.log(' не-doc фильтр: ok');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testZipDeflate() {
|
||||||
|
// deflate-raw требует DecompressionStream('deflate-raw') — в браузере есть, в Node 18 нет
|
||||||
|
let supported = true;
|
||||||
|
try { new DecompressionStream('deflate-raw'); } catch (e) { supported = false; }
|
||||||
|
if (!supported) {
|
||||||
|
console.log(' zip deflate (метод 8): skip — Node 18 не поддерживает deflate-raw');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const content = 'Hello deflate world '.repeat(50);
|
||||||
|
const zip = await makeZipDeflate([{ name: 'deflated.txt', data: content }]);
|
||||||
|
const files = await listZipFiles(new File([zip], 'a.zip'), ALLOWED);
|
||||||
|
assert.equal(files.length, 1);
|
||||||
|
assert.equal(files[0].name, 'deflated.txt');
|
||||||
|
assert.equal(await files[0].text(), content);
|
||||||
|
console.log(' zip deflate (метод 8): ok');
|
||||||
|
}
|
||||||
|
|
||||||
async function testParseZipNotZip() {
|
async function testParseZipNotZip() {
|
||||||
await assert.rejects(() => parseZip(new Uint8Array([1, 2, 3])), /Не ZIP/);
|
await assert.rejects(() => parseZip(new Uint8Array([1, 2, 3])), /Не ZIP/);
|
||||||
console.log(' не-ZIP бросок: ok');
|
console.log(' не-ZIP бросок: ok');
|
||||||
@@ -231,6 +315,7 @@ const TESTS = [
|
|||||||
['zip кириллица', testZipCyrillic],
|
['zip кириллица', testZipCyrillic],
|
||||||
['вложенный zip', testZipNested],
|
['вложенный zip', testZipNested],
|
||||||
['не-doc фильтр', testZipNonDoc],
|
['не-doc фильтр', testZipNonDoc],
|
||||||
|
['zip deflate (метод 8)', testZipDeflate],
|
||||||
['не-ZIP бросок', testParseZipNotZip],
|
['не-ZIP бросок', testParseZipNotZip],
|
||||||
['дедуп/суффиксы', testDedup],
|
['дедуп/суффиксы', testDedup],
|
||||||
['лимиты over', testLimits],
|
['лимиты over', testLimits],
|
||||||
|
|||||||
Reference in New Issue
Block a user