import { describe, expect, it } from 'vitest'; import { assessLot } from './auction.js'; import { feeBasisLabel } from './fees.js'; const riv = { rivUsd: 10_000, confidence: 0.8, sampleSize: 30, basis: 'transactions' as const }; describe('assessLot (§33–§35)', () => { it('uses the low estimate when there is no bid, on an all-in basis', () => { const a = assessLot({ house: "Sotheby's", currentBidUsd: null, bidCount: 0, estimateLowUsd: 6_000, estimateHighUsd: 9_000, riv }); expect(a.basedOn).toBe('estimate'); expect(a.allInBidUsd).toBeNull(); expect(a.allInEstimateLowUsd).toBe(7_620); // 6,000 × 1.27 expect(a.allInEstimateHighUsd).toBe(11_430); expect(a.estimateVsRiv).toBeCloseTo(-0.238, 3); expect(a.verdict).toBe('deal'); expect(a.feeBasis).toBe('added_published'); expect(a.buyerPremiumRate).toBe(0.27); }); it('uses the all-in current bid when bids exist', () => { const a = assessLot({ house: 'Goldin', currentBidUsd: 7_000, bidCount: 3, estimateLowUsd: 5_000, estimateHighUsd: 8_000, riv }); expect(a.basedOn).toBe('bid'); expect(a.allInBidUsd).toBe(8_400); // 20 % premium expect(a.bidVsRiv).toBeCloseTo(-0.16, 3); expect(a.verdict).toBe('deal'); }); it('an opening bid with no bidders is ignored (it is not a price)', () => { const a = assessLot({ house: 'Goldin', currentBidUsd: 1, bidCount: 0, estimateLowUsd: null, estimateHighUsd: null, riv }); expect(a.basedOn).toBe('none'); expect(a.verdict).toBe('ungated'); expect(a.bidVsRiv).toBeNull(); }); it('unknown house → default premium, labelled as such', () => { const a = assessLot({ house: 'Enchères du Coin', currentBidUsd: 8_000, bidCount: 2, estimateLowUsd: null, estimateHighUsd: null, riv }); expect(a.feeBasis).toBe('added_default'); expect(a.allInBidUsd).toBe(9_760); // 22 % expect(feeBasisLabel(a.feeBasis)).toContain('default'); }); it('variant mismatch → ungated, no discount written', () => { const a = assessLot({ house: 'Goldin', currentBidUsd: 5_000, bidCount: 4, estimateLowUsd: null, estimateHighUsd: null, riv, sameVariant: false }); expect(a.verdict).toBe('ungated'); expect(a.reasons).toContain('variant_mismatch'); expect(a.bidVsRiv).toBeNull(); expect(a.allInBidUsd).toBe(6_000); // the all-in cost is still informative }); it('review and anomaly verdicts never produce a usable discount', () => { expect(assessLot({ house: 'Goldin', currentBidUsd: 2_000, bidCount: 5, estimateLowUsd: null, estimateHighUsd: null, riv }).verdict).toBe('review'); // 2,400 all-in vs 10,000 const anomaly = assessLot({ house: 'Goldin', currentBidUsd: 200, bidCount: 5, estimateLowUsd: null, estimateHighUsd: null, riv }); expect(anomaly.verdict).toBe('anomaly'); expect(anomaly.bidVsRiv).toBeNull(); }); it('comps-only or thin valuations never qualify a lot', () => { expect(assessLot({ house: 'Goldin', currentBidUsd: 7_000, bidCount: 3, estimateLowUsd: null, estimateHighUsd: null, riv: { ...riv, basis: 'comps' } }).verdict).toBe('ungated'); expect(assessLot({ house: 'Goldin', currentBidUsd: 7_000, bidCount: 3, estimateLowUsd: null, estimateHighUsd: null, riv: { ...riv, sampleSize: 3 } }).verdict).toBe('ungated'); expect(assessLot({ house: 'Goldin', currentBidUsd: 7_000, bidCount: 3, estimateLowUsd: null, estimateHighUsd: null, riv: null }).reasons).toContain('no_riv'); }); it('fee basis labels are human-readable', () => { expect(feeBasisLabel('included')).toBe('premium included'); expect(feeBasisLabel('none')).toBe('no buyer premium'); expect(feeBasisLabel('unknown')).toBe('fees unknown'); expect(feeBasisLabel('added_approximate')).toContain('estimated'); }); });