import { describe, expect, it } from 'vitest'; import { amsterdamTime, cleanRow, parseCsv, toPayload, validatePayload } from './health-import'; describe('health import', () => { it('parses CSV with quotes and semicolons', () => { expect(parseCsv('date;kcal;notes\r\n2026-01-02;2500;"a; ""b"""\n')).toEqual([ { date: '2026-01-02', kcal: '2500', notes: 'a; "b"' } ]); }); it('validates and camel-cases rows', () => { const r = cleanRow('body_measurements', { date: '2026-01-02', weight_kg: '80,5', waist_cm: '' }); expect(r).toEqual({ ok: true, row: { id: undefined, values: { date: '2026-01-02', weightKg: 80.5, waistCm: null, bodyFatPct: null, notes: null } } }); expect(cleanRow('body_measurements', { date: '2026-01-02' }).ok).toBe(false); expect(cleanRow('workouts', { date: '2026-01-02', exercise: 'squat', rpe: 11 }).ok).toBe(false); expect(cleanRow('workouts', { date: '2026-01-02', exercise: 'squat', bogus: 1 }).ok).toBe( false ); expect( cleanRow('vaccinations', { vaccine: 'X', date: '2026-01-02', country: 'nl' }) ).toMatchObject({ ok: true, row: { values: { country: 'NL' } } }); }); it('resolves bare sleep times in Amsterdam time', () => { const r = cleanRow('sleep', { date: '2026-07-02', bedtime: '23:30', wake_time: '07:15' }); expect(r.ok && (r.row.values.bedtime as Date).toISOString()).toBe('2026-07-01T21:30:00.000Z'); expect(r.ok && (r.row.values.wakeTime as Date).toISOString()).toBe('2026-07-02T05:15:00.000Z'); expect(amsterdamTime('2026-01-15', '12:00')?.toISOString()).toBe('2026-01-15T11:00:00.000Z'); const after = cleanRow('sleep', { date: '2026-07-02', bedtime: '00:40', wake_time: '08:00' }); expect(after.ok && (after.row.values.bedtime as Date).toISOString()).toBe( '2026-07-01T22:40:00.000Z' ); }); it('reports errors per row and table', () => { const { errors } = validatePayload( toPayload({ nutrition_daily: [{ date: 'x' }, { date: '2026-01-01', kcal: 2000 }] }) ); expect(errors).toEqual([ { table: 'nutrition_daily', row: 1, error: expect.stringContaining('date') } ]); expect(() => toPayload([{}])).toThrow(); expect(() => toPayload({ nope: [] })).toThrow(); }); });