mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-25 14:14:57 +00:00
Compare commits
1 Commits
copilot/re
...
codex/add-
| Author | SHA1 | Date | |
|---|---|---|---|
| cadcfa7882 |
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
|
maxQueueSize: number
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutoSyncManager {
|
export class AutoSyncManager {
|
||||||
private config: AutoSyncConfig = {
|
private config: AutoSyncConfig = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
intervalMs: 30000,
|
intervalMs: 30000,
|
||||||
@@ -21,6 +21,8 @@ class AutoSyncManager {
|
|||||||
private lastSyncTime = 0
|
private lastSyncTime = 0
|
||||||
private changeCounter = 0
|
private changeCounter = 0
|
||||||
private dispatch: any = null
|
private dispatch: any = null
|
||||||
|
private syncInFlight: Promise<void> | null = null
|
||||||
|
private pendingSync = false
|
||||||
|
|
||||||
configure(config: Partial<AutoSyncConfig>) {
|
configure(config: Partial<AutoSyncConfig>) {
|
||||||
this.config = { ...this.config, ...config }
|
this.config = { ...this.config, ...config }
|
||||||
@@ -69,12 +71,32 @@ class AutoSyncManager {
|
|||||||
private async performSync() {
|
private async performSync() {
|
||||||
if (!this.dispatch) return
|
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 {
|
try {
|
||||||
await this.dispatch(syncToFlaskBulk())
|
await syncPromise
|
||||||
this.lastSyncTime = Date.now()
|
} finally {
|
||||||
this.changeCounter = 0
|
this.syncInFlight = null
|
||||||
} catch (error) {
|
}
|
||||||
console.error('[AutoSync] Sync failed:', error)
|
|
||||||
|
if (this.pendingSync) {
|
||||||
|
this.pendingSync = false
|
||||||
|
await this.performSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user