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%
2.9 KB · 61 lines typescript
Raw Blame History
1import { describe, expect, it } from 'vitest';2import meta from './meta.json' with { type: 'json' };3import { localMeta } from '../_lib/local-meta.js';4import { listFixtures, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';5import { createCrawlContext, createRouter, missingRequirements } from '@rareindex/connectors';6import createConnector, { offerProperties, slugForGame } from './index.js';78const connector = createConnector(localMeta(meta));910describe('cardtrader (gated)', () => {11  runFixtureSuite(connector, it, expect);1213  it('is reported as gated until CARDTRADER_API_TOKEN exists and never crashes without it', async () => {14    expect(meta.requires).toEqual(['CARDTRADER_API_TOKEN']);15    const saved = process.env.CARDTRADER_API_TOKEN;16    delete process.env.CARDTRADER_API_TOKEN;17    try {18      expect(missingRequirements(connector.meta)).toEqual(['CARDTRADER_API_TOKEN']);19      const ctx = createCrawlContext({ router: createRouter({}), meta: connector.meta, options: { mode: 'probe', limit: 1 } });20      const seen: unknown[] = [];21      for await (const r of connector.crawl(ctx)) seen.push(r);22      expect(seen).toEqual([]);23      expect(ctx.engineStats).toEqual({});24    } finally {25      if (saved !== undefined) process.env.CARDTRADER_API_TOKEN = saved;26    }27  });2829  it('maps games and offer properties', () => {30    const map = meta.config.gameSlugs as Record<string, string>;31    expect(slugForGame('Magic Magic: the Gathering', map)).toBe('magic_the_gathering');32    expect(slugForGame('Pokemon', map)).toBe('pokemon');33    expect(slugForGame('Warhammer 40k', map)).toBeNull();34    const p = offerProperties({ condition: 'Moderately Played', signed: false, mtg_foil: true, mtg_language: 'en', altered: false });35    expect(p).toMatchObject({ conditionRaw: 'Moderately Played', condition: 'very_good', language: 'English', foil: true, signed: false });36    expect(offerProperties({ condition: 'Near Mint', pokemon_language: 'jp', pokemon_reverse: true }).reverse).toBe(true);37  });3839  it('emits a catalog item with cross-marketplace ids and one listing per public offer', async () => {40    for (const name of listFixtures('cardtrader')) {41      const fx = loadFixture('cardtrader', name);42      const out = await connector.normalize(fx.raw);43      const cats = out.filter((r) => r.kind === 'catalog_item');44      expect(cats.length).toBe(1);45      const c = cats[0]!;46      if (c.kind === 'catalog_item') {47        expect(c.attributes.identifiers.cardtrader_blueprint_id).toMatch(/^\d+$/);48        expect(c.attributes.set).toBeTruthy();49      }50      for (const r of out) {51        if (r.kind !== 'listing') continue;52        expect(['EUR', 'USD']).toContain(r.currency);53        expect(r.price).toBeGreaterThan(0);54        expect(r.listingType).toBe('fixed_price');55        // private sellers' usernames are never stored56        if ((r.attributes.metadata as { seller_type: string }).seller_type !== 'professional') expect(r.seller).toBeNull();57      }58    }59  });60});61