SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
4.1 KB · 84 lines typescript
Raw Blame History
1import { describe, expect, it } from 'vitest';2import { readFileSync } from 'node:fs';3import path from 'node:path';4import meta from './meta.json' with { type: 'json' };5import { localMeta } from '../_lib/local-meta.js';6import { fixtureDir, listFixtures, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';7import createConnector, { parseCardPage, parseSetIndex, parseSetList, parseShortDate, tcgplayerIdFromUrl } from './index.js';8import { usdEur } from '../_g1-cards-eu-jp-lib/index.js';910const connector = createConnector(localMeta(meta));11const snap = (name: string) => readFileSync(path.join(fixtureDir('limitless-tcg'), name), 'utf8');1213describe('limitless-tcg', () => {14  runFixtureSuite(connector, it, expect);1516  it('parses Limitless dates, prices and TCGplayer ids', () => {17    expect(parseShortDate('26 Sep 25')).toBe('2025-09-26');18    expect(parseShortDate('17 Jul 26')).toBe('2026-07-17');19    expect(parseShortDate('n/a')).toBeNull();20    expect(usdEur('$0.25')).toEqual({ amount: 0.25, currency: 'USD' });21    expect(usdEur('1,585.73€')).toEqual({ amount: 1585.73, currency: 'EUR' });22    expect(usdEur('')).toBeNull();23    expect(tcgplayerIdFromUrl('https://partner.tcgplayer.com/LIMITLESS?u=https%3A%2F%2Fwww.tcgplayer.com%2Fproduct%2F654340%2Fpokemon-me01')).toBe('654340');24  });2526  it('parses the saved set index, set list and card page HTML', () => {27    const idx = parseSetIndex(snap('sets-index.html'), 'en');28    expect(idx.length).toBeGreaterThan(50);29    expect(idx[0]).toMatchObject({ lang: 'en' });30    expect(idx.every((s) => /^[A-Z0-9-]+$/i.test(s.code))).toBe(true);31    expect(idx.some((s) => s.releaseDate && /^\d{4}-\d{2}-\d{2}$/.test(s.releaseDate))).toBe(true);3233    const list = parseSetList(snap('set-list.html'));34    expect(list.setName).toBeTruthy();35    expect(list.cards.length).toBeGreaterThan(100);36    const first = list.cards[0]!;37    expect(first.number).toBe('1');38    expect(first.url).toMatch(/^https:\/\/limitlesstcg\.com\/cards\//);39    expect(first.image).toMatch(/\.png$/);40    expect(first.usd?.currency).toBe('USD');41    expect(first.eur?.currency).toBe('EUR');42    expect(first.usd?.tcgplayerId).toMatch(/^\d+$/);4344    const cp = parseCardPage(snap('card-page.html'));45    expect(cp.cardId).toBeGreaterThan(0);46    expect(cp.name).toBeTruthy();47    expect(cp.number).toBeTruthy();48  });4950  it('emits catalog items with relayed USD/EUR observations for English sets and none for Japanese sets', async () => {51    const en = loadFixture('limitless-tcg', 'en-set-list');52    const out = await connector.normalize(en.raw);53    const obs = out.filter((r) => r.kind === 'price_observation');54    expect(obs.length).toBeGreaterThan(0);55    for (const o of obs) {56      if (o.kind !== 'price_observation') continue;57      expect(['USD', 'EUR']).toContain(o.currency);58      expect(['tcgplayer', 'cardmarket']).toContain((o.attributes.metadata as { provider: string }).provider);59      expect(o.confidence).toBeLessThanOrEqual(0.6);60    }61    const jp = loadFixture('limitless-tcg', 'jp-set-list-no-prices');62    const jout = await connector.normalize(jp.raw);63    expect(jout.every((r) => r.kind === 'catalog_item')).toBe(true);64    for (const r of jout) if (r.kind === 'catalog_item') expect(r.attributes.language).toBe('Japanese');65  });6667  it('turns the price-history XHR into dated observations (cents → units, one per day per provider)', async () => {68    const names = listFixtures('limitless-tcg');69    const fx = loadFixture('limitless-tcg', names.find((n) => n === 'card-price-history')!);70    const out = await connector.normalize(fx.raw);71    expect(out.length).toBeGreaterThan(0);72    const dates = new Set<string>();73    for (const o of out) {74      if (o.kind !== 'price_observation') continue;75      expect(o.observationDate.getUTCHours()).toBe(0);76      expect(o.observationDate.getTime()).toBeLessThan(fx.raw.fetchedAt.getTime());77      expect(o.price).toBeLessThan(10_000);78      const k = `${(o.attributes.metadata as { provider: string }).provider}|${o.observationDate.toISOString()}`;79      expect(dates.has(k)).toBe(false);80      dates.add(k);81    }82  });83});84