import { describe, expect, it } from 'vitest'; import { getConnectorMeta } from '@rareindex/connectors'; import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing'; import createConnector from './index.js'; const connector = createConnector(getConnectorMeta('scryfall')); describe('scryfall', () => { runFixtureSuite(connector, it, expect); it('maps Black Lotus (Alpha) to a catalog item + EUR observation', async () => { const out = await connector.normalize(loadFixture('scryfall', 'black-lotus-alpha').raw); const cat = out.find((r) => r.kind === 'catalog_item'); const obs = out.filter((r) => r.kind === 'price_observation'); expect(cat && 'attributes' in cat && cat.attributes).toMatchObject({ categorySlug: 'magic_the_gathering', set: 'Limited Edition Alpha', setCode: 'LEA', number: '232', year: 1993, rarity: 'rare', language: 'en', variant: null }); expect(cat && 'attributes' in cat ? cat.attributes.identifiers : {}).toMatchObject({ scryfall_id: 'b0faa7f2-b547-42c4-a810-839da50dadfe', tcgplayer_id: '1042', cardmarket_id: '5465' }); expect(cat && 'rawTitle' in cat ? cat.rawTitle : '').toBe('Black Lotus ยท Limited Edition Alpha #232 (1993)'); expect(obs).toHaveLength(1); expect(obs[0]).toMatchObject({ currency: 'EUR', priceKind: 'market' }); if (obs[0]?.kind === 'price_observation') { expect(obs[0].price).toBeGreaterThan(1000); expect(obs[0].observationDate.toISOString()).toBe('2026-09-06T21:05:43.673Z'); } }); it('splits foil / nonfoil finishes into distinct variants with matching prices', async () => { const out = await connector.normalize(loadFixture('scryfall', 'secret-lair-foil-nonfoil').raw); const cats = out.filter((r) => r.kind === 'catalog_item'); expect(cats.map((c) => ('attributes' in c ? c.attributes.variant : undefined)).sort()).toEqual([null, 'Foil'].sort()); const obs = out.filter((r) => r.kind === 'price_observation'); const foilUsd = obs.find((o) => 'attributes' in o && o.attributes.variant === 'Foil' && 'currency' in o && o.currency === 'USD'); const nonfoilUsd = obs.find((o) => 'attributes' in o && o.attributes.variant === null && 'currency' in o && o.currency === 'USD'); expect(foilUsd && nonfoilUsd).toBeTruthy(); for (const o of obs) expect(o.kind === 'price_observation' && o.attributes.identifiers.finish).toMatch(/foil|nonfoil/); }); it('labels showcase treatments and foil-only printings', async () => { const out = await connector.normalize(loadFixture('scryfall', 'neo-showcase-foil-only').raw); const cats = out.filter((r) => r.kind === 'catalog_item'); expect(cats).toHaveLength(1); expect(cats[0] && 'attributes' in cats[0] ? cats[0].attributes.variant : '').toMatch(/Showcase.*Foil/); expect(out.filter((r) => r.kind === 'price_observation').every((o) => 'attributes' in o && /Foil/.test(o.attributes.variant ?? ''))).toBe(true); }); it('uses card face images for double-faced cards', async () => { const out = await connector.normalize(loadFixture('scryfall', 'clb-double-faced').raw); const cat = out.find((r) => r.kind === 'catalog_item'); expect(cat && 'imageUrls' in cat ? cat.imageUrls.length : 0).toBeGreaterThanOrEqual(1); }); it('recognises scryfall card URLs for lookup', () => { expect(connector.urlPatterns?.some((re) => re.test('https://scryfall.com/card/lea/232/black-lotus'))).toBe(true); expect(connector.urlPatterns?.some((re) => re.test('https://scryfall.com/sets/lea'))).toBe(false); }); });