import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { loadFixture, runFixtureSuite } from '@rareindex/connectors/testing'; import { localMeta } from '../_lib/local-meta.js'; import createConnector, { millerCategory, parseAmCatalog } from './index.js'; const dir = path.dirname(fileURLToPath(import.meta.url)); const meta = localMeta(JSON.parse(readFileSync(path.join(dir, 'meta.json'), 'utf8'))); const connector = createConnector(meta); // Real slices of the server-rendered JSON of https://live.millerandmillerauctions.com/auctions/4-10LJNY (captured 2026-09-08). const AUCTION = `{"row_id":"4-10LJNY","type":"auction","self_url":"https:\\/\\/production4-server.auctionmobility.com\\/v1\\/auction\\/4-10LJNY\\/","auction_type":"timed_then_live","title":"Watches, Jewellery & Decorative Arts","time_start":"2020-11-02T09:00:00Z","effective_end_time":"2020-11-21T22:00:00Z","extended_end_time":null,"lot_count":298,"sold_lot_count":0,"default_buyers_premium":null,"currency_code":"CAD","publication_status":"full","_detail_url":"\\/auctions\\/4-10LJNY\\/watches-jewellery-decorative-arts","_slug":"watches-jewellery-decorative-arts"}`; const LOT_SOLD = `{"row_id":"4-178VA3","type":"auction-lot-summary","self_url":"https:\\/\\/production4-server.auctionmobility.com\\/v1\\/auction-lot\\/4-178VA3\\/summary","lot_number":1,"status":"sold","title":"Movado 14K Gold Diamond Cocktail Wristwatch","last_updated":"2020-11-21T14:01:23Z","lot_number_extension":"","starting_price":"25.00","hide_estimate":false,"estimate_low":"200.00","estimate_high":"300.00","truncated_description":"Swiss. Gross weight 14g. CODE-WAT","buyers_premium_amount":"0.00","cover_thumbnail":"https:\\/\\/images4-cdn.auctionmobility.com\\/is3\\/x.jpg","currency_code":"CAD","timed_auction_bid":{"row_id":"f6fbd6ba","absentee_bid_id":"4-1BD47B","amount":"175.00","amount_percentage":null,"created_at":"2020-11-21T13:59:00.000Z","updated_at":"2020-11-21T14:01:23.402Z","registration":{"paddle":"1","row_id":"4-x","customer":{"paddle":null,"row_id":"4-y"}}},"_detail_url":"\\/lots\\/view\\/4-178VA3\\/movado-14k-gold-diamond-cocktail-wristwatch"}`; const LOT_PASSED = `{"row_id":"4-178VZZ","type":"auction-lot-summary","lot_number":9,"status":"expired","title":"Group of Assorted Costume Jewellery (12 pieces)","estimate_low":"100.00","estimate_high":"150.00","currency_code":"CAD","timed_auction_bid":null,"_detail_url":"\\/lots\\/view\\/4-178VZZ\\/costume"}`; const HTML = `Past Lots`; describe('miller-miller', () => { runFixtureSuite(connector, it, expect); it('parses the embedded Auction Mobility JSON (auction header + lot summaries)', () => { const p = parseAmCatalog(HTML, '4-10LJNY', 1)!; expect(p.auction).toMatchObject({ id: '4-10LJNY', title: 'Watches, Jewellery & Decorative Arts', currency: 'CAD', endTime: '2020-11-21T22:00:00Z', lotCount: 298, url: 'https://live.millerandmillerauctions.com/auctions/4-10LJNY/watches-jewellery-decorative-arts' }); expect(p.lots).toHaveLength(2); expect(p.lots[0]).toMatchObject({ id: '4-178VA3', lotNumber: '1', status: 'sold', hammer: 175, estimateLow: 200, estimateHigh: 300, currency: 'CAD', bidAt: '2020-11-21T14:01:23.402Z', url: 'https://live.millerandmillerauctions.com/lots/view/4-178VA3/movado-14k-gold-diamond-cocktail-wristwatch' }); expect(p.lots[1]).toMatchObject({ status: 'expired', hammer: null }); }); it('normalises sold lots to CAD hammer-price sales dated by the auction end; passed lots of closed auctions yield nothing', async () => { const p = parseAmCatalog(HTML, '4-10LJNY', 1)!; const out = await connector.normalize({ url: 'x', externalId: 'e', kind: 'sale', engine: 'api', fetchedAt: new Date('2026-09-08T00:00:00Z'), payload: p }); expect(out).toHaveLength(1); const s = out[0]!; if (s.kind !== 'sale') throw new Error('expected sale'); expect(s).toMatchObject({ price: 175, currency: 'CAD', buyerPremiumIncluded: false, auctionHouse: 'Miller & Miller Auctions', lotNumber: '1', location: 'CA', isBundle: false }); expect(s.saleDate.toISOString()).toBe('2020-11-21T22:00:00.000Z'); expect(s.attributes.categorySlug).toBe('other_watches'); expect(s.attributes.identifiers).toEqual({ miller_lot_id: '4-178VA3' }); expect(s.attributes.metadata).toMatchObject({ hammer_price: 175, estimate_low: 200 }); }); it('yields auction lots (not sales) for upcoming catalogs and reads PSA grades from titles', async () => { const fx = loadFixture('miller-miller', 'sports-upcoming-1'); const out = await connector.normalize(fx.raw); expect(out.length).toBeGreaterThan(0); expect(out.every((r) => r.kind === 'auction_lot')).toBe(true); const lot = out.find((r) => r.kind === 'auction_lot' && /PSA 6/.test(r.rawTitle)); expect(lot).toBeTruthy(); if (lot?.kind === 'auction_lot') { expect(lot.grade).toMatchObject({ grader: 'psa', grade: '6' }); expect(lot.attributes.categorySlug).toBe('baseball_cards'); expect(lot.currency).toBe('CAD'); expect(['upcoming', 'live']).toContain(lot.status); // the catalog was already open for bidding when captured } }); it('maps Miller & Miller sale departments to taxonomy slugs', () => { expect(millerCategory('Pre-1980 Sports Cards & Memorabilia', '1979 O-Pee-Chee #18 Wayne Gretzky Rookie PSA 7')).toBe('hockey_cards'); expect(millerCategory('Petroliana & Advertising', 'Red Indian Motor Oil Porcelain Sign')).toBe('advertising'); expect(millerCategory('Watches, Jewellery & Decorative Arts', 'Omega, Ladies 18K Diamond Cocktail Watch')).toBe('omega'); expect(millerCategory('Watches, Jewellery & Decorative Arts', '18K Gold & Sapphire Ring')).toBe('jewelry'); expect(millerCategory('Coins & Banknotes', '1937 Bank of Canada $2 Note')).toBe('banknotes'); expect(millerCategory('Post-War Canadian Folk Art', 'Maud Lewis (Canadian 1901-1970) Oil on Board')).toBe('art'); expect(millerCategory('Canadiana & Decoys', 'Pair of Redhead Decoys, Ontario c. 1920')).toBe('antiques'); expect(millerCategory('Firearms', 'Rifle')).toBeNull(); }); it('fixture sales are CAD and predate the capture', async () => { const fx = loadFixture('miller-miller', 'watches-sold-1'); const out = await connector.normalize(fx.raw); expect(out.length).toBeGreaterThanOrEqual(10); // fixture trimmed to 10 lots for (const r of out) { expect(r.kind).toBe('sale'); if (r.kind === 'sale') { expect(r.currency).toBe('CAD'); expect(r.buyerPremiumIncluded).toBe(false); expect(r.saleDate.getTime()).toBeLessThan(fx.raw.fetchedAt.getTime()); } } }); });