| 1 | import 'dart:convert'; |
| 2 | import 'dart:io'; |
| 3 | |
| 4 | import 'package:flutter_test/flutter_test.dart'; |
| 5 | import 'package:localmind/features/cloud_sync/data/models/cloud_sync_models.dart'; |
| 6 | import 'package:localmind/features/cloud_sync/data/repositories/s3_cloud_sync_repository.dart'; |
| 7 | import 'package:localmind/features/cloud_sync/services/cloud_sync_crypto_service.dart'; |
| 8 | |
| 9 | void main() { |
| 10 | final endpoint = Platform.environment['LOCALMIND_MINIO_ENDPOINT']; |
| 11 | |
| 12 | test( |
| 13 | 'two clients exchange encrypted state through MinIO', |
| 14 | () async { |
| 15 | final prefix = |
| 16 | 'localmind-integration-${DateTime.now().microsecondsSinceEpoch}'; |
| 17 | final config = S3SyncConfig( |
| 18 | endpoint: endpoint!, |
| 19 | bucket: Platform.environment['LOCALMIND_MINIO_BUCKET']!, |
| 20 | region: Platform.environment['LOCALMIND_MINIO_REGION'] ?? 'us-east-1', |
| 21 | prefix: prefix, |
| 22 | pathStyle: true, |
| 23 | allowInsecureHttp: endpoint.startsWith('http://'), |
| 24 | ); |
| 25 | final credentials = CloudSyncCredentials( |
| 26 | accessKeyId: Platform.environment['LOCALMIND_MINIO_ACCESS_KEY']!, |
| 27 | secretAccessKey: Platform.environment['LOCALMIND_MINIO_SECRET_KEY']!, |
| 28 | ); |
| 29 | final firstClient = S3CloudSyncRepository( |
| 30 | config: config, |
| 31 | credentials: credentials, |
| 32 | ); |
| 33 | final secondClient = S3CloudSyncRepository( |
| 34 | config: config, |
| 35 | credentials: credentials, |
| 36 | ); |
| 37 | final crypto = CloudSyncCryptoService(); |
| 38 | final salt = crypto.newSalt(); |
| 39 | final key = await crypto.deriveMasterKey('integration-passphrase', salt); |
| 40 | final encrypted = await crypto.encryptState( |
| 41 | utf8.encode('{"device":"one"}'), |
| 42 | masterKey: key, |
| 43 | salt: salt, |
| 44 | ); |
| 45 | |
| 46 | try { |
| 47 | await firstClient.testConnection(); |
| 48 | final etag = await firstClient.writeState(encrypted, createOnly: true); |
| 49 | final downloaded = await secondClient.readState(); |
| 50 | expect(downloaded, isNotNull); |
| 51 | expect( |
| 52 | utf8.decode(await crypto.decryptState(downloaded!.bytes, key)), |
| 53 | '{"device":"one"}', |
| 54 | ); |
| 55 | await expectLater( |
| 56 | crypto.decryptState(downloaded.bytes, List<int>.filled(32, 5)), |
| 57 | throwsA(isA<CloudSyncFailure>()), |
| 58 | ); |
| 59 | await secondClient.writeState(encrypted, expectedEtag: etag); |
| 60 | } finally { |
| 61 | await firstClient.deleteObject(firstClient.stateKey); |
| 62 | } |
| 63 | }, |
| 64 | skip: |
| 65 | endpoint == null || |
| 66 | Platform.environment['LOCALMIND_MINIO_BUCKET'] == null || |
| 67 | Platform.environment['LOCALMIND_MINIO_ACCESS_KEY'] == null || |
| 68 | Platform.environment['LOCALMIND_MINIO_SECRET_KEY'] == null |
| 69 | ? 'Set LOCALMIND_MINIO_* environment variables to run this test.' |
| 70 | : false, |
| 71 | ); |
| 72 | } |