160 lines
4.3 KiB
TypeScript
160 lines
4.3 KiB
TypeScript
import { HighlightSyncManager } from '@/lib/highlight-sync-manager'
|
|
import { addHighlight, getAllHighlights, clearAllHighlights } from '@/lib/highlight-manager'
|
|
import { resolveConflict, mergeHighlights } from '@/lib/sync-conflict-resolver'
|
|
import { BibleHighlight } from '@/types'
|
|
|
|
describe('E2E: Highlights Sync Flow', () => {
|
|
let manager: HighlightSyncManager
|
|
|
|
beforeEach(async () => {
|
|
manager = new HighlightSyncManager()
|
|
// Clear database before each test
|
|
await clearAllHighlights()
|
|
})
|
|
|
|
it('should complete full sync workflow', async () => {
|
|
// 1. User creates highlight locally
|
|
const highlight: BibleHighlight = {
|
|
id: 'h-1',
|
|
verseId: 'v-1',
|
|
color: 'yellow',
|
|
createdAt: Date.now(),
|
|
updatedAt: Date.now(),
|
|
syncStatus: 'pending'
|
|
}
|
|
|
|
await addHighlight(highlight)
|
|
|
|
// 2. Queue it for sync
|
|
await manager.init()
|
|
await manager.queueHighlight(highlight)
|
|
|
|
// 3. Check pending items
|
|
const pending = await manager.getPendingSyncItems()
|
|
expect(pending.length).toBe(1)
|
|
expect(pending[0].color).toBe('yellow')
|
|
|
|
// 4. Mark as syncing
|
|
await manager.markSyncing(['h-1'])
|
|
const syncing = await manager.getSyncingItems()
|
|
expect(syncing.length).toBe(1)
|
|
|
|
// 5. Simulate server response and mark synced
|
|
await manager.markSynced(['h-1'])
|
|
const allHighlights = await getAllHighlights()
|
|
const synced = allHighlights.find(h => h.id === 'h-1')
|
|
expect(synced?.syncStatus).toBe('synced')
|
|
})
|
|
|
|
it('should handle conflict resolution', () => {
|
|
const clientVersion: BibleHighlight = {
|
|
id: 'h-1',
|
|
verseId: 'v-1',
|
|
color: 'blue',
|
|
createdAt: 1000,
|
|
updatedAt: 3000,
|
|
syncStatus: 'pending'
|
|
}
|
|
|
|
const serverVersion: BibleHighlight = {
|
|
id: 'h-1',
|
|
verseId: 'v-1',
|
|
color: 'yellow',
|
|
createdAt: 1000,
|
|
updatedAt: 2000,
|
|
syncStatus: 'synced'
|
|
}
|
|
|
|
// Client version is newer, should win
|
|
const resolved = resolveConflict(clientVersion, serverVersion)
|
|
expect(resolved.color).toBe('blue')
|
|
expect(resolved.syncStatus).toBe('synced')
|
|
})
|
|
|
|
it('should handle sync errors gracefully', async () => {
|
|
const highlight: BibleHighlight = {
|
|
id: 'h-1',
|
|
verseId: 'v-1',
|
|
color: 'yellow',
|
|
createdAt: Date.now(),
|
|
updatedAt: Date.now(),
|
|
syncStatus: 'pending'
|
|
}
|
|
|
|
await addHighlight(highlight)
|
|
await manager.init()
|
|
await manager.queueHighlight(highlight)
|
|
|
|
// Mark as error
|
|
await manager.markError(['h-1'], 'Network timeout')
|
|
|
|
const syncing = await manager.getSyncingItems()
|
|
expect(syncing.length).toBe(0) // Not syncing anymore
|
|
|
|
const all = await getAllHighlights()
|
|
const errored = all.find(h => h.id === 'h-1')
|
|
expect(errored?.syncStatus).toBe('error')
|
|
expect(errored?.syncErrorMsg).toBe('Network timeout')
|
|
})
|
|
|
|
it('should merge highlights with conflict resolution', () => {
|
|
const clientHighlights: BibleHighlight[] = [
|
|
{
|
|
id: 'h-1',
|
|
verseId: 'v-1',
|
|
color: 'yellow',
|
|
createdAt: 1000,
|
|
updatedAt: 2000,
|
|
syncStatus: 'pending'
|
|
},
|
|
{
|
|
id: 'h-2',
|
|
verseId: 'v-2',
|
|
color: 'blue',
|
|
createdAt: 1000,
|
|
updatedAt: Date.now(),
|
|
syncStatus: 'pending'
|
|
}
|
|
]
|
|
|
|
const serverHighlights: BibleHighlight[] = [
|
|
{
|
|
id: 'h-1',
|
|
verseId: 'v-1',
|
|
color: 'orange',
|
|
createdAt: 1000,
|
|
updatedAt: 3000, // Server is newer
|
|
syncStatus: 'synced'
|
|
},
|
|
{
|
|
id: 'h-3',
|
|
verseId: 'v-3',
|
|
color: 'pink',
|
|
createdAt: 1000,
|
|
updatedAt: 1500,
|
|
syncStatus: 'synced'
|
|
}
|
|
]
|
|
|
|
const merged = mergeHighlights(clientHighlights, serverHighlights)
|
|
|
|
// Should have 3 highlights
|
|
expect(merged.length).toBe(3)
|
|
|
|
// h-1: Server won (newer timestamp)
|
|
const h1 = merged.find(h => h.id === 'h-1')
|
|
expect(h1?.color).toBe('orange')
|
|
expect(h1?.syncStatus).toBe('synced')
|
|
|
|
// h-2: Client only, kept as is
|
|
const h2 = merged.find(h => h.id === 'h-2')
|
|
expect(h2?.color).toBe('blue')
|
|
expect(h2?.syncStatus).toBe('pending')
|
|
|
|
// h-3: Server only, added
|
|
const h3 = merged.find(h => h.id === 'h-3')
|
|
expect(h3?.color).toBe('pink')
|
|
expect(h3?.syncStatus).toBe('synced')
|
|
})
|
|
})
|