// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. import { describe, expect, it } from 'vitest'; import { DesktopFileNodeParser } from '../src/onenote/onestore/file-node.js'; import { OneNoteParserError } from '../src/onenote/parser/error.js'; import { resolveParserLimits } from '../src/onenote/parser/limits.js'; const MAGIC = 0xa4567ab1f5f7f4c4n; const FOOTER = 0x8bc215c38233ba4bn; describe('DesktopFileNodeParser', () => { it('rejects a multi-fragment cycle immediately', () => { const bytes = new Uint8Array(72); writeFragment(bytes, 0, 0x10, 0, 36); writeFragment(bytes, 36, 0x10, 1, 0); const parser = new DesktopFileNodeParser(bytes, resolveParserLimits()); const parse = () => parser.parseList({ offset: 0, size: 36, nil: false, zero: false }); expect(parse).toThrowError(OneNoteParserError); expect(parse).toThrow(/cycle/i); }); it('stops decoding at ChunkTerminatorFND and permits an opaque tail', () => { const bytes = new Uint8Array(52); const view = new DataView(bytes.buffer); view.setBigUint64(0, MAGIC, true); view.setUint32(8, 0x10, true); view.setUint32(12, 0, true); view.setUint32(16, 0x10ff, true); // id 0xff, declared size 4 bytes.fill(0xa5, 20, 32); // deliberately not valid FileNode words view.setBigUint64(32, 0xffffffffffffffffn, true); view.setUint32(40, 0, true); view.setBigUint64(44, FOOTER, true); const parser = new DesktopFileNodeParser(bytes, resolveParserLimits()); const parsed = parser.parseList({ offset: 0, size: bytes.length, nil: false, zero: false, }); expect(parsed.nodes).toEqual([]); }); it('uses the transaction-log count to delimit nonzero fragment padding', () => { const bytes = new Uint8Array(52); const view = new DataView(bytes.buffer); view.setBigUint64(0, MAGIC, true); view.setUint32(8, 0x10, true); view.setUint32(12, 0, true); view.setUint32(16, 0x1004, true); // id 4, declared size 4 bytes.fill(0xa5, 20, 32); // opaque tail without a terminator view.setBigUint64(32, 0xffffffffffffffffn, true); view.setUint32(40, 0, true); view.setBigUint64(44, FOOTER, true); const parser = new DesktopFileNodeParser( bytes, resolveParserLimits(), new Map([[0x10, 1]]) ); const parsed = parser.parseList({ offset: 0, size: bytes.length, nil: false, zero: false, }); expect(parsed.nodes.map(({ id }) => id)).toEqual([0x004]); }); }); function writeFragment( bytes: Uint8Array, offset: number, listId: number, sequence: number, nextOffset: number ): void { const view = new DataView(bytes.buffer, offset, 36); view.setBigUint64(0, MAGIC, true); view.setUint32(8, listId, true); view.setUint32(12, sequence, true); view.setBigUint64(16, BigInt(nextOffset), true); view.setUint32(24, 36, true); view.setBigUint64(28, FOOTER, true); }