import { mergeMeasurements } from '../src/sync/syncService' import { Measurement } from '../src/types' jest.mock('expo-sqlite', () => ({ openDatabaseAsync: jest.fn() })) const make = (overrides: Partial = {}): Measurement => ({ id: 'id-1', weight: 75, bmi: 22, bodyFatPercentage: 15, fatMass: 10, musclePercentage: 40, muscleMass: 30, visceralFat: 8, bmr: 1600, measuredAt: '2024-06-01T08:00:00.000Z', createdAt: '2024-06-01T08:00:00.000Z', updatedAt: '2024-06-01T08:00:00.000Z', syncStatus: 'synced', ...overrides, }) describe('mergeMeasurements', () => { it('both empty returns empty', () => { expect(mergeMeasurements([], [])).toEqual([]) }) it('local only kept', () => { const r = mergeMeasurements([make()], []) expect(r).toHaveLength(1) }) it('remote only kept', () => { const r = mergeMeasurements([], [make({ id: 'r1' })]) expect(r).toHaveLength(1) expect(r[0].id).toBe('r1') }) it('last-writer-wins: remote newer', () => { const local = make({ weight: 75, updatedAt: '2024-06-02T08:00:00.000Z' }) const remote = make({ weight: 76, updatedAt: '2024-06-03T08:00:00.000Z' }) const r = mergeMeasurements([local], [remote]) expect(r[0].weight).toBe(76) }) it('last-writer-wins: local newer', () => { const local = make({ weight: 77, updatedAt: '2024-06-04T08:00:00.000Z' }) const remote = make({ weight: 76, updatedAt: '2024-06-03T08:00:00.000Z' }) const r = mergeMeasurements([local], [remote]) expect(r[0].weight).toBe(77) }) it('remote deleted wins over synced local', () => { const local = make({ updatedAt: '2024-06-01T08:00:00.000Z' }) const remote: any = { ...make({ updatedAt: '2024-06-03T08:00:00.000Z' }), deleted: true } const r = mergeMeasurements([local], [remote]) expect(r[0].deletedAt).toBe('2024-06-03T08:00:00.000Z') }) it('local synced wins over older remote deleted', () => { const local = make({ updatedAt: '2024-06-03T08:00:00.000Z' }) const remote: any = { ...make({ updatedAt: '2024-06-01T08:00:00.000Z' }), deleted: true } const r = mergeMeasurements([local], [remote]) expect(r[0].deletedAt).toBeUndefined() }) it('local pending-delete wins over remote deleted', () => { const local = make({ updatedAt: '2024-06-01T08:00:00.000Z', syncStatus: 'pending-delete', deletedAt: '2024-06-05T08:00:00.000Z' }) const remote: any = { ...make({ updatedAt: '2024-06-03T08:00:00.000Z' }), deleted: true } const r = mergeMeasurements([local], [remote]) expect(r[0].syncStatus).toBe('pending-delete') expect(r[0].deletedAt).toBe('2024-06-05T08:00:00.000Z') }) it('local pending wins over remote newer', () => { const local = make({ weight: 75, updatedAt: '2024-06-01T08:00:00.000Z', syncStatus: 'pending' }) const remote: any = { ...make({ weight: 76, updatedAt: '2024-06-03T08:00:00.000Z' }) } const r = mergeMeasurements([local], [remote]) expect(r[0].weight).toBe(75) expect(r[0].syncStatus).toBe('pending') }) it('remote deleted with no local record is skipped', () => { const remote: any = { ...make({ id: 'r1', updatedAt: '2024-06-03T08:00:00.000Z' }), deleted: true } const r = mergeMeasurements([], [remote]) expect(r).toHaveLength(0) }) it('local without syncStatus treated as synced', () => { const local = make({ weight: 75, updatedAt: '2024-06-01T08:00:00.000Z' }) delete local.syncStatus const remote = make({ weight: 76, updatedAt: '2024-06-03T08:00:00.000Z' }) const r = mergeMeasurements([local], [remote]) expect(r[0].weight).toBe(76) }) })