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.4 KB · 69 lines typescript
Raw Blame History
1import { describe, expect, it } from 'vitest';2import { getConnectorMeta } from '@rareindex/connectors';3import { listFixtures, loadFixture, runFixtureSuite } from '@rareindex/connectors/testing';4import createConnector, { auctionLinks, classifyTitle, hintsForAuction, lotUrl, parseAuctionPage, parseGraphqlLots, type PagePayload } from './index.js';56const connector = createConnector(getConnectorMeta('sothebys'));78describe('sothebys connector', () => {9  runFixtureSuite(connector, it, expect);1011  it('closed sales: price includes premium, hammer kept in metadata, date from the lot closing time', async () => {12    const closed = listFixtures('sothebys').filter((n) => n.startsWith('closed'));13    expect(closed.length).toBeGreaterThan(0);14    const fx = loadFixture('sothebys', closed[0]!);15    const payload = fx.raw.payload as PagePayload;16    expect(payload.auction.state).toBe('Closed');17    const out = await connector.normalize(fx.raw);18    expect(out.length).toBeGreaterThan(0);19    for (const r of out) {20      expect(r.kind).toBe('sale');21      if (r.kind !== 'sale') continue;22      const lot = payload.lots.find((l) => l.lotId === r.externalId)!;23      expect(lot.isSold).toBe(true);24      expect(r.price).toBe(lot.finalPrice);25      expect(r.price).toBeGreaterThanOrEqual(lot.currentBid ?? 0);26      expect(r.buyerPremiumIncluded).toBe(true);27      expect(r.attributes.metadata.hammer_price).toBe(lot.currentBid);28      expect(r.saleDate.toISOString()).toBe(new Date(lot.closingTime ?? payload.auction.closedAt!).toISOString());29      expect(r.auctionHouse).toBe("Sotheby's");30      expect(r.sourceUrl).toBe(lotUrl(payload.auction.url, lot.slug, lot.lotId));31    }32  });3334  it('open sales: lots become auction_lot records with estimates', async () => {35    const live = listFixtures('sothebys').filter((n) => n.startsWith('live'));36    expect(live.length).toBeGreaterThan(0);37    const fx = loadFixture('sothebys', live[0]!);38    const payload = fx.raw.payload as PagePayload;39    expect(payload.auction.state).not.toBe('Closed');40    const out = await connector.normalize(fx.raw);41    expect(out.length).toBeGreaterThan(0);42    for (const r of out) {43      expect(r.kind).toBe('auction_lot');44      if (r.kind !== 'auction_lot') continue;45      expect(['upcoming', 'live']).toContain(r.status);46      expect(r.currency).toBe(payload.auction.currency);47      expect(r.auctionName).toBe(payload.auction.title);48    }49  });5051  it('mixed multi-department sales only trust title keywords', () => {52    const mixed = { auctionId: 'x', url: 'u', title: 'Arcade | New York', saleNumber: null, state: 'Closed', type: 'Timed', departments: ['American Furniture', 'Decorative Art', 'American Art', 'Books & Manuscripts', 'Contemporary Art'], currency: 'USD', location: 'New York', startsAt: null, endsAt: null, closedAt: null, totalLots: null };53    const hints = hintsForAuction(mixed);54    expect(hints[0]).toBe('unknown');55    expect(classifyTitle('Tom Baril — Lilies', hints)).toBeNull();56    expect(classifyTitle('Andy Warhol — Marilyn, screenprint', hints)).toBe('contemporary_art');57    const watches = { ...mixed, title: 'Important Watches', departments: ['Watches'] };58    expect(classifyTitle('Patek Philippe — Nautilus Ref. 5711/1A', hintsForAuction(watches))).toBe('patek_philippe');59  });6061  it('parses GraphQL pages and discovery links defensively', () => {62    expect(parseAuctionPage('<html></html>', 'u')).toBeNull();63    expect(parseGraphqlLots({ errors: [{ message: 'x' }] })).toBeNull();64    const gl = parseGraphqlLots({ data: { auction: { lotCards: { totalCount: 1, hasNextPage: false, lots: [{ lotId: 'l1', title: 'T', lotNumber: { lotDisplayNumber: '7' }, slug: { lotSlug: 't' }, estimateV2: { lowEstimate: { amount: '10' }, highEstimate: { amount: '20' } }, bidState: { isClosed: true, closingTime: '2026-08-26T16:01Z', numberOfBids: 3, currentBidV2: { amount: '900', currency: 'USD' }, sold: { __typename: 'ResultVisible', isSold: true, premiums: { finalPriceV2: { amount: '1152', currency: 'USD' } } } } }] } } } });65    expect(gl?.lots[0]).toMatchObject({ lotId: 'l1', lotNumber: '7', estimateLow: 10, currentBid: 900, finalPrice: 1152, finalCurrency: 'USD', isSold: true });66    expect(auctionLinks('<a href="https://www.sothebys.com/en/buy/auction/2026/arcade-new-york">x</a> https://www.sothebys.com/en/buy/auction/2026/arcade-new-york/lilies')).toEqual(['https://www.sothebys.com/en/buy/auction/2026/arcade-new-york']);67  });68});69