mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Add autosync manager serialization tests
This commit is contained in:
112
src/store/middleware/__tests__/autoSyncManager.test.ts
Normal file
112
src/store/middleware/__tests__/autoSyncManager.test.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { AutoSyncManager } from '../autoSyncMiddleware'
|
||||
import { syncToFlaskBulk } from '../../slices/syncSlice'
|
||||
|
||||
vi.mock('../../slices/syncSlice', () => ({
|
||||
syncToFlaskBulk: vi.fn(() => ({ type: 'sync/syncToFlaskBulk' })),
|
||||
checkFlaskConnection: vi.fn(() => ({ type: 'sync/checkConnection' })),
|
||||
}))
|
||||
|
||||
type Deferred<T> = {
|
||||
promise: Promise<T>
|
||||
resolve: (value: T) => void
|
||||
reject: (error?: unknown) => void
|
||||
}
|
||||
|
||||
const createDeferred = <T,>(): Deferred<T> => {
|
||||
let resolve!: (value: T) => void
|
||||
let reject!: (error?: unknown) => void
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
|
||||
describe('AutoSyncManager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('serializes syncs and runs one pending sync after completion', async () => {
|
||||
const manager = new AutoSyncManager()
|
||||
const deferreds = [createDeferred<void>(), createDeferred<void>()]
|
||||
const dispatch = vi
|
||||
.fn()
|
||||
.mockImplementation(() => deferreds.shift()?.promise ?? Promise.resolve())
|
||||
|
||||
manager.setDispatch(dispatch)
|
||||
|
||||
const firstSync = manager.syncNow()
|
||||
const secondSync = manager.syncNow()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
||||
|
||||
deferreds[0].resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(2)
|
||||
|
||||
deferreds[1].resolve()
|
||||
|
||||
await firstSync
|
||||
await secondSync
|
||||
})
|
||||
|
||||
it('resets changeCounter after a successful sync', async () => {
|
||||
const manager = new AutoSyncManager()
|
||||
const dispatch = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
manager.setDispatch(dispatch)
|
||||
manager.trackChange()
|
||||
manager.trackChange()
|
||||
|
||||
expect(manager.getStatus().changeCounter).toBe(2)
|
||||
|
||||
await manager.syncNow()
|
||||
|
||||
expect(manager.getStatus().changeCounter).toBe(0)
|
||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('coalesces multiple pending sync requests into one run', async () => {
|
||||
const manager = new AutoSyncManager()
|
||||
const deferreds = [createDeferred<void>(), createDeferred<void>()]
|
||||
const dispatch = vi
|
||||
.fn()
|
||||
.mockImplementation(() => deferreds.shift()?.promise ?? Promise.resolve())
|
||||
|
||||
manager.setDispatch(dispatch)
|
||||
|
||||
const firstSync = manager.syncNow()
|
||||
const secondSync = manager.syncNow()
|
||||
const thirdSync = manager.syncNow()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
||||
|
||||
deferreds[0].resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(2)
|
||||
|
||||
deferreds[1].resolve()
|
||||
|
||||
await firstSync
|
||||
await secondSync
|
||||
await thirdSync
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('dispatches the sync thunk when performing a sync', async () => {
|
||||
const manager = new AutoSyncManager()
|
||||
const dispatch = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
manager.setDispatch(dispatch)
|
||||
|
||||
await manager.syncNow()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(syncToFlaskBulk())
|
||||
})
|
||||
})
|
||||
@@ -9,7 +9,7 @@ interface AutoSyncConfig {
|
||||
maxQueueSize: number
|
||||
}
|
||||
|
||||
class AutoSyncManager {
|
||||
export class AutoSyncManager {
|
||||
private config: AutoSyncConfig = {
|
||||
enabled: false,
|
||||
intervalMs: 30000,
|
||||
@@ -21,6 +21,8 @@ class AutoSyncManager {
|
||||
private lastSyncTime = 0
|
||||
private changeCounter = 0
|
||||
private dispatch: any = null
|
||||
private syncInFlight: Promise<void> | null = null
|
||||
private pendingSync = false
|
||||
|
||||
configure(config: Partial<AutoSyncConfig>) {
|
||||
this.config = { ...this.config, ...config }
|
||||
@@ -69,12 +71,32 @@ class AutoSyncManager {
|
||||
private async performSync() {
|
||||
if (!this.dispatch) return
|
||||
|
||||
if (this.syncInFlight) {
|
||||
this.pendingSync = true
|
||||
return
|
||||
}
|
||||
|
||||
const syncPromise = (async () => {
|
||||
try {
|
||||
await this.dispatch(syncToFlaskBulk())
|
||||
this.lastSyncTime = Date.now()
|
||||
this.changeCounter = 0
|
||||
} catch (error) {
|
||||
console.error('[AutoSync] Sync failed:', error)
|
||||
}
|
||||
})()
|
||||
|
||||
this.syncInFlight = syncPromise
|
||||
|
||||
try {
|
||||
await this.dispatch(syncToFlaskBulk())
|
||||
this.lastSyncTime = Date.now()
|
||||
this.changeCounter = 0
|
||||
} catch (error) {
|
||||
console.error('[AutoSync] Sync failed:', error)
|
||||
await syncPromise
|
||||
} finally {
|
||||
this.syncInFlight = null
|
||||
}
|
||||
|
||||
if (this.pendingSync) {
|
||||
this.pendingSync = false
|
||||
await this.performSync()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user