import assert from 'node:assert/strict' import { strToU8, zipSync } from 'fflate' import { collectGeometryImportText } from '../src/facade/geometryRuntime' import { inspectFcstdArchive } from '../src/facade/fcstd' import { createMockFacade } from '../src/facade/mockFacade' const documentXml = (body = '') => `${body}` const oneMiB = new Uint8Array(1024 * 1024) for (let index = 0; index < oneMiB.length; index += 1) oneMiB[index] = (index * 31 + 17) & 0xff const withinLimit = zipSync({ 'Document.xml': strToU8(documentXml()), 'Resources/payload.bin': oneMiB }, { level: 0 }) const accepted = inspectFcstdArchive(withinLimit, { maxArchiveBytes: 2 * 1024 * 1024, maxEntryBytes: 2 * 1024 * 1024, maxTotalUncompressedBytes: 2 * 1024 * 1024 }) assert.equal(accepted.entries.length, 2) assert.equal(accepted.entries.find((entry) => entry.path === 'Resources/payload.bin')?.uncompressedBytes, oneMiB.byteLength) const rejected: Record = {} const expectReject = (name: string, action: () => unknown, pattern: RegExp) => { let caught: unknown try { action() } catch (error) { caught = error } assert.ok(caught instanceof Error && pattern.test(caught.message), `${name} did not fail explicitly: ${caught instanceof Error ? caught.message : String(caught)}`) rejected[name] = caught.message } expectReject('archive-bytes', () => inspectFcstdArchive(withinLimit, { maxArchiveBytes: withinLimit.byteLength - 1 }), /archive exceeds/) expectReject('entry-bytes', () => inspectFcstdArchive(withinLimit, { maxEntryBytes: oneMiB.byteLength - 1 }), /entry .* exceeds/) expectReject('total-uncompressed', () => inspectFcstdArchive(withinLimit, { maxTotalUncompressedBytes: oneMiB.byteLength }), /archive exceeds .* uncompressed bytes/) expectReject('entry-count', () => inspectFcstdArchive(withinLimit, { maxEntries: 1 }), /archive exceeds 1 entries/) const compressed = zipSync({ 'Document.xml': strToU8(documentXml()), 'Resources/compressible.bin': new Uint8Array(256 * 1024) }, { level: 9 }) expectReject('compression-ratio', () => inspectFcstdArchive(compressed, { maxCompressionRatio: 2 }), /maximum compression ratio/) expectReject('truncated-zip', () => inspectFcstdArchive(withinLimit.slice(0, -16)), /ZIP archive|central directory|end-of-central-directory/) const deepXml = zipSync({ 'Document.xml': strToU8(documentXml(`${''.repeat(10)}${''.repeat(10)}`)) }) expectReject('xml-depth', () => inspectFcstdArchive(deepXml, { maxXmlDepth: 8 }), /nesting depth/) const wideXml = zipSync({ 'Document.xml': strToU8(documentXml(Array.from({ length: 32 }, (_, index) => ``).join(''))) }) expectReject('xml-nodes', () => inspectFcstdArchive(wideXml, { maxXmlNodes: 20 }), /XML nodes/) const malformedXml = zipSync({ 'Document.xml': strToU8('') }) expectReject('malformed-xml', () => inspectFcstdArchive(malformedXml), /XML|closing tag|Document/i) const streamChunks = [new TextEncoder().encode('ISO-10303-21;\n'), new TextEncoder().encode('DATA;\n'), new TextEncoder().encode('ENDSEC;\nEND-ISO-10303-21;')] async function* successfulStream() { for (const chunk of streamChunks) yield chunk } const streamed = await collectGeometryImportText(successfulStream(), undefined, 1024) assert.equal(streamed, 'ISO-10303-21;\nDATA;\nENDSEC;\nEND-ISO-10303-21;') const controller = new AbortController() async function* cancelledStream() { yield streamChunks[0]; controller.abort(); yield streamChunks[1] } await assert.rejects(() => collectGeometryImportText(cancelledStream(), controller.signal, 1024), (error: unknown) => error instanceof DOMException && error.name === 'AbortError') async function* invalidUtf8() { yield new Uint8Array([0xff]) } await assert.rejects(() => collectGeometryImportText(invalidUtf8(), undefined, 1024), /UTF-8|encoding|encoded/i) async function* overLimitStream() { yield new Uint8Array(4); yield new Uint8Array(5) } await assert.rejects(() => collectGeometryImportText(overLimitStream(), undefined, 8), /exceeds 8 bytes/) const facade = createMockFacade() await facade.project.save() facade.app.document.setProperty({ objectId: 'pad', propertyName: 'Length', value: 31 }) await facade.project.save() const recovery = await facade.project.recovery('doc-pump-housing') assert.equal(recovery.integrity, 'ok') assert.equal(recovery.lastSavedVersion, facade.app.document.getActive().version) assert.ok(recovery.checkpoints.length >= 2) console.log(JSON.stringify({ status: 'fcstd-hardening-pass', accepted: { archiveBytes: withinLimit.byteLength, entryCount: accepted.entries.length, payloadBytes: oneMiB.byteLength }, rejected, streaming: { chunks: streamChunks.length, bytes: new TextEncoder().encode(streamed).byteLength, cancellation: 'AbortError', invalidUtf8: 'rejected', overLimit: 'rejected' }, recovery: { integrity: recovery.integrity, lastSavedVersion: recovery.lastSavedVersion, checkpointCount: recovery.checkpoints.length } }, null, 2))