import { describe, expect, it } from 'vitest'; import { dhashFromGray, hamming, hostRule, imageKey, nearestWidth, requestHeadersFor } from './images-core'; describe('images-core', () => { it('computes a stable dHash and hamming distance', () => { const grad = Array.from({ length: 72 }, (_, i) => (i % 9) * 30); // left→right ramp: all bits 0 const h1 = dhashFromGray(grad); expect(h1).toBe('0000000000000000'); const inv = Array.from({ length: 72 }, (_, i) => 255 - (i % 9) * 30); const h2 = dhashFromGray(inv); expect(h2).toBe('ffffffffffffffff'); expect(hamming(h1, h2)).toBe(64); expect(hamming(h1, h1)).toBe(0); }); it('applies host rules', () => { expect(hostRule('cards.scryfall.io').maxConcurrent).toBe(4); const h = requestHeadersFor(new URL('https://storage.googleapis.com/images.pricecharting.com/x/240.jpg')); expect(h.referer).toBe('https://www.pricecharting.com/'); expect(h['user-agent']).toMatch(/RareIndexImageCache/); expect(h['user-agent']).not.toBe('node'); const c = requestHeadersFor(new URL('https://www.comicconnect.com/coverimages/x.jpg')); expect(c.referer).toBe('https://www.comicconnect.com/'); expect(requestHeadersFor(new URL('https://images.ygoprodeck.com/images/cards/1.jpg')).referer).toBeUndefined(); }); it('snaps widths and keys urls', () => { expect(nearestWidth(100)).toBe(192); expect(nearestWidth(384)).toBe(384); expect(nearestWidth(5000)).toBe(1200); expect(nearestWidth('abc')).toBe(384); expect(imageKey('https://a/b.jpg')).toHaveLength(40); expect(imageKey('https://a/b.jpg')).toBe(imageKey('https://a/b.jpg')); }); });