From befcd65510c862874ab89f2b5af1f16e1bbafa8f Mon Sep 17 00:00:00 2001 From: Max Nowack Date: Mon, 13 Jan 2025 16:09:49 +0100 Subject: [PATCH] test: extend tests for new start/pause sync functionality --- .../base/sync/__tests__/SyncManager.spec.ts | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/packages/base/sync/__tests__/SyncManager.spec.ts b/packages/base/sync/__tests__/SyncManager.spec.ts index 3baf5588..d8c01676 100644 --- a/packages/base/sync/__tests__/SyncManager.spec.ts +++ b/packages/base/sync/__tests__/SyncManager.spec.ts @@ -1153,3 +1153,181 @@ it('should fail if there was a persistence error during initialization', async ( expect(persistenceInitialized).toBeFalsy() expect(persistenceError).toBeTruthy() }) + +it('should start sync if autostart is enabled', async () => { + const mockPull = vi.fn<() => Promise>>().mockResolvedValue({ + items: [{ id: '1', name: 'Test Item' }], + }) + const mockPush = vi.fn<(options: any, pushParameters: any) => Promise>() + .mockResolvedValue() + + const registerRemoteChange = vi.fn() + const syncManager = new SyncManager({ + pull: mockPull, + push: mockPush, + registerRemoteChange, + autostart: true, + }) + + const collection = new Collection() + syncManager.addCollection(collection, { name: 'test' }) + + expect(registerRemoteChange).toBeCalledTimes(1) + await syncManager.sync('test') + expect(mockPull).toBeCalled() + + await syncManager.startSync('test') + expect(registerRemoteChange).toBeCalledTimes(1) // already started, should not be called again +}) + +it('should not start sync if autostart is disabled', async () => { + const mockPull = vi.fn<() => Promise>>().mockResolvedValue({ + items: [{ id: '1', name: 'Test Item' }], + }) + const mockPush = vi.fn<(options: any, pushParameters: any) => Promise>() + .mockResolvedValue() + + const registerRemoteChange = vi.fn() + const syncManager = new SyncManager({ + pull: mockPull, + push: mockPush, + registerRemoteChange, + autostart: false, + }) + + const collection = new Collection() + syncManager.addCollection(collection, { name: 'test' }) + + expect(registerRemoteChange).toBeCalledTimes(0) + await syncManager.sync('test') + expect(mockPull).toBeCalled() + expect(registerRemoteChange).toBeCalledTimes(0) + + await syncManager.startSync('test') + expect(registerRemoteChange).toBeCalledTimes(1) +}) + +it('should not trigger sync if collection is paused', async () => { + const mockPull = vi.fn<() => Promise>>().mockResolvedValue({ + items: [{ id: '1', name: 'Test Item' }], + }) + const mockPush = vi.fn<(options: any, pushParameters: any) => Promise>() + .mockResolvedValue() + + type SyncManagerOptions = ConstructorParameters[0] + type RegisterRemoteChangeParameters = Parameters> + type CollectionOptions = RegisterRemoteChangeParameters[0] + type RemoteChangeHandler = RegisterRemoteChangeParameters[1] + + let onRemoteChangeHandler: RemoteChangeHandler | undefined + const cleanupFunction = vi.fn() + const registerRemoteChange = vi.fn(( + options: CollectionOptions, + onRemoteChange: RemoteChangeHandler, + ) => { + onRemoteChangeHandler = onRemoteChange + return cleanupFunction + }) + const syncManager = new SyncManager({ + pull: mockPull, + push: mockPush, + registerRemoteChange, + autostart: false, + }) + + const collection = new Collection() + syncManager.addCollection(collection, { name: 'test' }) + + expect(registerRemoteChange).toBeCalledTimes(0) + expect(mockPull).toBeCalledTimes(0) + expect(onRemoteChangeHandler).toBeUndefined() + + await syncManager.startSync('test') + if (!onRemoteChangeHandler) return + expect(cleanupFunction).toBeCalledTimes(0) + expect(mockPull).toBeCalledTimes(0) + expect(registerRemoteChange).toBeCalledTimes(1) + expect(onRemoteChangeHandler).toBeDefined() + + await onRemoteChangeHandler() + expect(mockPull).toBeCalledTimes(1) + + await syncManager.pauseSync('test') + await onRemoteChangeHandler() + expect(cleanupFunction).toBeCalledTimes(1) + expect(mockPull).toBeCalledTimes(2) +}) + +it('should only automatically push if started', async () => { + const mockPull = vi.fn<() => Promise>>().mockResolvedValue({ + items: [{ id: '1', name: 'Test Item' }], + }) + + const mockPush = vi.fn<(options: any, pushParameters: any) => Promise>() + .mockResolvedValue() + const onError = vi.fn() + + const syncManager = new SyncManager({ + onError, + pull: mockPull, + push: mockPush, + autostart: false, + }) + + const mockCollection = new Collection() + + syncManager.addCollection(mockCollection, { name: 'test' }) + mockCollection.insert({ id: '2', name: 'New Item' }) + + await new Promise((resolve) => { + setTimeout(resolve, 110) + }) + + expect(onError).not.toHaveBeenCalled() + expect(mockPush).toHaveBeenCalledTimes(0) + + await syncManager.startSync('test') + await new Promise((resolve) => { + setTimeout(resolve, 110) + }) + expect(onError).not.toHaveBeenCalled() + expect(mockPush).toHaveBeenCalledTimes(1) +}) + +it('should handle an error during registerRemoteChange', async () => { + const mockPull = vi.fn<() => Promise>>().mockResolvedValue({ + items: [{ id: '1', name: 'Test Item' }], + }) + const mockPush = vi.fn<(options: any, pushParameters: any) => Promise>() + .mockResolvedValue() + const onError = vi.fn() + const syncManager = new SyncManager({ + pull: mockPull, + push: mockPush, + autostart: true, + registerRemoteChange: () => new Promise((resolve, reject) => { + reject(new Error('Failed to register remote change')) + }), + onError, + }) + + const collection = new Collection() + syncManager.addCollection(collection, { name: 'test' }) + await new Promise((resolve) => { + setTimeout(resolve, 0) + }) + expect(onError).toHaveBeenCalledTimes(1) +}) + +it('should return a tuple from getCollection function', () => { + const syncManager = new SyncManager({ + pull: vi.fn(), + push: vi.fn(), + }) + const collection = new Collection() + syncManager.addCollection(collection, { name: 'test' }) + // eslint-disable-next-line @typescript-eslint/no-deprecated + const [collectionInstance, collectionOptions] = syncManager.getCollection('test') + expect(collectionInstance).toBeInstanceOf(Collection) + expect(collectionOptions).toEqual({ name: 'test' }) +})