import { mergeMeasurements } from '../src/sync/syncService' import { Measurement } from '../src/types' jest.mock('expo-sqlite', () => ({ openDatabaseAsync: jest.fn(), })) const base: 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: 'local', deleted: 0, } describe('mergeMeasurements', () => { it('returns empty array when both are empty', () => { expect(mergeMeasurements([], [])).toEqual([]) }) it('keeps local items not on remote', () => { const result = mergeMeasurements([base], []) expect(result).toHaveLength(1) expect(result[0].id).toBe('id-1') }) it('keeps remote items not on local', () => { const remote = { ...base, syncStatus: 'synced' as const } const result = mergeMeasurements([], [remote]) expect(result).toHaveLength(1) expect(result[0].id).toBe('id-1') }) it('picks the newer version when both have same id', () => { const local = { ...base, weight: 75, updatedAt: '2024-06-02T08:00:00.000Z' } const remote = { ...base, weight: 76, updatedAt: '2024-06-03T08:00:00.000Z' } const result = mergeMeasurements([local], [remote]) expect(result).toHaveLength(1) expect(result[0].weight).toBe(76) }) it('picks local when local is newer', () => { const local = { ...base, weight: 77, updatedAt: '2024-06-04T08:00:00.000Z' } const remote = { ...base, weight: 76, updatedAt: '2024-06-03T08:00:00.000Z' } const result = mergeMeasurements([local], [remote]) expect(result).toHaveLength(1) expect(result[0].weight).toBe(77) }) it('filters out locally deleted items even if present on remote', () => { const local = { ...base, deleted: 1 as const, updatedAt: '2024-06-05T08:00:00.000Z' } const remote = { ...base, deleted: 0 as const, updatedAt: '2024-06-03T08:00:00.000Z' } const result = mergeMeasurements([local], [remote]) expect(result).toHaveLength(0) }) it('filters out remotely deleted items when remote is newer', () => { const local = { ...base, deleted: 0 as const, updatedAt: '2024-06-04T08:00:00.000Z' } const remote = { ...base, deleted: 1 as const, updatedAt: '2024-06-05T08:00:00.000Z' } const result = mergeMeasurements([local], [remote]) expect(result).toHaveLength(0) }) it('keeps local active item when local is newer than remote deletion', () => { const local = { ...base, deleted: 0 as const, weight: 78, updatedAt: '2024-06-06T08:00:00.000Z' } const remote = { ...base, deleted: 1 as const, updatedAt: '2024-06-05T08:00:00.000Z' } const result = mergeMeasurements([local], [remote]) expect(result).toHaveLength(1) expect(result[0].weight).toBe(78) }) it('keeps active item when local deleted is older than remote active', () => { const local = { ...base, deleted: 1 as const, updatedAt: '2024-06-01T08:00:00.000Z' } const remote = { ...base, deleted: 0 as const, updatedAt: '2024-06-05T08:00:00.000Z', syncStatus: 'synced' as const } const result = mergeMeasurements([local], [remote]) expect(result).toHaveLength(1) expect(result[0].weight).toBe(75) }) it('merges multiple active and deleted items correctly', () => { const localActive = { ...base, id: 'a', weight: 70, updatedAt: '2024-06-05T08:00:00.000Z' } const localDeleted = { ...base, id: 'b', weight: 80, deleted: 1 as const, updatedAt: '2024-06-05T08:00:00.000Z' } const remoteActive = { ...base, id: 'c', weight: 90, updatedAt: '2024-06-03T08:00:00.000Z', syncStatus: 'synced' as const } const remoteDeleted = { ...base, id: 'd', weight: 100, deleted: 1 as const, updatedAt: '2024-06-05T08:00:00.000Z' } const result = mergeMeasurements( [localActive, localDeleted], [remoteActive, remoteDeleted] ) expect(result).toHaveLength(2) expect(result.map(m => m.id).sort()).toEqual(['a', 'c']) }) })