import { describe, expect, it } from 'vitest'; import { parseCmbCsv, parseIcbcCsv, parseGenericBankCsv } from '../src/domain/adapters'; import { migrateDb, MemoryMigrationDb, SCHEMA_VERSION } from '../src/storage/migrations'; describe('CSV adapters', () => { it('parseCmbCsv 招商银行', () => { const csv = '交易日期,交易类型,交易金额,交易对方,摘要\n2026-07-10,消费,100.50,超市,买日用品\n2026-07-11,收入,5000.00,公司,工资'; const events = parseCmbCsv(csv); expect(events).toHaveLength(2); expect(events[0].direction).toBe('expense'); expect(events[0].amount).toBe('-100.50'); expect(events[1].direction).toBe('income'); expect(events[1].amount).toBe('5000.00'); }); it('parseIcbcCsv 工商银行', () => { const csv = '交易时间,交易类型,交易金额,对方户名,摘要\n2026-07-10,消费,200,商户,购物'; const events = parseIcbcCsv(csv); expect(events).toHaveLength(1); expect(events[0].counterparty).toBe('商户'); }); it('parseGenericBankCsv 通用', () => { const csv = '日期,金额,交易对方\n2026-07-10,50,便利店'; const events = parseGenericBankCsv(csv, 'Bank:Custom'); expect(events).toHaveLength(1); expect(events[0].id).toContain('Bank:Custom'); }); it('退款项识别为 refund', () => { const csv = '日期,交易类型,金额,交易对方\n2026-07-10,退款,30,商家'; const events = parseGenericBankCsv(csv); expect(events[0].direction).toBe('refund'); }); }); describe('migrations', () => { it('从 0 迁移到当前版本', async () => { const db = new MemoryMigrationDb(); await migrateDb(db); expect(db.executed.length).toBeGreaterThan(0); // 应包含建表语句 expect(db.executed.some(s => s.includes('CREATE TABLE'))).toBe(true); // 应包含 user_version 设置 expect(db.executed.some(s => s.includes('PRAGMA user_version'))).toBe(true); }); it('已迁移过的不再重复执行', async () => { const db = new MemoryMigrationDb(); await migrateDb(db); const firstCount = db.executed.length; await migrateDb(db); // 第二次 expect(db.executed.length).toBe(firstCount); // 无新增 }); it('SCHEMA_VERSION 至少为 2(含双轨制表)', () => { expect(SCHEMA_VERSION).toBeGreaterThanOrEqual(2); }); it('v2 包含 categories 表', async () => { const db = new MemoryMigrationDb(); await migrateDb(db); expect(db.executed.some(s => s.includes('categories'))).toBe(true); expect(db.executed.some(s => s.includes('credit_cards'))).toBe(true); }); });