Skip to content

Commit

Permalink
Merge pull request #1052 from atsign-foundation/invalid_sync_regex
Browse files Browse the repository at this point in the history
fix: Add AtClientException in SyncProgress and return exception on invalid sync regex
  • Loading branch information
sitaram-kalluri authored Jun 6, 2023
2 parents 169f5b5 + eff2273 commit c43dada
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 80 deletions.
2 changes: 2 additions & 0 deletions packages/at_client/lib/src/service/sync/sync_status.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:at_client/at_client.dart';
import 'package:at_client/src/service/sync_service_impl.dart';

///Enum to represent the sync status
Expand All @@ -14,6 +15,7 @@ class SyncProgress {
int? localCommitIdBeforeSync;
int? localCommitId;
int? serverCommitId;
AtClientException? atClientException;

@override
String toString() {
Expand Down
20 changes: 18 additions & 2 deletions packages/at_client/lib/src/service/sync_service_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,24 @@ class SyncServiceImpl implements SyncService, AtSignChangeListener {
_atClient.getPreferences()!.atClientParticulars,
'RCVD: stats notification in sync: ${notification.value}'));
final serverCommitId = notification.value;
if (serverCommitId != null &&
int.parse(serverCommitId) > await _getLocalCommitId()) {
int localCommitId = -1;
try {
localCommitId = await _getLocalCommitId();
} on FormatException catch (e) {
_logger.finer('Exception occurred in statsListener ${e.message}');

_statsNotificationListener.stopAllSubscriptions();
var syncRequest = SyncRequest()
..result = (SyncResult()
..atClientException = AtClientException.message(e.message));
_syncError(syncRequest);

SyncProgress syncProgress = SyncProgress()
..atClientException = AtClientException.message(e.message)
..syncStatus = SyncStatus.failure;
_informSyncProgress(syncProgress);
}
if (serverCommitId != null && int.parse(serverCommitId) > localCommitId) {
final syncRequest = SyncRequest();
syncRequest.onDone = _onDone;
syncRequest.onError = _onError;
Expand Down
182 changes: 139 additions & 43 deletions packages/at_client/test/sync_new_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,10 @@ class MockAtClient extends Mock implements AtClient {
String? getCurrentAtSign() {
return TestResources.atsign;
}

@override
AtClientPreference getPreferences() {
return AtClientPreference();
}
}

class MockNotificationServiceImpl extends Mock
implements NotificationServiceImpl {
@override
Stream<at_notification.AtNotification> subscribe(
{String? regex, bool shouldDecrypt = false}) {
return StreamController<at_notification.AtNotification>().stream;
}
}
implements NotificationServiceImpl {}

class MockNetworkUtil extends Mock implements NetworkUtil {}

Expand Down Expand Up @@ -1199,6 +1188,11 @@ void main() {
when(() => mockAtClient.put(
any(that: LastReceivedServerCommitIdMatcher()), any()))
.thenAnswer((_) => Future.value(true));
when(() => mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer(
(_) => StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());

//instantiate sync service using mocks
SyncServiceImpl syncService = await SyncServiceImpl.create(mockAtClient,
Expand Down Expand Up @@ -1246,18 +1240,23 @@ void main() {
group(
'tests related to sending uncommitted entries to server via the batch verb',
() {
setUp(() async {
TestResources.atsign = '@alice';
await TestResources.setupLocalStorage(TestResources.atsign);
});

AtClient mockAtClient = MockAtClient();
AtClientManager mockAtClientManager = MockAtClientManager();
NotificationServiceImpl mockNotificationService =
MockNotificationServiceImpl();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
NetworkUtil mockNetworkUtil = MockNetworkUtil();

setUp(() async {
TestResources.atsign = '@alice';
await TestResources.setupLocalStorage(TestResources.atsign);
when(() => mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer(
(_) => StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());
});

/// Preconditions:
/// 1. The local commitId is 5 and hive_seq is also at 5
/// 2. There are 3 uncommitted entries - CommitOp.Update - 3.
Expand Down Expand Up @@ -2052,6 +2051,11 @@ void main() {
when(() => mockAtClient.put(
any(that: LastReceivedServerCommitIdMatcher()), any()))
.thenAnswer((_) => Future.value(true));
when(() => mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer(
(_) => StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());

SyncServiceImpl syncService = await SyncServiceImpl.create(mockAtClient,
atClientManager: mockAtClientManager,
Expand Down Expand Up @@ -2353,6 +2357,11 @@ void main() {
mockAtClient.get(any(that: LastReceivedServerCommitIdMatcher())))
.thenAnswer((invocation) =>
throw AtKeyNotFoundException('key is not found in keystore'));
when(() => mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer(
(_) => StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());

SyncServiceImpl syncService = await SyncServiceImpl.create(mockAtClient,
atClientManager: mockAtClientManager,
Expand Down Expand Up @@ -2415,6 +2424,12 @@ void main() {
mockRemoteSecondary = MockRemoteSecondary();
mockNetworkUtil = MockNetworkUtil();
mockSyncUtil = MockSyncUtil();

when(() => mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer(
(_) => StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());
});

/// The test should contain all types of keys - public key, shared key, self key
Expand Down Expand Up @@ -2727,18 +2742,21 @@ void main() {
});

group('A group of test to verify sync conflict resolution', () {
setUp(() async {
TestResources.atsign = '@hiro';
await TestResources.setupLocalStorage(TestResources.atsign);
});

AtClient mockAtClient = MockAtClient();
AtClientManager mockAtClientManager = MockAtClientManager();
NotificationServiceImpl mockNotificationService =
MockNotificationServiceImpl();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
NetworkUtil mockNetworkUtil = MockNetworkUtil();

setUp(() async {
TestResources.atsign = '@hiro';
await TestResources.setupLocalStorage(TestResources.atsign);
when(() => mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer(
(_) => StreamController<at_notification.AtNotification>().stream);
});

/// Preconditions:
/// 1. The server commit id should be greater than local commit id
/// 2. The server response should an contains a entry - @alice:phone@bob
Expand Down Expand Up @@ -2776,6 +2794,8 @@ void main() {
when(() => mockRemoteSecondary
.executeVerb(any(that: StatsVerbBuilderMatcher())))
.thenAnswer((invocation) => Future.value('data:[{"value":"3"}]'));
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());
when(() => mockRemoteSecondary.executeVerb(
any(that: SyncVerbBuilderMatcher()),
sync: any(
Expand Down Expand Up @@ -2884,7 +2904,8 @@ void main() {
when(() => mockRemoteSecondary
.executeVerb(any(that: StatsVerbBuilderMatcher())))
.thenAnswer((invocation) => Future.value('data:[{"value":"3"}]'));

when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());
when(() => mockRemoteSecondary.executeVerb(
any(that: SyncVerbBuilderMatcher()),
sync: any(
Expand Down Expand Up @@ -3013,18 +3034,22 @@ void main() {
/// 3. Server and client are already in sync
/// 4. sync request threshold is not met
group('A group of tests to verify sync trigger criteria', () {
setUp(() async {
TestResources.atsign = '@knox';
await TestResources.setupLocalStorage(TestResources.atsign);
});

AtClient mockAtClient = MockAtClient();
AtClientManager mockAtClientManager = MockAtClientManager();
NotificationServiceImpl mockNotificationService =
MockNotificationServiceImpl();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
NetworkUtil mockNetworkUtil = MockNetworkUtil();

setUp(() async {
TestResources.atsign = '@knox';
await TestResources.setupLocalStorage(TestResources.atsign);
when(() =>
mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer((_) =>
StreamController<at_notification.AtNotification>().stream);
});

///***********************************
///unable to assert if sync has happened
///***********************************
Expand Down Expand Up @@ -3119,6 +3144,8 @@ void main() {
.get(any(that: LastReceivedServerCommitIdMatcher())))
.thenAnswer((invocation) =>
throw AtKeyNotFoundException('key is not found in keystore'));
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());

//----------------------------Preconditions setup ----------------------
await localSecondary.putValue(
Expand Down Expand Up @@ -3253,6 +3280,8 @@ void main() {
.get(any(that: LastReceivedServerCommitIdMatcher())))
.thenAnswer((invocation) =>
throw AtKeyNotFoundException('key is not found in keystore'));
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());

//------------------Assertions -------------------
//onDoneCallback when triggered, flips the switch in TestResources
Expand Down Expand Up @@ -3280,18 +3309,18 @@ void main() {
});

group('A group of tests to verify isSyncInProgress flag', () {
setUp(() async {
TestResources.atsign = '@levi';
await TestResources.setupLocalStorage(TestResources.atsign);
});

AtClient mockAtClient = MockAtClient();
AtClientManager mockAtClientManager = MockAtClientManager();
NotificationServiceImpl mockNotificationService =
MockNotificationServiceImpl();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
NetworkUtil mockNetworkUtil = MockNetworkUtil();

setUp(() async {
TestResources.atsign = '@levi';
await TestResources.setupLocalStorage(TestResources.atsign);
});

/// Preconditions:
/// 1. Initially the isSyncInProgress is set to false.
/// 2. The server commit id is greater than local commit id
Expand Down Expand Up @@ -3349,6 +3378,12 @@ void main() {
.get(any(that: LastReceivedServerCommitIdMatcher())))
.thenAnswer((invocation) =>
throw AtKeyNotFoundException('key is not found in keystore'));
when(() =>
mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer((_) =>
StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());

SyncServiceImpl syncService = await SyncServiceImpl.create(mockAtClient,
atClientManager: mockAtClientManager,
Expand Down Expand Up @@ -3452,17 +3487,22 @@ void main() {
group(
'A group of tests to validate sync command - sync server changes to client',
() {
setUp(() async {
TestResources.atsign = '@nadia';
await TestResources.setupLocalStorage(TestResources.atsign);
});

AtClient mockAtClient = MockAtClient();
AtClientManager mockAtClientManager = MockAtClientManager();
NotificationServiceImpl mockNotificationService =
MockNotificationServiceImpl();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
NetworkUtil mockNetworkUtil = MockNetworkUtil();
setUp(() async {
TestResources.atsign = '@nadia';
await TestResources.setupLocalStorage(TestResources.atsign);
when(() =>
mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer((_) =>
StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());
});

/// Preconditions:
/// 1. The localCommitId is at commitId 5
Expand Down Expand Up @@ -3591,18 +3631,24 @@ void main() {
});

group('A group of test on sync progress call back', () {
setUp(() async {
TestResources.atsign = '@poland';
await TestResources.setupLocalStorage(TestResources.atsign);
});

AtClient mockAtClient = MockAtClient();
AtClientManager mockAtClientManager = MockAtClientManager();
NotificationServiceImpl mockNotificationService =
MockNotificationServiceImpl();
RemoteSecondary mockRemoteSecondary = MockRemoteSecondary();
MockNetworkUtil mockNetworkUtil = MockNetworkUtil();

setUp(() async {
TestResources.atsign = '@poland';
await TestResources.setupLocalStorage(TestResources.atsign);
when(() =>
mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer((_) =>
StreamController<at_notification.AtNotification>().stream);
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference());
});

/// Preconditions:
/// 1. Create a class that extends "SyncProgressListener" and override "onSyncProgressEvent" method
/// 2. ServerCommitId is greater than localCommitId. Say localCommitId is at 10 and serverCommitId is at 15
Expand Down Expand Up @@ -4002,6 +4048,56 @@ void main() {
}));
});

test(
'A test to verify exception is thrown when an invalid regex is supplied',
() async {
SyncUtil mockSyncUtil = MockSyncUtil();
when(() => mockAtClient.getPreferences())
.thenAnswer((_) => AtClientPreference()..syncRegex = '.buzz)');
when(() =>
mockNotificationService.subscribe(regex: 'statsNotification'))
.thenAnswer((_) {
var streamController =
StreamController<at_notification.AtNotification>();
// Adding a delay of 1 second to let the sync service initialize and
// add the progress listener to the sync service.
Future.delayed(Duration(seconds: 1)).then((_) {
streamController.add(at_notification.AtNotification(
'-1',
'statsNotification',
TestResources.atsign,
TestResources.atsign,
DateTime.now().millisecondsSinceEpoch,
MessageType.key.toString(),
false,
value: '10'));
});
return streamController.stream;
});

when(() =>
mockSyncUtil.getLastSyncedEntry(any(that: startsWith('.buzz)')),
atSign: TestResources.atsign)).thenAnswer(
(_) async => throw FormatException('.buzz) is not a valid regex'));

var syncServiceImpl = await SyncServiceImpl.create(mockAtClient,
atClientManager: mockAtClientManager,
notificationService: mockNotificationService,
remoteSecondary: mockRemoteSecondary) as SyncServiceImpl;
syncServiceImpl.syncUtil = mockSyncUtil;
var syncProgressListener = CustomSyncProgressListener();
syncServiceImpl.addProgressListener(syncProgressListener);

syncProgressListener.streamController.stream
.listen(expectAsync1((syncProgress) {
expect(syncProgress.syncStatus, SyncStatus.failure);
expect(syncProgress.atClientException, isA<AtClientException>());
expect(syncProgress.atClientException?.message,
'.buzz) is not a valid regex');
print(syncProgress.atClientException);
}));
});

tearDown(() async {
await TestResources.tearDownLocalStorage();
resetMocktailState();
Expand Down
Loading

0 comments on commit c43dada

Please sign in to comment.