test: add E2E tests for highlights sync flow
This commit is contained in:
159
__tests__/e2e/highlights-sync.test.ts
Normal file
159
__tests__/e2e/highlights-sync.test.ts
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user