diff --git a/packages/at_client/lib/src/client/request_options.dart b/packages/at_client/lib/src/client/request_options.dart index 43d5eca0a..a9545af09 100644 --- a/packages/at_client/lib/src/client/request_options.dart +++ b/packages/at_client/lib/src/client/request_options.dart @@ -22,6 +22,10 @@ class PutRequestOptions extends RequestOptions { /// Whether to send this update request directly to the remote atServer bool useRemoteAtServer = false; + + /// Except public keys, shared keys and self keys are encrypted by default. + /// If client prefers not to encrypt a shared key or self key/ use their own encryption scheme, set this flag to false. + bool shouldEncrypt = true; } /// Parameters that application code can optionally provide when calling diff --git a/packages/at_client/lib/src/preference/at_client_preference.dart b/packages/at_client/lib/src/preference/at_client_preference.dart index f6f51931e..5eeaa8a95 100644 --- a/packages/at_client/lib/src/preference/at_client_preference.dart +++ b/packages/at_client/lib/src/preference/at_client_preference.dart @@ -101,7 +101,6 @@ class AtClientPreference { /// Which version of the atProtocol this client will use. /// Note that this is different from the version of the - /// Note that this is different from the version of the /// atProtocol that the client supports, which is set in /// [AtClientConfig] /// This instance variable is experimental, for now diff --git a/packages/at_client/lib/src/transformer/request_transformer/put_request_transformer.dart b/packages/at_client/lib/src/transformer/request_transformer/put_request_transformer.dart index 1f60fc3c0..a77a54816 100644 --- a/packages/at_client/lib/src/transformer/request_transformer/put_request_transformer.dart +++ b/packages/at_client/lib/src/transformer/request_transformer/put_request_transformer.dart @@ -37,38 +37,58 @@ class PutRequestTransformer AtClientUtil.fixAtSign(updateVerbBuilder.atKey.sharedBy); // Setting updateVerbBuilder.value updateVerbBuilder.value = tuple.two; - - //Encrypt the data for non public keys - if (!updateVerbBuilder.atKey.metadata.isPublic) { - var encryptionService = AtKeyEncryptionManager(_atClient) - .get(updateVerbBuilder.atKey, _atClient.getCurrentAtSign()!); - try { - updateVerbBuilder.value = await encryptionService.encrypt( - updateVerbBuilder.atKey, updateVerbBuilder.value, - storeSharedKeyEncryptedWithData: - options.storeSharedKeyEncryptedMetadata); - } on AtException catch (e) { - e.stack(AtChainedException(Intent.shareData, - ExceptionScenario.encryptionFailed, 'Failed to encrypt the data')); - rethrow; - } + final atKey = updateVerbBuilder.atKey; + final metadata = atKey.metadata; + // Check if the data needs to be encrypted for non-public keys + if (!_isPublicKey(metadata) && options.shouldEncrypt) { + await _encryptData(updateVerbBuilder, options); } else { - if (encryptionPrivateKey.isNull) { - throw AtPrivateKeyNotFoundException('Failed to sign the public data'); - } - final atSigningInput = AtSigningInput(updateVerbBuilder.value) - ..signingMode = AtSigningMode.data; - final signingResult = _atClient.atChops!.sign(atSigningInput); - updateVerbBuilder.atKey.metadata.dataSignature = signingResult.result; - // Encode the public data if it contains new line characters - if (updateVerbBuilder.value.contains('\n')) { - updateVerbBuilder.value = - AtEncoderImpl().encodeData(updateVerbBuilder.value, encodingType); - updateVerbBuilder.atKey.metadata.encoding = - encodingType.toShortString(); + // Sign the data for public keys + if (_isPublicKey(metadata)) { + _signPublicData(updateVerbBuilder, encryptionPrivateKey); } + // Encode the data if it contains new line characters + _encodeIfValueContainsNewLine(updateVerbBuilder); } return updateVerbBuilder; } + + Future _encryptData( + UpdateVerbBuilder updateVerbBuilder, PutRequestOptions options) async { + var encryptionService = AtKeyEncryptionManager(_atClient) + .get(updateVerbBuilder.atKey, _atClient.getCurrentAtSign()!); + try { + updateVerbBuilder.value = await encryptionService.encrypt( + updateVerbBuilder.atKey, updateVerbBuilder.value, + storeSharedKeyEncryptedWithData: + options.storeSharedKeyEncryptedMetadata); + updateVerbBuilder.atKey.metadata.isEncrypted = true; + } on AtException catch (e) { + e.stack(AtChainedException(Intent.shareData, + ExceptionScenario.encryptionFailed, 'Failed to encrypt the data')); + rethrow; + } + } + + void _signPublicData( + UpdateVerbBuilder updateVerbBuilder, String? encryptionPrivateKey) { + if (encryptionPrivateKey.isNull) { + throw AtPrivateKeyNotFoundException('Failed to sign the public data'); + } + final atSigningInput = AtSigningInput(updateVerbBuilder.value) + ..signingMode = AtSigningMode.data; + final signingResult = _atClient.atChops!.sign(atSigningInput); + updateVerbBuilder.atKey.metadata.dataSignature = signingResult.result; + } + + void _encodeIfValueContainsNewLine(UpdateVerbBuilder updateVerbBuilder) { + if (updateVerbBuilder.value.contains('\n')) { + updateVerbBuilder.value = + AtEncoderImpl().encodeData(updateVerbBuilder.value, encodingType); + updateVerbBuilder.atKey.metadata.encoding = encodingType.toShortString(); + } + } + + bool _isPublicKey(Metadata metadata) => metadata.isPublic; } diff --git a/packages/at_client/lib/src/transformer/response_transformer/get_response_transformer.dart b/packages/at_client/lib/src/transformer/response_transformer/get_response_transformer.dart index 517b6b09a..dabd63e10 100644 --- a/packages/at_client/lib/src/transformer/response_transformer/get_response_transformer.dart +++ b/packages/at_client/lib/src/transformer/response_transformer/get_response_transformer.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:at_client/src/client/at_client_spec.dart'; import 'package:at_base2e15/at_base2e15.dart'; import 'package:at_client/src/converters/decoder/at_decoder.dart'; +import 'package:at_client/src/decryption_service/decryption.dart'; import 'package:at_client/src/decryption_service/decryption_manager.dart'; import 'package:at_client/src/response/default_response_parser.dart'; import 'package:at_client/src/response/json_utils.dart'; @@ -17,7 +18,7 @@ import 'package:at_commons/at_commons.dart'; class GetResponseTransformer implements Transformer, AtValue> { late final AtClient _atClient; - + AtKeyDecryptionManager? decryptionManager; GetResponseTransformer(this._atClient); @override @@ -35,39 +36,67 @@ class GetResponseTransformer atValue.metadata = metadata; tuple.one.metadata = metadata!; } - // For public and cached public keys, data is not encrypted. + if (_isKeyPublic(decodedResponse['key'])) { + return _handlePublicData(atValue, tuple); + } + decryptionManager ??= AtKeyDecryptionManager(_atClient); + var decryptionService = + decryptionManager!.get(tuple.one, _atClient.getCurrentAtSign()!); // Decrypt the data, for other keys - if (!(decodedResponse['key'].startsWith('public:')) && - !(decodedResponse['key'].startsWith('cached:public:'))) { - var decryptionService = AtKeyDecryptionManager(_atClient) - .get(tuple.one, _atClient.getCurrentAtSign()!); + // For new encrypted data after AtClient v3.2.1, isEncrypted will be true(default value for PutRequestOptions.shouldEncrypt) for self and shared keys + // isEncrypted will be false if client sets PutRequestOptions.shouldEncrypt to false + if (_shouldDecrypt(atValue.metadata)) { + atValue.value = await _decrypt(atValue, decryptionService, tuple.one); + } else { + // for old data, try decrypting the value. if decryption fails, set the original value. try { - atValue.value = - await decryptionService.decrypt(tuple.one, atValue.value) as String; - } on AtException catch (e) { - e.stack(AtChainedException(Intent.fetchData, - ExceptionScenario.decryptionFailed, 'Failed to decrypt the data')); - rethrow; + atValue.value = await _decrypt(atValue, decryptionService, tuple.one); + } on FormatException { + // trying to decrypt plain data will result in FormatException. + if (atValue.metadata!.encoding != null) { + atValue.value = AtDecoderImpl() + .decodeData(atValue.value, atValue.metadata!.encoding!); + } } } + // After decrypting the data, if data is binary, decode the data + // For cached keys, isBinary is not on server-side. Hence getting + // isBinary from AtKey. + if (tuple.one.metadata.isBinary) { + atValue.value = Base2e15.decode(atValue.value); + } + return atValue; + } - if (((decodedResponse['key'].startsWith('public:')) || - (decodedResponse['key'].startsWith('cached:public:'))) && - (atValue.metadata!.encoding.isNotNull)) { + AtValue _handlePublicData(AtValue atValue, Tuple tuple) { + if (atValue.metadata?.encoding != null) { atValue.value = AtDecoderImpl() .decodeData(atValue.value, atValue.metadata!.encoding!); } - // After decrypting the data, if data is binary, decode the data - // For cached keys, isBinary is not on server-side. Hence getting - // isBinary from AtKey. if (tuple.one.metadata.isBinary) { atValue.value = Base2e15.decode(atValue.value); } + return atValue; } + Future _decrypt( + AtValue atValue, AtKeyDecryption decryptionService, AtKey atKey) async { + try { + return await decryptionService.decrypt(atKey, atValue.value) as String; + } on AtException catch (e) { + e.stack(AtChainedException(Intent.fetchData, + ExceptionScenario.decryptionFailed, 'Failed to decrypt the data')); + rethrow; + } + } + + bool _shouldDecrypt(Metadata? metadata) { + return metadata != null && metadata.isEncrypted; + } + /// Return true if key is a public key or a cached public key /// Else returns false bool _isKeyPublic(String key) { diff --git a/packages/at_client/pubspec.yaml b/packages/at_client/pubspec.yaml index df8a0c824..78abb0f9e 100644 --- a/packages/at_client/pubspec.yaml +++ b/packages/at_client/pubspec.yaml @@ -41,6 +41,18 @@ dependencies: meta: ^1.8.0 version: ^3.0.2 +dependency_overrides: + at_commons: + git: + url: https://github.com/atsign-foundation/at_libraries.git + ref: update_isencrypted_changes + path: packages/at_commons + at_lookup: + git: + url: https://github.com/atsign-foundation/at_libraries.git + ref: at_lookup_publish + path: packages/at_lookup + dev_dependencies: lints: ^4.0.0 test: ^1.21.4 diff --git a/packages/at_client/test/get_request_test.dart b/packages/at_client/test/get_request_test.dart deleted file mode 100644 index 58c696ea7..000000000 --- a/packages/at_client/test/get_request_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:at_client/at_client.dart'; -import 'package:at_client/src/converters/encoder/at_encoder.dart'; -import 'package:at_client/src/transformer/response_transformer/get_response_transformer.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:test/test.dart'; - -class MockAtClient extends Mock implements AtClient { - @override - AtClientPreference getPreferences() { - return AtClientPreference()..namespace = 'wavi'; - } -} - -void main() { - AtClient mockAtClient = MockAtClient(); - group('A group of test to validate decoding of public data', () { - test('A test to validate new line character decoding', () async { - var atKey = AtKey() - ..key = 'key1' - ..namespace = 'wavi' - ..metadata = (Metadata() - ..isPublic = true - ..encoding = EncodingType.base64.toString()); - var value = - 'data:{"key":"public:key1.wavi@sitaram","data":"dmFsdWUKMQ==","metaData":{"createdBy":null,"updatedBy":null,"createdAt":"2022-07-26 17:46:50.247Z","updatedAt":"2022-07-26 17:50:12.680Z","availableAt":"2022-07-26 17:50:12.680Z","expiresAt":null,"refreshAt":null,"status":"active","version":0,"ttl":0,"ttb":0,"ttr":0,"ccd":null,"isBinary":false,"isEncrypted":false,"dataSignature":"l7LsM9t68Xd2NNHsMqFzh9aiUQO7NixEMpZ3KjduwnbpnLLesutglvFaabBTiX/BYAjatqC43tkk/ERUvvKP85GfYbsSjrVqMDE5dbwU1i13bWzL0TeUJUT3H7xVARjwrahHjvlw8u35rm/I2sPlqmxTtrFXZM89ByfdGJsrh4opd1cXfLf10W2wx7pIePfmeWCnam2stLVTSx4mWOV8sUdDcN5Mrh2FjVWGGewQOrktxGogn0KkENq/cmm41I5xZaMpVTVL/RvVUEJ8obgpmauN23puedq/HPBRUAoAL8LFnzji5PtVcZoAYvqZrjKKi4ac7ZOEy4xWXIr4ps9zSA==","sharedKeyEnc":null,"pubKeyCS":null,"encoding":"EncodingType.base64"}}'; - var expectedValue = 'value\n1'; - var atValue = await GetResponseTransformer(mockAtClient).transform(Tuple() - ..one = atKey - ..two = value); - expect(atValue.value, expectedValue); - }); - }); -} diff --git a/packages/at_client/test/get_response_transformer_test.dart b/packages/at_client/test/get_response_transformer_test.dart new file mode 100644 index 000000000..132bd883e --- /dev/null +++ b/packages/at_client/test/get_response_transformer_test.dart @@ -0,0 +1,181 @@ +import 'package:at_base2e15/at_base2e15.dart'; +import 'package:at_client/at_client.dart'; +import 'package:at_client/src/converters/encoder/at_encoder.dart'; +import 'package:at_client/src/decryption_service/decryption.dart'; +import 'package:at_client/src/decryption_service/decryption_manager.dart'; +import 'package:at_client/src/transformer/response_transformer/get_response_transformer.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; + +class MockAtClient extends Mock implements AtClient { + @override + AtClientPreference getPreferences() { + return AtClientPreference()..namespace = 'wavi'; + } +} + +class MockAtKeyDecryptionManager extends Mock + implements AtKeyDecryptionManager {} + +class MockAtKeyDecryption extends Mock implements AtKeyDecryption {} + +void main() { + group('A group of test for GetResponseTransformer', () { + late MockAtClient mockAtClient; + late MockAtKeyDecryptionManager mockDecryptionManager; + late MockAtKeyDecryption mockDecryptionService; + late GetResponseTransformer transformer; + // late Tuple mockTuple; + setUp(() { + mockAtClient = MockAtClient(); + mockDecryptionManager = MockAtKeyDecryptionManager(); + mockDecryptionService = MockAtKeyDecryption(); + transformer = GetResponseTransformer(mockAtClient) + ..decryptionManager = mockDecryptionManager; + when(() => mockAtClient.getCurrentAtSign()).thenReturn('@alice'); + }); + test('A test to validate new line character decoding', () async { + var atKey = AtKey() + ..key = 'key1' + ..namespace = 'wavi' + ..metadata = (Metadata() + ..isPublic = true + ..encoding = EncodingType.base64.toString()); + var value = + 'data:{"key":"public:key1.wavi@sitaram","data":"dmFsdWUKMQ==","metaData":{"createdBy":null,"updatedBy":null,"createdAt":"2022-07-26 17:46:50.247Z","updatedAt":"2022-07-26 17:50:12.680Z","availableAt":"2022-07-26 17:50:12.680Z","expiresAt":null,"refreshAt":null,"status":"active","version":0,"ttl":0,"ttb":0,"ttr":0,"ccd":null,"isBinary":false,"isEncrypted":false,"dataSignature":"l7LsM9t68Xd2NNHsMqFzh9aiUQO7NixEMpZ3KjduwnbpnLLesutglvFaabBTiX/BYAjatqC43tkk/ERUvvKP85GfYbsSjrVqMDE5dbwU1i13bWzL0TeUJUT3H7xVARjwrahHjvlw8u35rm/I2sPlqmxTtrFXZM89ByfdGJsrh4opd1cXfLf10W2wx7pIePfmeWCnam2stLVTSx4mWOV8sUdDcN5Mrh2FjVWGGewQOrktxGogn0KkENq/cmm41I5xZaMpVTVL/RvVUEJ8obgpmauN23puedq/HPBRUAoAL8LFnzji5PtVcZoAYvqZrjKKi4ac7ZOEy4xWXIr4ps9zSA==","sharedKeyEnc":null,"pubKeyCS":null,"encoding":"EncodingType.base64"}}'; + var expectedValue = 'value\n1'; + var atValue = await GetResponseTransformer(mockAtClient).transform(Tuple() + ..one = atKey + ..two = value); + expect(atValue.value, expectedValue); + }); + test('A test to verify response transformer for public key', () async { + final keyName = 'phone'; + final atKey = AtKey() + ..metadata = (Metadata()..isPublic = true) + ..key = keyName; + final tuple = Tuple() + ..one = atKey + ..two = + '{"data": "my_public_phone_number", "key": "public:$keyName@alice"}'; + var result = await transformer.transform(tuple); + + expect(result.value, equals('my_public_phone_number')); + }); + test( + 'A test to verify response transformer for public key with encoding set', + () async { + final keyName = 'phone'; + final atKey = AtKey() + ..metadata = (Metadata()..isPublic = true) + ..key = keyName; + var base64EncodedString = AtEncoderImpl() + .encodeData("my_public_phone_number", EncodingType.base64); + final tuple = Tuple() + ..one = atKey + ..two = + '{"data": "$base64EncodedString", "key": "public:$keyName@alice", "metaData": {"encoding": "base64"}}'; + var result = await transformer.transform(tuple); + + expect(result.value, equals('my_public_phone_number')); + }); + + test('A test to verify response transformer for public key binary data', + () async { + final keyName = 'phone'; + final atKey = AtKey() + ..metadata = (Metadata() + ..isPublic = true + ..isBinary = true) + ..key = keyName; + final dataInBytes = "my_public_phone_number".codeUnits; + var binaryData = Base2e15.encode(dataInBytes); + final tuple = Tuple() + ..one = atKey + ..two = '{"data": "$binaryData", "key": "public:$keyName@alice"}'; + var result = await transformer.transform(tuple); + + expect(result.value, equals(dataInBytes)); + }); + + test('A test to verify response transformer for cached public key', + () async { + final keyName = 'phone'; + final atKey = AtKey() + ..metadata = (Metadata()..isPublic = true) + ..key = keyName; + final tuple = Tuple() + ..one = atKey + ..two = + '{"data": "my_public_phone_number", "key": "cached:public:$keyName@alice"}'; + var result = await transformer.transform(tuple); + + expect(result.value, equals('my_public_phone_number')); + }); + test( + 'A test to verify response transformer for encrypted data with isEncrypted set to true', + () async { + final keyName = 'phone'; + final atKey = AtKey() + ..metadata = Metadata() + ..key = keyName + ..sharedBy = '@alice' + ..sharedWith = '@bob'; + final tuple = Tuple() + ..one = atKey + ..two = + '{"data": "shared_phone_number", "key": "@bob:$keyName@alice","metaData": {"isEncrypted": true}}'; + when(() => mockDecryptionManager.get(atKey, '@alice')) + .thenReturn(mockDecryptionService); + when(() => mockDecryptionService.decrypt(atKey, "shared_phone_number")) + .thenAnswer((_) async => 'decrypted_data'); + + var result = await transformer.transform(tuple); + + expect(result.value, equals('decrypted_data')); + }); + + test( + 'A test to verify response transformer for encrypted data with isEncrypted set to false(for old data)', + () async { + final keyName = 'phone'; + final atKey = AtKey() + ..metadata = Metadata() + ..key = keyName + ..sharedBy = '@alice' + ..sharedWith = '@bob'; + final tuple = Tuple() + ..one = atKey + ..two = + '{"data": "shared_phone_number", "key": "@bob:$keyName@alice","metaData": {"isEncrypted": false}}'; + when(() => mockDecryptionManager.get(atKey, '@alice')) + .thenReturn(mockDecryptionService); + when(() => mockDecryptionService.decrypt(atKey, "shared_phone_number")) + .thenAnswer((_) async => 'decrypted_data'); + + var result = await transformer.transform(tuple); + + expect(result.value, equals('decrypted_data')); + }); + + test('A test to verify transform throws AtException on decryption failure', + () async { + final atKey = AtKey() + ..metadata = Metadata() + ..key = 'phone' + ..sharedBy = '@alice' + ..sharedWith = '@bob'; + final tuple = Tuple() + ..one = atKey + ..two = + '{"data": "shared_phone_number", "key": "@bob:phone@alice","metaData": {"isEncrypted": true}}'; + when(() => mockDecryptionManager.get(atKey, '@alice')) + .thenReturn(mockDecryptionService); + when(() => mockDecryptionService.decrypt(atKey, "shared_phone_number")) + .thenThrow(AtException('Decryption failed')); + + expect(() async => await transformer.transform(tuple), + throwsA(isA())); + }); + }); +} diff --git a/tests/at_functional_test/test/atclient_put_test.dart b/tests/at_functional_test/test/atclient_put_test.dart index 78c884ed5..163062929 100644 --- a/tests/at_functional_test/test/atclient_put_test.dart +++ b/tests/at_functional_test/test/atclient_put_test.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:at_client/at_client.dart'; import 'package:at_functional_test/src/config_util.dart'; +import 'package:at_functional_test/src/sync_service.dart'; import 'package:test/test.dart'; import 'test_utils.dart'; @@ -19,20 +20,59 @@ void main() { atClientManager.atClient.syncService.sync(); }); - test('put method - create a key sharing to other atSign', () async { - // phone.me@aliceđź›  + test( + 'put method - create a key sharing to other atSign and verify metadata isEncrypted from server', + () async { var phoneKey = AtKey() ..key = 'phone' - ..sharedWith = '@bobđź› '; + ..sharedWith = '@bobđź› ' + ..sharedBy = atSign; var value = '+1 100 200 300'; var putResult = await atClientManager.atClient.put(phoneKey, value); expect(putResult, true); var getResult = await atClientManager.atClient.get(phoneKey); expect(getResult.value, value); + expect(getResult.metadata?.isEncrypted, true); + await FunctionalTestSyncService.getInstance().syncData( + AtClientManager.getInstance().atClient.syncService, + syncOptions: SyncOptions()..key = phoneKey.toString()); + // fetch the key from remote and verify isEncrypted + var getResultRemote = await atClientManager.atClient.get(phoneKey, + getRequestOptions: GetRequestOptions()..useRemoteAtServer = true); + expect(getResultRemote.metadata?.isEncrypted, true); + }); + + test( + 'put method - create a key sharing to other atSign with isEncrypted set to false', + () async { + var phoneKey = AtKey() + ..key = 'phone' + ..sharedWith = '@bobđź› ' + ..sharedBy = atSign; + var value = '+1 100 200 300'; + final putRequestOptions = PutRequestOptions()..shouldEncrypt = false; + var putResult = await atClientManager.atClient + .put(phoneKey, value, putRequestOptions: putRequestOptions); + expect(putResult, true); + // get the value from local keystore to check whether it is not encrypted + var getKeyStoreResult = await atClientManager.atClient + .getLocalSecondary()! + .keyStore! + .get(phoneKey.toString()); + expect(getKeyStoreResult.data, value); + var getResult = await atClientManager.atClient.get(phoneKey); + expect(getResult.value, value); + expect(getResult.metadata?.isEncrypted, false); + await FunctionalTestSyncService.getInstance().syncData( + AtClientManager.getInstance().atClient.syncService, + syncOptions: SyncOptions()..key = phoneKey.toString()); + // fetch the key from remote and verify isEncrypted + var getResultRemote = await atClientManager.atClient.get(phoneKey, + getRequestOptions: GetRequestOptions()..useRemoteAtServer = true); + expect(getResultRemote.metadata?.isEncrypted, false); }); test('put method - create a public key', () async { - // phone.me@aliceđź›  var phoneKey = AtKey() ..key = 'location' ..metadata = (Metadata()..isPublic = true); @@ -45,7 +85,6 @@ void main() { }); test('put method - create a self key with sharedWith populated', () async { - // phone.me@aliceđź›  var phoneKey = AtKey() ..key = 'country' ..sharedWith = atSign; @@ -59,7 +98,6 @@ void main() { test('put method - create a self key with sharedWith not populated', () async { - // phone.me@aliceđź›  var phoneKey = AtKey()..key = 'mobile'; var value = '+1 100 200 300'; var putResult = await atClientManager.atClient.put(phoneKey, value); @@ -70,7 +108,6 @@ void main() { }); test('put method - create a key with binary data', () async { - // phone.me@aliceđź›  var phoneKey = AtKey() ..key = 'image' ..metadata = (Metadata()..isBinary = true); @@ -86,7 +123,6 @@ void main() { }); test('put method - create a public key with binary data', () async { - // phone.me@aliceđź›  var phoneKey = AtKey() ..key = 'image' ..metadata = (Metadata() @@ -106,7 +142,6 @@ void main() { }); test('put method - create a public key', () async { - // phone.me@aliceđź›  var phoneKey = AtKey() ..key = 'city' ..metadata = (Metadata()..isPublic = true); diff --git a/tests/at_functional_test/test/sync_multiple_client_test.dart b/tests/at_functional_test/test/sync_multiple_client_test.dart index 0b6978983..496100943 100644 --- a/tests/at_functional_test/test/sync_multiple_client_test.dart +++ b/tests/at_functional_test/test/sync_multiple_client_test.dart @@ -65,7 +65,7 @@ final String clientOneHiveKeyStorePath = 'test/hive/client1'; final String clientTwoHiveKeyStorePath = 'test/hive/client2'; late Isolate clientOneIsolate; late Isolate clientTwoIsolate; - +int N = 35; void main() async { var mainIsolateReceivePort = ReceivePort('MainIsolateReceivePort'); SyncServiceImpl.syncRequestThreshold = 1; @@ -73,6 +73,7 @@ void main() async { SyncServiceImpl.syncRunIntervalSeconds = 1; SyncServiceImpl.queueSize = 1; String uniqueId = Uuid().v4().hashCode.toString(); + List atKeyEntityList = [ 'country-$uniqueId', 'phone-$uniqueId', @@ -80,6 +81,14 @@ void main() async { 'worknumber-$uniqueId', 'city-$uniqueId' ]; + // create 35 keys + for (int i = 1; i <= 7; i++) { + atKeyEntityList.add('country_$i-$uniqueId'); + atKeyEntityList.add('phone_$i-$uniqueId'); + atKeyEntityList.add('location_$i-$uniqueId'); + atKeyEntityList.add('worknumber_$i-$uniqueId'); + atKeyEntityList.add('city_$i-$uniqueId'); + } var clientInitializationParameters = { 'client1': ChildIsolatePreferences() @@ -215,7 +224,7 @@ void mainIsolateMessageListener(data) { } Future childIsolate(ChildIsolatePreferences clientParameters) async { - int numberOfRepetitions = 5; + int numberOfRepetitions = N; int counter = 0; var random = Random(); @@ -258,7 +267,7 @@ Future childIsolate(ChildIsolatePreferences clientParameters) async { // Execute Update/delete operation on the client for (counter = 0; counter < numberOfRepetitions; counter++) { - AtKey atKey = (AtKey.self(clientParameters.localKeysList[random.nextInt(5)], + AtKey atKey = (AtKey.self(clientParameters.localKeysList[random.nextInt(N)], namespace: namespace, sharedBy: currentAtSign)) .build(); _childIsolateLogger @@ -288,7 +297,8 @@ Future childIsolate(ChildIsolatePreferences clientParameters) async { } Future startClient(ChildIsolatePreferences clientParameters) async { - var atClientPreferences = _getAtClientPreference(currentAtSign, clientParameters.clientId.name, + var atClientPreferences = _getAtClientPreference( + currentAtSign, clientParameters.clientId.name, hiveStoragePath: clientParameters.hiveStoragePath, commitLogPath: clientParameters.commitLogPath); atClientManager = await TestUtils.initAtClient(currentAtSign, namespace, @@ -419,6 +429,7 @@ bool assertCommitEntries( if (!(atKeyList.contains(AtKey.fromString(mapEntry.key).key))) { continue; } + _logger.info('mapEntry: $mapEntry'); // Compare server commit id with both client's commit log if ((serverCommitLogMap[mapEntry.key][0] != mapEntry.value['commitId']) || (serverCommitLogMap[mapEntry.key][0] !=