Skip to content

Commit ca2a752

Browse files
committed
Fix infinite loop after token expires on web
1 parent 7e87350 commit ca2a752

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

packages/powersync_core/lib/src/web/sync_controller.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class SyncWorkerHandle implements StreamingSync {
4848
await connector.uploadData(database);
4949
return (JSObject(), null);
5050
case SyncWorkerMessageType.invalidCredentialsCallback:
51-
final credentials = await connector.fetchCredentials();
51+
final credentials = await connector.prefetchCredentials();
5252
return (
5353
credentials != null
5454
? SerializedCredentials.from(credentials)

packages/powersync_core/test/in_memory_sync_test.dart

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,26 @@ void main() {
2020
late CommonDatabase raw;
2121
late PowerSyncDatabase database;
2222
late MockSyncService syncService;
23-
late StreamingSyncImplementation syncClient;
23+
late StreamingSync syncClient;
24+
var credentialsCallbackCount = 0;
2425

2526
setUp(() async {
27+
credentialsCallbackCount = 0;
2628
final (client, server) = inMemoryServer();
2729
syncService = MockSyncService();
2830
server.mount(syncService.router.call);
2931

3032
factory = await testUtils.testFactory();
3133
(raw, database) = await factory.openInMemoryDatabase();
3234
await database.initialize();
35+
3336
syncClient = database.connectWithMockService(
3437
client,
3538
TestConnector(() async {
39+
credentialsCallbackCount++;
3640
return PowerSyncCredentials(
3741
endpoint: server.url.toString(),
38-
token: 'token not used here',
42+
token: 'token$credentialsCallbackCount',
3943
expiresAt: DateTime.now(),
4044
);
4145
}),
@@ -312,18 +316,37 @@ void main() {
312316
},
313317
);
314318
});
319+
320+
test('reconnects when token expires', () async {
321+
await waitForConnection();
322+
expect(credentialsCallbackCount, 1);
323+
// When the sync service says the token has expired
324+
syncService
325+
..addLine({'token_expires_in': 0})
326+
..endCurrentListener();
327+
328+
final nextRequest = await syncService.waitForListener;
329+
expect(nextRequest.headers['Authorization'], 'Token token2');
330+
expect(credentialsCallbackCount, 2);
331+
});
315332
});
316333
}
317334

318335
TypeMatcher<SyncStatus> isSyncStatus(
319-
{Object? downloading, Object? connected, Object? hasSynced}) {
336+
{Object? downloading,
337+
Object? connected,
338+
Object? connecting,
339+
Object? hasSynced}) {
320340
var matcher = isA<SyncStatus>();
321341
if (downloading != null) {
322342
matcher = matcher.having((e) => e.downloading, 'downloading', downloading);
323343
}
324344
if (connected != null) {
325345
matcher = matcher.having((e) => e.connected, 'connected', connected);
326346
}
347+
if (connecting != null) {
348+
matcher = matcher.having((e) => e.connecting, 'connecting', connecting);
349+
}
327350
if (hasSynced != null) {
328351
matcher = matcher.having((e) => e.hasSynced, 'hasSynced', hasSynced);
329352
}

packages/powersync_core/test/server/sync_server/in_memory_sync_server.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import 'package:shelf_router/shelf_router.dart';
66

77
final class MockSyncService {
88
// Use a queued stream to make tests easier.
9-
StreamController<String> _controller = StreamController<String>();
10-
Completer<void> _listener = Completer();
9+
StreamController<String> _controller = StreamController();
10+
Completer<Request> _listener = Completer();
1111

1212
final router = Router();
1313

1414
MockSyncService() {
1515
router
1616
..post('/sync/stream', (Request request) async {
17-
_listener.complete();
17+
_listener.complete(request);
1818
// Respond immediately with a stream
1919
return Response.ok(_controller.stream.transform(utf8.encoder),
2020
headers: {
@@ -33,7 +33,7 @@ final class MockSyncService {
3333
});
3434
}
3535

36-
Future<void> get waitForListener => _listener.future;
36+
Future<Request> get waitForListener => _listener.future;
3737

3838
// Queue events which will be sent to connected clients.
3939
void addRawEvent(String data) {
@@ -48,6 +48,12 @@ final class MockSyncService {
4848
addLine({'token_expires_in': tokenExpiresIn});
4949
}
5050

51+
void endCurrentListener() {
52+
_controller.close();
53+
_controller = StreamController();
54+
_listener = Completer();
55+
}
56+
5157
// Clear events. We rely on a buffered controller here. Create a new controller
5258
// in order to clear the buffer.
5359
Future<void> clearEvents() async {

0 commit comments

Comments
 (0)