117 lines
3.3 KiB
JavaScript
117 lines
3.3 KiB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
vi.mock('../../../lib/pricing/price-history.js', () => ({
|
|
recordPriceSnapshots: vi.fn(async (rows) => rows.length),
|
|
}));
|
|
|
|
import { recordPriceSnapshots } from '../../../lib/pricing/price-history.js';
|
|
import {
|
|
enrichLorcanaRowWithFolio,
|
|
mapLorcanaCard,
|
|
runBulkLorcanaSync,
|
|
} from '../../../lib/card-import/lorcana-bulk.js';
|
|
import { buildFolioPriceIndex } from '../../../lib/pricing/foolio-client.js';
|
|
|
|
describe('mapLorcanaCard pricing', () => {
|
|
it('includes price fields when Foilio pricing is provided', () => {
|
|
const mapped = mapLorcanaCard(
|
|
{
|
|
Unique_ID: 'TFC-001',
|
|
Name: 'Ariel - On Human Legs',
|
|
Set_Name: 'The First Chapter',
|
|
Set_ID: 'TFC',
|
|
Card_Num: 1,
|
|
},
|
|
{ currentPrice: 1.25, marketPrice: 1.5, currency: 'USD' }
|
|
);
|
|
|
|
expect(mapped.currentPrice).toBe(1.25);
|
|
expect(mapped.marketPrice).toBe(1.5);
|
|
expect(mapped.pricesUpdatedAt).toBeInstanceOf(Date);
|
|
});
|
|
});
|
|
|
|
describe('enrichLorcanaRowWithFolio', () => {
|
|
it('matches by card id and tracks matched/unmatched counts', () => {
|
|
const index = buildFolioPriceIndex([
|
|
{ card_id: 'TFC-001', usd_low: 1, usd_market: 2 },
|
|
]);
|
|
const stats = { pricesMatched: 0, unmatchedCount: 0 };
|
|
|
|
const enriched = enrichLorcanaRowWithFolio(
|
|
mapLorcanaCard({
|
|
Unique_ID: 'TFC-001',
|
|
Name: 'Ariel - On Human Legs',
|
|
Set_ID: 'TFC',
|
|
}),
|
|
index,
|
|
stats
|
|
);
|
|
|
|
expect(enriched.currentPrice).toBe(1);
|
|
expect(enriched.marketPrice).toBe(2);
|
|
expect(stats.pricesMatched).toBe(1);
|
|
expect(stats.unmatchedCount).toBe(0);
|
|
});
|
|
|
|
it('increments unmatchedCount when no feed row matches', () => {
|
|
const stats = { pricesMatched: 0, unmatchedCount: 0 };
|
|
const enriched = enrichLorcanaRowWithFolio(
|
|
mapLorcanaCard({
|
|
Unique_ID: 'TFC-999',
|
|
Name: 'Unknown',
|
|
Set_ID: 'TFC',
|
|
}),
|
|
buildFolioPriceIndex([]),
|
|
stats
|
|
);
|
|
|
|
expect(enriched.currentPrice ?? null).toBeNull();
|
|
expect(stats.unmatchedCount).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('runBulkLorcanaSync', () => {
|
|
beforeEach(() => {
|
|
vi.mocked(recordPriceSnapshots).mockClear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('fetches Foilio feed once and reports price match summary', async () => {
|
|
const fetchFolioIndex = vi.fn(async () => buildFolioPriceIndex([
|
|
{ card_id: 'TFC-001', usd_low: 0.5, usd_market: 0.75 },
|
|
]));
|
|
|
|
vi.stubGlobal('fetch', vi.fn(async () => ({
|
|
ok: true,
|
|
json: async () => ([
|
|
{
|
|
Unique_ID: 'TFC-001',
|
|
Name: 'Ariel - On Human Legs',
|
|
Set_Name: 'The First Chapter',
|
|
Set_ID: 'TFC',
|
|
Card_Num: 1,
|
|
Gamemode: 'Lorcana',
|
|
},
|
|
]),
|
|
})));
|
|
|
|
const query = vi.fn(async (text, values) => {
|
|
if (String(text).includes('SELECT id, scryfall_id')) {
|
|
return { rows: [{ id: 1, scryfall_id: 'TFC-001' }], rowCount: 1 };
|
|
}
|
|
return { rows: [], rowCount: 0 };
|
|
});
|
|
|
|
const summary = await runBulkLorcanaSync({
|
|
sql: { query },
|
|
fetchFolioIndex,
|
|
});
|
|
|
|
expect(fetchFolioIndex).toHaveBeenCalledTimes(1);
|
|
expect(summary.pricesMatched).toBe(1);
|
|
expect(summary.unmatchedCount).toBe(0);
|
|
expect(summary.upserted).toBe(1);
|
|
expect(recordPriceSnapshots).toHaveBeenCalled();
|
|
});
|
|
});
|