Replace @vercel/postgres, Blob, and Upstash with lib/sql.js, MinIO object storage, and CT 102 Redis rate limits. Add Dockerfile for Dokploy deploy, homelab runbooks, Neon data-copy helper, and point CI smoke/visual at the homelab URL instead of Vercel previews. Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
2.6 KiB
JavaScript
83 lines
2.6 KiB
JavaScript
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
|
|
import { buildCatalogSyncQueue, runUnifiedCatalogSync } from '../../lib/card-import/sync-catalog.js';
|
|
import { runBulkMtgSync } from '../../lib/card-import/bulk-sync.js';
|
|
import { runBulkPokemonSync } from '../../lib/card-import/pokemon-bulk.js';
|
|
import { runBulkLorcanaSync } from '../../lib/card-import/lorcana-bulk.js';
|
|
|
|
vi.mock('../../lib/card-import/bulk-sync.js', () => ({
|
|
runBulkMtgSync: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../lib/card-import/pokemon-bulk.js', () => ({
|
|
runBulkPokemonSync: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../lib/card-import/lorcana-bulk.js', () => ({
|
|
runBulkLorcanaSync: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../lib/sql.js', () => ({
|
|
sql: vi.fn(() => Promise.resolve({ rows: [] })),
|
|
}));
|
|
|
|
describe('buildCatalogSyncQueue', () => {
|
|
it('merges MTG and Pokémon missing sets by release date, newest first', () => {
|
|
const queue = buildCatalogSyncQueue(
|
|
[
|
|
{ code: 'fin', name: 'Final Fantasy', releasedAt: '2026-05-01' },
|
|
{ code: 'dsk', name: 'Duskmourn', releasedAt: '2024-09-27' },
|
|
],
|
|
[
|
|
{ id: 'sv10', name: 'Perfect Order', releasedAt: '2026-03-27' },
|
|
{ id: 'sv9', name: 'Journey Together', releasedAt: '2025-03-28' },
|
|
],
|
|
3
|
|
);
|
|
|
|
expect(queue).toEqual([
|
|
{
|
|
game: 'mtg',
|
|
setCode: 'fin',
|
|
name: 'Final Fantasy',
|
|
releasedAt: '2026-05-01',
|
|
},
|
|
{
|
|
game: 'pokemon',
|
|
setCode: 'sv10',
|
|
name: 'Perfect Order',
|
|
releasedAt: '2026-03-27',
|
|
},
|
|
{
|
|
game: 'pokemon',
|
|
setCode: 'sv9',
|
|
name: 'Journey Together',
|
|
releasedAt: '2025-03-28',
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('runUnifiedCatalogSync', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('runs all three games and isolates per-game failures', async () => {
|
|
runBulkMtgSync.mockResolvedValue({ mode: 'bulk', upserted: 36000, errors: 0 });
|
|
runBulkPokemonSync.mockRejectedValue(new Error('Pokemon API down'));
|
|
runBulkLorcanaSync.mockResolvedValue({ mode: 'bulk', upserted: 2283, errors: 0 });
|
|
|
|
const summary = await runUnifiedCatalogSync();
|
|
|
|
expect(summary.mode).toBe('unified');
|
|
expect(summary.mtg.upserted).toBe(36000);
|
|
expect(summary.pokemon.error).toBe('Pokemon API down');
|
|
expect(summary.lorcana.upserted).toBe(2283);
|
|
expect(summary.errors).toEqual([{ game: 'pokemon', message: 'Pokemon API down' }]);
|
|
expect(summary.durationMs).toBeGreaterThanOrEqual(0);
|
|
expect(runBulkMtgSync).toHaveBeenCalledOnce();
|
|
expect(runBulkPokemonSync).toHaveBeenCalledOnce();
|
|
expect(runBulkLorcanaSync).toHaveBeenCalledOnce();
|
|
});
|
|
});
|