/** * ICD Test Runner — CRUD Helpers * Shared utilities for CRUD tier tests (ZIP builder, etc.). */ (function () { 'use strict'; var T = window.ICD; if (!T) return; if (!T.crud) T.crud = {}; // ── Minimal ZIP builder (no dependencies) ── // Builds a valid ZIP archive from { filename: content } pairs. // Content is UTF-8 strings. Returns a Blob suitable for FormData upload. var crc32Table = null; function crc32(bytes) { if (!crc32Table) { crc32Table = new Uint32Array(256); for (var i = 0; i < 256; i++) { var c = i; for (var j = 0; j < 8; j++) { c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1); } crc32Table[i] = c; } } var crc = 0xFFFFFFFF; for (var k = 0; k < bytes.length; k++) { crc = crc32Table[(crc ^ bytes[k]) & 0xFF] ^ (crc >>> 8); } return (crc ^ 0xFFFFFFFF) >>> 0; } T.crud.buildTestZip = function (files) { var encoder = new TextEncoder(); var entries = []; var offset = 0; // Build file entries Object.keys(files).forEach(function (name) { var nameBytes = encoder.encode(name); var dataBytes = encoder.encode(files[name]); var c = crc32(dataBytes); entries.push({ name: nameBytes, data: dataBytes, crc: c, offset: offset }); // Local file header (30) + name + data offset += 30 + nameBytes.length + dataBytes.length; }); // Calculate central directory var cdOffset = offset; var cdSize = 0; entries.forEach(function (e) { cdSize += 46 + e.name.length; }); var totalSize = cdOffset + cdSize + 22; // + EOCD var buf = new ArrayBuffer(totalSize); var view = new DataView(buf); var pos = 0; // Write local file headers + data entries.forEach(function (e) { view.setUint32(pos, 0x04034b50, true); pos += 4; // signature view.setUint16(pos, 20, true); pos += 2; // version needed view.setUint16(pos, 0, true); pos += 2; // flags view.setUint16(pos, 0, true); pos += 2; // compression (store) view.setUint16(pos, 0, true); pos += 2; // mod time view.setUint16(pos, 0, true); pos += 2; // mod date view.setUint32(pos, e.crc, true); pos += 4; // crc32 view.setUint32(pos, e.data.length, true); pos += 4; // compressed size view.setUint32(pos, e.data.length, true); pos += 4; // uncompressed size view.setUint16(pos, e.name.length, true); pos += 2; // name length view.setUint16(pos, 0, true); pos += 2; // extra length new Uint8Array(buf, pos, e.name.length).set(e.name); pos += e.name.length; new Uint8Array(buf, pos, e.data.length).set(e.data); pos += e.data.length; }); // Write central directory entries.forEach(function (e) { view.setUint32(pos, 0x02014b50, true); pos += 4; // signature view.setUint16(pos, 20, true); pos += 2; // version made by view.setUint16(pos, 20, true); pos += 2; // version needed view.setUint16(pos, 0, true); pos += 2; // flags view.setUint16(pos, 0, true); pos += 2; // compression view.setUint16(pos, 0, true); pos += 2; // mod time view.setUint16(pos, 0, true); pos += 2; // mod date view.setUint32(pos, e.crc, true); pos += 4; // crc32 view.setUint32(pos, e.data.length, true); pos += 4; // compressed size view.setUint32(pos, e.data.length, true); pos += 4; // uncompressed size view.setUint16(pos, e.name.length, true); pos += 2; // name length view.setUint16(pos, 0, true); pos += 2; // extra length view.setUint16(pos, 0, true); pos += 2; // comment length view.setUint16(pos, 0, true); pos += 2; // disk number view.setUint16(pos, 0, true); pos += 2; // internal attrs view.setUint32(pos, 0, true); pos += 4; // external attrs view.setUint32(pos, e.offset, true); pos += 4; // local header offset new Uint8Array(buf, pos, e.name.length).set(e.name); pos += e.name.length; }); // Write EOCD view.setUint32(pos, 0x06054b50, true); pos += 4; // signature view.setUint16(pos, 0, true); pos += 2; // disk number view.setUint16(pos, 0, true); pos += 2; // cd start disk view.setUint16(pos, entries.length, true); pos += 2; // entries on disk view.setUint16(pos, entries.length, true); pos += 2; // total entries view.setUint32(pos, cdSize, true); pos += 4; // cd size view.setUint32(pos, cdOffset, true); pos += 4; // cd offset view.setUint16(pos, 0, true); // comment length return new Blob([buf], { type: 'application/zip' }); }; })();