Files
onenote-tools/src/onenote/onepkg/quantum/decoder.test.ts

122 lines
4.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { OnePkgError } from '../errors.js';
import { QuantumDecoder } from './decoder.js';
/*
* Pinned compcol MIT fixture from tests/quantum.rs at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50. The source bytes are the
* Quantum CFDATA payload from libmspack's mszip_lzx_qtm.cab; the compcol test
* appends 0xFF, while QuantumDecoder appends that CAB sentinel itself.
*/
const QTM_TEXT = Uint8Array.from([
0xd6, 0x06, 0x69, 0x0b, 0xcb, 0x47, 0xf0, 0x2c, 0x2a, 0x3a, 0x8f, 0x2c, 0xab,
0xbb, 0x3c, 0xb9, 0x33, 0x01, 0x8b, 0xd8, 0x58, 0x4b, 0x7b, 0x01, 0xba, 0x6f,
0x6d, 0x51, 0x6e, 0x3a, 0xc3, 0x67, 0x42, 0x4b, 0xeb, 0x02, 0x36, 0x43, 0xd6,
0x66, 0x56, 0xca, 0x9e, 0x72, 0xcc, 0x30, 0x00, 0x00,
]);
const EXPECTED_TEXT =
'If you can read this, the Quantum decompressor is working!\n';
const ZERO_FRAME = Uint8Array.from([
0xff, 0x6d, 0xda, 0x34, 0x62, 0x1a, 0x9b, 0xa9, 0x92, 0x04, 0xd2, 0x80, 0x00,
0x20,
]);
const SECOND_ZERO_FRAME = Uint8Array.from([0x69, 0x33, 0x90, 0x00, 0x06, 0x00]);
function expectCode(action: () => unknown, code: string): void {
try {
action();
} catch (error) {
expect(error).toBeInstanceOf(OnePkgError);
expect((error as OnePkgError).diagnostic.code).toBe(code);
return;
}
throw new Error(`Expected OnePkgError ${code}`);
}
describe('Quantum decoder', () => {
it('decodes the pinned compcol qtm.txt CFDATA payload', () => {
const decoder = new QuantumDecoder(18, { maxOperations: 1_000_000 });
decoder.appendCabBlock(QTM_TEXT, true);
const output = decoder.decompressNext(EXPECTED_TEXT.length, true);
expect(new TextDecoder().decode(output)).toBe(EXPECTED_TEXT);
});
it('rejects truncated streams and enforces the operation budget', () => {
expectCode(() => {
const decoder = new QuantumDecoder(18, {
maxOperations: 1_000_000,
});
decoder.appendCabBlock(QTM_TEXT.subarray(0, 4), true);
decoder.decompressNext(EXPECTED_TEXT.length, true);
}, 'quantum-unexpected-eof');
expectCode(() => {
const decoder = new QuantumDecoder(18, { maxOperations: 10 });
decoder.appendCabBlock(QTM_TEXT, true);
decoder.decompressNext(EXPECTED_TEXT.length, true);
}, 'quantum-operation-limit');
});
it('checks cancellation during a Quantum frame', () => {
const cancelled = new Error('cancelled');
const decoder = new QuantumDecoder(18, {
maxOperations: 10_000_000,
checkCancellation: () => {
throw cancelled;
},
});
decoder.appendCabBlock(ZERO_FRAME, true);
expect(() => decoder.decompressNext(32_768, true)).toThrow(cancelled);
});
it('appends the CAB sentinel and preserves state across frames', () => {
const decoder = new QuantumDecoder(18, { maxOperations: 10_000_000 });
decoder.appendCabBlock(ZERO_FRAME, false);
decoder.appendCabBlock(SECOND_ZERO_FRAME, true);
const first = decoder.decompressNext(32_768, false);
const second = decoder.decompressNext(32_768, true);
expect(first.every((value) => value === 0)).toBe(true);
expect(second.every((value) => value === 0)).toBe(true);
});
it('bounds and validates Quantum frame padding before the sentinel', () => {
const invalid = new QuantumDecoder(18, {
maxOperations: 10_000_000,
});
invalid.appendCabBlock(Uint8Array.from([...ZERO_FRAME, 0x42]), true);
expectCode(
() => invalid.decompressNext(32_768, true),
'quantum-invalid-frame-trailer'
);
const tooMuchPadding = new QuantumDecoder(18, {
maxOperations: 10_000_000,
});
tooMuchPadding.appendCabBlock(
Uint8Array.from([...ZERO_FRAME, 0, 0, 0, 0, 0]),
true
);
expectCode(
() => tooMuchPadding.decompressNext(32_768, true),
'quantum-invalid-frame-trailer'
);
});
it('rejects partial non-final frames and data after the final block', () => {
const decoder = new QuantumDecoder(18, { maxOperations: 1_000_000 });
decoder.appendCabBlock(QTM_TEXT, true);
expectCode(
() => decoder.decompressNext(EXPECTED_TEXT.length, false),
'quantum-partial-intermediate-frame'
);
decoder.decompressNext(EXPECTED_TEXT.length, true);
expectCode(
() => decoder.decompressNext(EXPECTED_TEXT.length, true),
'quantum-data-after-final-block'
);
});
});