Skip to content

Commit da53396

Browse files
Update tests for shared web workers
1 parent 1e880d6 commit da53396

File tree

8 files changed

+172
-258
lines changed

8 files changed

+172
-258
lines changed

packages/web/src/db/PowerSyncDatabase.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
import {
2-
type BucketStorageAdapter,
3-
type PowerSyncBackendConnector,
4-
type PowerSyncCloseOptions,
5-
type RequiredAdditionalConnectionOptions,
62
AbstractPowerSyncDatabase,
73
DBAdapter,
8-
DEFAULT_POWERSYNC_CLOSE_OPTIONS,
9-
isDBAdapter,
10-
isSQLOpenFactory,
114
PowerSyncDatabaseOptions,
125
PowerSyncDatabaseOptionsWithDBAdapter,
136
PowerSyncDatabaseOptionsWithOpenFactory,
147
PowerSyncDatabaseOptionsWithSettings,
158
SqliteBucketStorage,
16-
StreamingSyncImplementation
9+
StreamingSyncImplementation,
10+
isDBAdapter,
11+
isSQLOpenFactory,
12+
type BucketStorageAdapter,
13+
type PowerSyncBackendConnector,
14+
type PowerSyncCloseOptions,
15+
type RequiredAdditionalConnectionOptions
1716
} from '@powersync/common';
1817
import { Mutex } from 'async-mutex';
1918
import { getNavigatorLocks } from '../shared/navigator';
19+
import { WebDBAdapter } from './adapters/WebDBAdapter';
2020
import { WASQLiteOpenFactory } from './adapters/wa-sqlite/WASQLiteOpenFactory';
2121
import {
2222
DEFAULT_WEB_SQL_FLAGS,
2323
ResolvedWebSQLOpenOptions,
24-
resolveWebSQLFlags,
25-
WebSQLFlags
24+
WebSQLFlags,
25+
resolveWebSQLFlags
2626
} from './adapters/web-sql-flags';
27-
import { WebDBAdapter } from './adapters/WebDBAdapter';
28-
import { SharedWebStreamingSyncImplementation } from './sync/SharedWebStreamingSyncImplementation';
2927
import { SSRStreamingSyncImplementation } from './sync/SSRWebStreamingSyncImplementation';
28+
import { SharedWebStreamingSyncImplementation } from './sync/SharedWebStreamingSyncImplementation';
3029
import { WebRemote } from './sync/WebRemote';
3130
import {
3231
WebStreamingSyncImplementation,
@@ -160,14 +159,13 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
160159
* By default the sync stream client is only disconnected if
161160
* multiple tabs are not enabled.
162161
*/
163-
close(options: PowerSyncCloseOptions = DEFAULT_POWERSYNC_CLOSE_OPTIONS): Promise<void> {
162+
close(options?: PowerSyncCloseOptions): Promise<void> {
164163
if (this.unloadListener) {
165164
window.removeEventListener('unload', this.unloadListener);
166165
}
167-
168166
return super.close({
169167
// Don't disconnect by default if multiple tabs are enabled
170-
disconnect: options.disconnect ?? !this.resolvedFlags.enableMultiTabs
168+
disconnect: options?.disconnect ?? !this.resolvedFlags.enableMultiTabs
171169
});
172170
}
173171

packages/web/src/db/sync/SharedWebStreamingSyncImplementation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,6 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
255255
async dispose(): Promise<void> {
256256
await this.waitForReady();
257257

258-
await super.dispose();
259-
260258
await new Promise<void>((resolve) => {
261259
// Listen for the close acknowledgment from the worker
262260
this.messagePort.addEventListener('message', (event) => {
@@ -273,6 +271,9 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
273271
};
274272
this.messagePort.postMessage(closeMessagePayload);
275273
});
274+
275+
await super.dispose();
276+
276277
this.abortOnClose.abort();
277278

278279
// Release the proxy

packages/web/src/worker/sync/MockSyncServiceWorker.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ export class MockSyncService {
1515
* Register a new pending request (called by WebRemote when a sync stream is requested).
1616
* Returns a promise that resolves when a client creates a response for this request.
1717
*/
18-
registerPendingRequest(url: string, method: string, headers: Record<string, string>, body: any): Promise<Response> {
18+
registerPendingRequest(
19+
url: string,
20+
method: string,
21+
headers: Record<string, string>,
22+
body: any,
23+
signal?: AbortSignal
24+
): Promise<Response> {
1925
const id = `pending-${++this.nextId}`;
2026

2127
let resolveResponse: (response: Response) => void;
@@ -40,6 +46,20 @@ export class MockSyncService {
4046

4147
this.pendingRequests.set(id, pendingRequest);
4248

49+
signal?.addEventListener('abort', () => {
50+
this.pendingRequests.delete(id);
51+
rejectResponse(new Error('Request aborted'));
52+
53+
// if already in active responses, remove it
54+
if (this.activeResponses.has(id)) {
55+
const response = this.activeResponses.get(id);
56+
if (response) {
57+
response.stream.close();
58+
}
59+
this.activeResponses.delete(id);
60+
}
61+
});
62+
4363
// Return the promise - it will resolve when createResponse is called
4464
return responsePromise;
4565
}

packages/web/tests/mockSyncServiceExample.test.ts

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,9 @@ describe('Mock Sync Service Example', { timeout: 100000 }, () => {
1717
sharedMockSyncServiceTest(
1818
'should allow mocking sync responses in shared worker',
1919
{ timeout: 100000 },
20-
async ({ database, connect }) => {
20+
async ({ context: { database, connect } }) => {
2121
// Call connect to start the sync worker and get the sync service
22-
const { syncService, connectionPromise } = await connect();
23-
24-
// Wait for a pending request to appear
25-
let pendingRequestId: string;
26-
await vi.waitFor(async () => {
27-
const requests = await syncService.getPendingRequests();
28-
expect(requests.length).toBeGreaterThan(0);
29-
pendingRequestId = requests[0].id;
30-
});
31-
32-
// Get the pending request to inspect it
33-
const requests = await syncService.getPendingRequests();
34-
expect(requests).toHaveLength(1);
35-
expect(requests[0].url).toContain('/sync/stream');
36-
expect(requests[0].method).toBe('POST');
37-
expect(requests[0].headers).toBeDefined();
38-
expect(requests[0].body).toBeDefined();
39-
40-
// Create a response for the pending request
41-
await syncService.createResponse(pendingRequestId!, 200, { 'Content-Type': 'application/json' });
22+
const { syncService, syncRequestId } = await connect();
4223

4324
// Push a checkpoint with buckets (following node test pattern)
4425
const checkpoint: StreamingSyncCheckpoint = {
@@ -56,13 +37,10 @@ describe('Mock Sync Service Example', { timeout: 100000 }, () => {
5637
}
5738
};
5839

59-
await syncService.pushBodyLine(pendingRequestId!, checkpoint);
40+
await syncService.pushBodyLine(syncRequestId, checkpoint);
6041

6142
// The connect call should resolve by now
62-
await connectionPromise;
63-
64-
// Push data line with PUT operation to save data (following node test pattern)
65-
await syncService.pushBodyLine(pendingRequestId!, {
43+
await syncService.pushBodyLine(syncRequestId, {
6644
data: {
6745
bucket: 'a',
6846
data: [
@@ -79,14 +57,14 @@ describe('Mock Sync Service Example', { timeout: 100000 }, () => {
7957
});
8058

8159
// Push checkpoint_complete to finish the sync
82-
await syncService.pushBodyLine(pendingRequestId!, {
60+
await syncService.pushBodyLine(syncRequestId, {
8361
checkpoint_complete: {
8462
last_op_id: '1'
8563
}
8664
});
8765

8866
// Complete the response
89-
await syncService.completeResponse(pendingRequestId!);
67+
await syncService.completeResponse(syncRequestId);
9068

9169
// Wait for sync to complete and verify the data was saved
9270
await vi.waitFor(async () => {

packages/web/tests/mocks/MockWebRemote.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
RemoteConnector,
1111
SocketSyncStreamOptions
1212
} from '@powersync/common';
13-
import type { BSON } from 'bson';
13+
import { serialize, type BSON } from 'bson';
1414
import { getMockSyncService, setupMockServiceMessageHandler } from '../utils/MockSyncService';
1515

1616
/**
@@ -77,7 +77,7 @@ class MockSyncServiceFetchProvider extends FetchImplementationProvider {
7777
});
7878

7979
// Register as a pending request and wait for client to create response
80-
return await mockService.registerPendingRequest(request.url, request.method, headers, body);
80+
return await mockService.registerPendingRequest(request.url, request.method, headers, body, request.signal);
8181
}
8282

8383
// Fallback if mock service is not available
@@ -189,6 +189,6 @@ export class WebRemote extends AbstractRemote {
189189
bson?: typeof BSON
190190
): Promise<DataStream<T>> {
191191
// postStreamRaw decodes to strings, so convert back to Uint8Array for the map function
192-
return await this.postStreamRaw(options, (line: string) => map(new TextEncoder().encode(line)));
192+
return await this.postStreamRaw(options, (line: string) => map(serialize(JSON.parse(line))));
193193
}
194194
}

0 commit comments

Comments
 (0)