import { describe, expect, it } from 'vitest' import { normalizeIcoSizes, packIco } from '../src/lib/ico' import { MAX_PNG_DIMENSION, MAX_PNG_FILE_SIZE, MAX_PNG_PIXELS, parsePngHeader, PNG_HEADER_LENGTH, PNG_SIGNATURE, } from '../src/lib/png' function pngHeader(width: number, height: number): Uint8Array { const bytes = new Uint8Array(PNG_HEADER_LENGTH) bytes.set(PNG_SIGNATURE) const view = new DataView(bytes.buffer) view.setUint32(8, 13, false) bytes.set([0x49, 0x48, 0x44, 0x52], 12) view.setUint32(16, width, false) view.setUint32(20, height, false) bytes.set([8, 6, 0, 0, 0], 24) return bytes } describe('packIco', () => { it('writes a valid ICO directory with PNG frame offsets', () => { const first = Uint8Array.from([1, 2, 3]) const second = Uint8Array.from([4, 5, 6, 7]) const output = packIco([{ size: 32, data: first }, { size: 256, data: second }]) const view = new DataView(output.buffer) expect(view.getUint16(0, true)).toBe(0) expect(view.getUint16(2, true)).toBe(1) expect(view.getUint16(4, true)).toBe(2) expect(view.getUint8(6)).toBe(32) expect(view.getUint8(22)).toBe(0) expect(view.getUint32(14, true)).toBe(3) expect(view.getUint32(18, true)).toBe(38) expect(view.getUint32(34, true)).toBe(41) expect(Array.from(output.slice(38))).toEqual([1, 2, 3, 4, 5, 6, 7]) }) it('rejects missing or invalid frames', () => { expect(() => packIco([])).toThrow('至少') expect(() => packIco([{ size: 512, data: Uint8Array.of(1) }])).toThrow('尺寸无效') }) it('normalizes sizes before allocating canvases', () => { expect(normalizeIcoSizes([256, 16, 16, 32])).toEqual([16, 32, 256]) expect(() => normalizeIcoSizes([])).toThrow('至少') expect(() => normalizeIcoSizes([0])).toThrow('尺寸无效') expect(() => normalizeIcoSizes([16.5])).toThrow('尺寸无效') expect(() => normalizeIcoSizes(Array.from({ length: 256 }, () => 16))).toThrow('数量过多') }) }) describe('PNG header validation', () => { it('reads valid IHDR dimensions', () => { expect(parsePngHeader(pngHeader(512, 256))).toEqual({ width: 512, height: 256, pixels: 131_072 }) }) it('rejects invalid signatures, truncated headers and missing IHDR chunks', () => { expect(() => parsePngHeader(new Uint8Array(PNG_HEADER_LENGTH))).toThrow('有效的 PNG') expect(() => parsePngHeader(pngHeader(1, 1).slice(0, 24), PNG_HEADER_LENGTH)).toThrow('文件头不完整') const missingIhdr = pngHeader(1, 1) missingIhdr[12] = 0 expect(() => parsePngHeader(missingIhdr)).toThrow('IHDR') }) it('enforces compressed size, dimensions and decoded pixel limits', () => { expect(() => parsePngHeader(pngHeader(1, 1), MAX_PNG_FILE_SIZE + 1)).toThrow('32 MiB') expect(() => parsePngHeader(pngHeader(MAX_PNG_DIMENSION + 1, 1))).toThrow('宽高') expect(() => parsePngHeader(pngHeader(8_000, 4_001))).toThrow('总像素') expect(() => parsePngHeader(pngHeader(0, 1))).toThrow('尺寸无效') }) })