import { describe, expect, it, vi } from 'vitest'
import { detectImageMime, prepareImageForUpload, validateImageFile } from '@/lib/fileValidation'
const PNG_HEADER = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
const JPEG_HEADER = new Uint8Array([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46])
function blobPart(bytes: Uint8Array): ArrayBuffer {
const buffer = new ArrayBuffer(bytes.byteLength)
new Uint8Array(buffer).set(bytes)
return buffer
}
/** Minimal HEIC-like ftyp box: size(4) + 'ftyp' + 'heic' */
function heicHeader(): Uint8Array {
const bytes = new Uint8Array(16)
bytes.set([0x00, 0x00, 0x00, 0x18], 0)
bytes.set(new TextEncoder().encode('ftypheic'), 4)
return bytes
}
describe('detectImageMime', () => {
it('detects JPEG/PNG from magic bytes even when declared type is empty', () => {
expect(detectImageMime(JPEG_HEADER, '')).toBe('image/jpeg')
expect(detectImageMime(PNG_HEADER, '')).toBe('image/png')
})
it('detects HEIC from ftyp brand or declared mime', () => {
expect(detectImageMime(heicHeader(), '')).toBe('image/heic')
expect(detectImageMime(new Uint8Array(16), 'image/heic')).toBe('image/heic')
})
})
describe('image upload validation', () => {
it('accepts a PNG with a valid binary signature', async () => {
const file = new File([blobPart(PNG_HEADER)], 'safe.png', { type: 'image/png' })
await expect(validateImageFile(file)).resolves.toBeNull()
})
it('accepts JPEG when file.type is empty (common on iOS Safari)', async () => {
const file = new File([blobPart(JPEG_HEADER)], 'photo.jpg', { type: '' })
const prepared = await prepareImageForUpload(file)
expect(typeof prepared === 'string' ? prepared : prepared.type).toBe('image/jpeg')
await expect(validateImageFile(file)).resolves.toBeNull()
})
it('rejects spoofed MIME, SVG and oversized images', async () => {
const spoofed = new File([''], 'attack.png', { type: 'image/png' })
const svg = new File(['