diff --git a/.github/workflows/scripts/install-tools.sh b/.github/workflows/scripts/install-tools.sh index 1f77f519..087cfecb 100755 --- a/.github/workflows/scripts/install-tools.sh +++ b/.github/workflows/scripts/install-tools.sh @@ -1,6 +1,6 @@ #!/bin/bash -flutter pub global activate melos 0.4.0+1 +flutter pub global activate melos echo "::add-path::$HOME/.pub-cache/bin" echo "::add-path::$GITHUB_WORKSPACE/_flutter/.pub-cache/bin" echo "::add-path::$GITHUB_WORKSPACE/_flutter/bin/cache/dart-sdk/bin" \ No newline at end of file diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 25dab1f4..8edf9293 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -48,7 +48,6 @@ jobs: - name: 'Install Tools' run: | ./.github/workflows/scripts/install-tools.sh - curl -sL https://github.com/google/google-java-format/releases/download/google-java-format-1.3/google-java-format-1.3-all-deps.jar -o $HOME/google-java-format.jar - name: 'Bootstrap Workspace' run: melos bootstrap - name: 'Dart' @@ -56,20 +55,8 @@ jobs: melos exec -c 1 -- \ flutter format . ./.github/workflows/scripts/validate-formatting.sh - - name: 'Objective-C' - if: ${{ success() || failure() }} - run: | - melos exec -c 4 --ignore="*platform_interface*" --ignore="*web*" -- \ - find . -maxdepth 3 -name "*.h" -o -name "*.m" -print0 \| xargs -0 clang-format -i --style=Google --verbose - ./.github/workflows/scripts/validate-formatting.sh - - name: 'Java' - if: ${{ success() || failure() }} - run: | - melos exec -c 4 --ignore="*platform_interface*" --ignore="*web*" -- \ - find . -maxdepth 12 -name "*.java" -print0 \| xargs -0 java -jar $HOME/google-java-format.jar --replace - ./.github/workflows/scripts/validate-formatting.sh - test_dart: + test: runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -79,26 +66,35 @@ jobs: - name: 'Install Flutter' run: ./.github/workflows/scripts/install-flutter.sh stable - name: 'Install Tools' - run: ./.github/workflows/scripts/install-tools.sh - - name: 'Bootstrap Workspace' - run: melos bootstrap - - name: 'Flutter Test' - run: cd packages/stream_chat && flutter pub run test - - test_flutter: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: 'Install Flutter' - run: ./.github/workflows/scripts/install-flutter.sh stable - - name: 'Install Tools' - run: ./.github/workflows/scripts/install-tools.sh + run: | + ./.github/workflows/scripts/install-tools.sh + flutter pub global activate coverage - name: 'Bootstrap Workspace' run: melos bootstrap + - name: 'Dart Test' + run: | + cd packages/stream_chat + flutter pub run test --coverage coverage/ + format_coverage --lcov --in=coverage/ --out=lcov.info --packages=.packages --report-on=lib - name: 'Flutter Test' run: | melos exec -c 3 --flutter --dir-exists=test --ignore="*example*" --ignore="*web*" -- \ - flutter test \ No newline at end of file + flutter test --coverage + - name: CodeCov + run: bash <(curl -s https://codecov.io/bash) -t ${{ secrets.CODECOV_TOKEN }} + - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + with: + path: packages/stream_chat/lcov.info + min_coverage: 50 + - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + with: + path: packages/stream_chat_persistence/coverage/lcov.info + min_coverage: 0.2 + - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + with: + path: packages/stream_chat_flutter_core/coverage/lcov.info + min_coverage: 4.5 + - uses: VeryGoodOpenSource/very_good_coverage@v1.1.1 + with: + path: packages/stream_chat_flutter/coverage/lcov.info + min_coverage: 16 diff --git a/.gitignore b/.gitignore index c75573b2..8f4fc629 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,8 @@ .atom/ .idea/ .vscode/ - +**/lcov.info +coverage .packages .pub/ .dart_tool/ diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart index eae1abc6..bdffeee7 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart @@ -2,21 +2,23 @@ import 'package:moor/isolate.dart'; import 'package:moor/moor.dart'; import 'package:stream_chat/stream_chat.dart'; -import '../entity/entity.dart'; -import '../dao/dao.dart'; import '../converter/converter.dart'; +import '../dao/dao.dart'; +import '../entity/entity.dart'; import 'shared/shared_db.dart'; part 'moor_chat_database.g.dart'; LazyDatabase _openConnection( String userId, { - logStatements = false, + bool logStatements = false, + bool persistOnDisk = true, }) { return LazyDatabase(() async { return await SharedDB.constructDatabase( userId, logStatements: logStatements, + persistOnDisk: persistOnDisk, ); }); } @@ -48,9 +50,11 @@ class MoorChatDatabase extends _$MoorChatDatabase { MoorChatDatabase( this._userId, { logStatements = false, + bool persistOnDisk = true, }) : super(_openConnection( _userId, logStatements: logStatements, + persistOnDisk: persistOnDisk, )); /// Instantiate a new database instance diff --git a/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart index 74cf91f8..c52e4a8f 100644 --- a/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart +++ b/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'dart:isolate'; + import 'package:moor/ffi.dart'; import 'package:moor/isolate.dart'; import 'package:moor/moor.dart'; @@ -20,17 +21,20 @@ class SharedDB { static Future constructDatabase( String userId, { bool logStatements = false, + bool persistOnDisk = true, }) async { final dbName = 'db_$userId'; - if (Platform.isIOS || Platform.isAndroid) { - final dir = await getApplicationDocumentsDirectory(); - final path = join(dir.path, '$dbName.sqlite'); - final file = File(path); - return VmDatabase(file, logStatements: logStatements); - } - if (Platform.isMacOS || Platform.isLinux) { - final file = File('$dbName.sqlite'); - return VmDatabase(file, logStatements: logStatements); + if (persistOnDisk) { + if (Platform.isIOS || Platform.isAndroid) { + final dir = await getApplicationDocumentsDirectory(); + final path = join(dir.path, '$dbName.sqlite'); + final file = File(path); + return VmDatabase(file, logStatements: logStatements); + } + if (Platform.isMacOS || Platform.isLinux) { + final file = File('$dbName.sqlite'); + return VmDatabase(file, logStatements: logStatements); + } } return VmDatabase.memory(logStatements: logStatements); } diff --git a/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart index 303d925f..b597993d 100644 --- a/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart +++ b/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart @@ -8,6 +8,7 @@ class SharedDB { static dynamic constructDatabase( String userId, { bool logStatements = false, + bool persistOnDisk = true, }) { throw 'Unsupported Platform'; } diff --git a/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart index 6f4360c0..a23e42ee 100644 --- a/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart +++ b/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart @@ -12,6 +12,7 @@ class SharedDB { static Future constructDatabase( String userId, { bool logStatements = false, + bool persistOnDisk = true, // ignored on web }) async { final dbName = 'db_$userId'; return WebDatabase(dbName, logStatements: logStatements); diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart index e6be01ec..44007b8d 100644 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart @@ -1,3 +1,4 @@ +import 'package:meta/meta.dart'; import 'package:stream_chat/stream_chat.dart'; import 'db/moor_chat_database.dart'; @@ -24,13 +25,14 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { _connectionMode = connectionMode, _logger = Logger.detached('💽')..level = logLevel; - MoorChatDatabase _db; + @visibleForTesting + MoorChatDatabase db; final Logger _logger; final ConnectionMode _connectionMode; @override Future connect(String userId) async { - if (_db != null) { + if (db != null) { throw Exception( 'An instance of StreamChatDatabase is already connected.\n' 'disconnect the previous instance before connecting again.', @@ -39,71 +41,71 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { switch (_connectionMode) { case ConnectionMode.regular: _logger.info('Connecting on a regular isolate'); - _db = MoorChatDatabase(userId); + db = MoorChatDatabase(userId); return; case ConnectionMode.background: _logger.info('Connecting on background isolate'); - _db = await SharedDB.constructMoorChatDatabase(userId); + db = await SharedDB.constructMoorChatDatabase(userId); return; } } @override Future getConnectionInfo() { - return _db.connectionEventDao.connectionEvent; + return db.connectionEventDao.connectionEvent; } @override Future updateConnectionInfo(Event event) { - return _db.connectionEventDao.updateConnectionEvent(event); + return db.connectionEventDao.updateConnectionEvent(event); } @override Future updateLastSyncAt(DateTime lastSyncAt) { - return _db.connectionEventDao.updateLastSyncAt(lastSyncAt); + return db.connectionEventDao.updateLastSyncAt(lastSyncAt); } @override Future getLastSyncAt() { - return _db.connectionEventDao.lastSyncAt; + return db.connectionEventDao.lastSyncAt; } @override Future deleteChannels(List cids) { - return _db.channelDao.deleteChannelByCids(cids); + return db.channelDao.deleteChannelByCids(cids); } @override - Future> getChannelCids() => _db.channelDao.cids; + Future> getChannelCids() => db.channelDao.cids; @override Future deleteMessageByIds(List messageIds) { - return _db.messageDao.deleteMessageByIds(messageIds); + return db.messageDao.deleteMessageByIds(messageIds); } @override Future deletePinnedMessageByIds(List messageIds) { - return _db.pinnedMessageDao.deleteMessageByIds(messageIds); + return db.pinnedMessageDao.deleteMessageByIds(messageIds); } @override Future deleteMessageByCids(List cids) { - return _db.messageDao.deleteMessageByCids(cids); + return db.messageDao.deleteMessageByCids(cids); } @override Future deletePinnedMessageByCids(List cids) { - return _db.pinnedMessageDao.deleteMessageByCids(cids); + return db.pinnedMessageDao.deleteMessageByCids(cids); } @override Future> getMembersByCid(String cid) { - return _db.memberDao.getMembersByCid(cid); + return db.memberDao.getMembersByCid(cid); } @override Future getChannelByCid(String cid) { - return _db.channelDao.getChannelByCid(cid); + return db.channelDao.getChannelByCid(cid); } @override @@ -111,7 +113,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { String cid, { PaginationParams messagePagination, }) { - return _db.messageDao.getMessagesByCid( + return db.messageDao.getMessagesByCid( cid, messagePagination: messagePagination, ); @@ -122,7 +124,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { String cid, { PaginationParams messagePagination, }) { - return _db.pinnedMessageDao.getMessagesByCid( + return db.pinnedMessageDao.getMessagesByCid( cid, messagePagination: messagePagination, ); @@ -130,12 +132,12 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { @override Future> getReadsByCid(String cid) { - return _db.readDao.getReadsByCid(cid); + return db.readDao.getReadsByCid(cid); } @override Future>> getChannelThreads(String cid) async { - final messages = await _db.messageDao.getThreadMessages(cid); + final messages = await db.messageDao.getThreadMessages(cid); final messageByParentIdDictionary = >{}; for (final message in messages) { final parentId = message.parentId; @@ -152,7 +154,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { String parentId, { PaginationParams options, }) { - return _db.messageDao.getThreadMessagesByParentId( + return db.messageDao.getThreadMessagesByParentId( parentId, options: options, ); @@ -164,7 +166,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { List> sort = const [], PaginationParams paginationParams, }) async { - final channels = await _db.channelQueryDao.getChannels( + final channels = await db.channelQueryDao.getChannels( filter: filter, sort: sort, paginationParams: paginationParams, @@ -178,7 +180,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { List cids, bool clearQueryCache, ) { - return _db.channelQueryDao.updateChannelQueries( + return db.channelQueryDao.updateChannelQueries( filter, cids, clearQueryCache, @@ -187,63 +189,63 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { @override Future updateChannels(List channels) { - return _db.channelDao.updateChannels(channels); + return db.channelDao.updateChannels(channels); } @override Future updateMembers(String cid, List members) { - return _db.memberDao.updateMembers(cid, members); + return db.memberDao.updateMembers(cid, members); } @override Future updateMessages(String cid, List messages) { - return _db.messageDao.updateMessages(cid, messages); + return db.messageDao.updateMessages(cid, messages); } @override Future updatePinnedMessages(String cid, List messages) { - return _db.pinnedMessageDao.updateMessages(cid, messages); + return db.pinnedMessageDao.updateMessages(cid, messages); } @override Future updateReactions(List reactions) { - return _db.reactionDao.updateReactions(reactions); + return db.reactionDao.updateReactions(reactions); } @override Future updateReads(String cid, List reads) { - return _db.readDao.updateReads(cid, reads); + return db.readDao.updateReads(cid, reads); } @override Future updateUsers(List users) { - return _db.userDao.updateUsers(users); + return db.userDao.updateUsers(users); } @override Future deleteReactionsByMessageId(List messageIds) { - return _db.reactionDao.deleteReactionsByMessageIds(messageIds); + return db.reactionDao.deleteReactionsByMessageIds(messageIds); } @override Future deleteMembersByCids(List cids) { - return _db.memberDao.deleteMemberByCids(cids); + return db.memberDao.deleteMemberByCids(cids); } @override Future disconnect({bool flush = false}) async { - if (_db != null) { + if (db != null) { _logger.info('Disconnecting'); if (flush) { _logger.info('Flushing'); - await _db.batch((batch) { - _db.allTables.forEach((table) { - _db.delete(table).go(); + await db.batch((batch) { + db.allTables.forEach((table) { + db.delete(table).go(); }); }); } - await _db.disconnect(); - _db = null; + await db.disconnect(); + db = null; } } } diff --git a/packages/stream_chat_persistence/test/src/stream_chat_persistence_client_test.dart b/packages/stream_chat_persistence/test/src/stream_chat_persistence_client_test.dart new file mode 100644 index 00000000..561ca04d --- /dev/null +++ b/packages/stream_chat_persistence/test/src/stream_chat_persistence_client_test.dart @@ -0,0 +1,27 @@ +import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; +import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; +import 'package:test/test.dart'; + +void main() { + group('connect', () { + test('throws exception because already connected', () { + final streamChatPersistenceClient = StreamChatPersistenceClient( + connectionMode: ConnectionMode.background, + logLevel: Level.INFO, + )..db = MoorChatDatabase( + 'test', + persistOnDisk: false, + ); + + expect( + () => streamChatPersistenceClient.connect('test'), + throwsA(allOf(isException, predicate((e) { + return e.message == + 'An instance of StreamChatDatabase is already connected.\n' + 'disconnect the previous instance before connecting again.'; + }))), + ); + }); + }); +}