add initialized channel tests

Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
Sahil Kumar
2021-06-09 21:01:35 +05:30
parent 89f989003d
commit e2529231fd
9 changed files with 2019 additions and 28 deletions
@@ -51,9 +51,9 @@ class Channel {
String? _id; String? _id;
String? _cid; String? _cid;
final Map<String, dynamic> _extraData; final Map<String, Object?> _extraData;
set extraData(Map<String, dynamic> extraData) { set extraData(Map<String, Object?> extraData) {
if (_initializedCompleter.isCompleted) { if (_initializedCompleter.isCompleted) {
throw StateError( throw StateError(
'Once the channel is initialized you should use channel.update ' 'Once the channel is initialized you should use channel.update '
@@ -734,7 +734,7 @@ class Channel {
/// Edit the channel custom data /// Edit the channel custom data
Future<UpdateChannelResponse> update( Future<UpdateChannelResponse> update(
Map<String, dynamic> channelData, [ Map<String, Object?> channelData, [
Message? updateMessage, Message? updateMessage,
]) async { ]) async {
_checkInitialized(); _checkInitialized();
@@ -814,19 +814,24 @@ class Channel {
final messageId = message.id; final messageId = message.id;
final res = await _client.sendAction(id!, type, messageId, formData); final res = await _client.sendAction(id!, type, messageId, formData);
// update the passed message with response message
if (res.message != null) { if (res.message != null) {
state!.addMessage(res.message!); state!.addMessage(res.message!);
} else { } else {
// remove the passed message if response does
// not contain message
final oldIndex = state!.messages.indexWhere((m) => m.id == messageId); final oldIndex = state!.messages.indexWhere((m) => m.id == messageId);
Message? oldMessage; // remove regular message if present
if (oldIndex != -1) { if (oldIndex != -1) {
oldMessage = state!.messages[oldIndex]; final oldMessage = state!.messages[oldIndex];
state!.updateChannelState(state!._channelState.copyWith( state!.updateChannelState(state!._channelState.copyWith(
messages: state?.messages?..remove(oldMessage), messages: state?.messages?..remove(oldMessage),
)); ));
} else { } else {
oldMessage = state!.threads.values // remove thread message if present
// also reduces total reply count
final oldMessage = state!.threads.values
.expand((messages) => messages) .expand((messages) => messages)
.firstWhereOrNull((m) => m.id == messageId); .firstWhereOrNull((m) => m.id == messageId);
if (oldMessage?.parentId != null) { if (oldMessage?.parentId != null) {
@@ -858,18 +863,11 @@ class Channel {
} }
/// Loads the initial channel state and watches for changes /// Loads the initial channel state and watches for changes
Future<ChannelState> watch([Map<String, dynamic> options = const {}]) async { Future<ChannelState> watch() async {
final watchOptions = Map<String, dynamic>.from({
'state': true,
'watch': true,
'presence': false,
})
..addAll(options);
ChannelState response; ChannelState response;
try { try {
response = await query(options: watchOptions); response = await query(watch: true);
} catch (error, stackTrace) { } catch (error, stackTrace) {
if (!_initializedCompleter.isCompleted) { if (!_initializedCompleter.isCompleted) {
_initializedCompleter.completeError(error, stackTrace); _initializedCompleter.completeError(error, stackTrace);
@@ -956,17 +954,15 @@ class Channel {
); );
/// Creates a new channel /// Creates a new channel
Future<ChannelState> create() async => query(options: { Future<ChannelState> create() async => query(state: false);
'watch': false,
'state': false,
'presence': false,
});
/// Query the API, get messages, members or other channel fields /// Query the API, get messages, members or other channel fields
/// Set [preferOffline] to true to avoid the api call if the data is already /// Set [preferOffline] to true to avoid the api call if the data is already
/// in the offline storage /// in the offline storage
Future<ChannelState> query({ Future<ChannelState> query({
Map<String, dynamic> options = const {}, bool state = true,
bool watch = false,
bool presence = false,
PaginationParams? messagesPagination, PaginationParams? messagesPagination,
PaginationParams? membersPagination, PaginationParams? membersPagination,
PaginationParams? watchersPagination, PaginationParams? watchersPagination,
@@ -976,10 +972,10 @@ class Channel {
final updatedState = await _client.chatPersistenceClient final updatedState = await _client.chatPersistenceClient
?.getChannelStateByCid(cid!, messagePagination: messagesPagination); ?.getChannelStateByCid(cid!, messagePagination: messagesPagination);
if (updatedState != null && updatedState.messages.isNotEmpty) { if (updatedState != null && updatedState.messages.isNotEmpty) {
if (state == null) { if (this.state == null) {
_initState(updatedState); _initState(updatedState);
} else { } else {
state?.updateChannelState(updatedState); this.state?.updateChannelState(updatedState);
} }
return updatedState; return updatedState;
} }
@@ -990,6 +986,9 @@ class Channel {
type, type,
channelId: id, channelId: id,
channelData: _extraData, channelData: _extraData,
state: state,
watch: watch,
presence: presence,
messagesPagination: messagesPagination, messagesPagination: messagesPagination,
membersPagination: membersPagination, membersPagination: membersPagination,
watchersPagination: watchersPagination, watchersPagination: watchersPagination,
@@ -1000,7 +999,7 @@ class Channel {
_cid = updatedState.channel!.cid; _cid = updatedState.channel!.cid;
} }
state?.updateChannelState(updatedState); this.state?.updateChannelState(updatedState);
return updatedState; return updatedState;
} catch (e) { } catch (e) {
if (!_client.persistenceEnabled) { if (!_client.persistenceEnabled) {
@@ -1775,7 +1774,7 @@ class ChannelClientState {
); );
} }
late Timer _cleaningTimer; Timer? _cleaningTimer;
void _startCleaning() { void _startCleaning() {
if (_channelState.channel?.config.typingEvents == false) { if (_channelState.channel?.config.typingEvents == false) {
@@ -1842,7 +1841,7 @@ class ChannelClientState {
_channelStateController.close(); _channelStateController.close();
_isUpToDateController.close(); _isUpToDateController.close();
_threadsController.close(); _threadsController.close();
_cleaningTimer.cancel(); _cleaningTimer?.cancel();
_pinnedMessagesTimer.cancel(); _pinnedMessagesTimer.cancel();
_typingEventsController.close(); _typingEventsController.close();
} }
@@ -757,7 +757,7 @@ class StreamChatClient {
Future<UpdateChannelResponse> updateChannel( Future<UpdateChannelResponse> updateChannel(
String channelId, String channelId,
String channelType, String channelType,
Map<String, dynamic> data, { Map<String, Object?> data, {
Message? message, Message? message,
}) => }) =>
_chatApi.channel.updateChannel( _chatApi.channel.updateChannel(
@@ -92,7 +92,7 @@ class ChannelApi {
Future<UpdateChannelResponse> updateChannel( Future<UpdateChannelResponse> updateChannel(
String channelId, String channelId,
String channelType, String channelType,
Map<String, dynamic> data, { Map<String, Object?> data, {
Message? message, Message? message,
}) async { }) async {
final response = await _client.post( final response = await _client.post(
File diff suppressed because it is too large Load Diff
+21
View File
@@ -82,3 +82,24 @@ class FakeChatApi extends Fake implements StreamChatApi {
AttachmentFileUploader get fileUploader => AttachmentFileUploader get fileUploader =>
_fileUploader ??= MockAttachmentFileUploader(); _fileUploader ??= MockAttachmentFileUploader();
} }
class FakeClientState extends Fake implements ClientState {
@override
OwnUser? get user => OwnUser(id: 'test-user-id');
var _totalUnreadCount = 0;
@override
int? get totalUnreadCount => _totalUnreadCount;
@override
set totalUnreadCount(int? unreadCount) {
_totalUnreadCount += unreadCount ?? 0;
}
}
class FakeMessage extends Fake implements Message {}
class FakeAttachmentFile extends Fake implements AttachmentFile {}
class FakeEvent extends Fake implements Event {}
@@ -1,4 +1,7 @@
import 'package:collection/collection.dart';
import 'package:dio/dio.dart' show MultipartFile; import 'package:dio/dio.dart' show MultipartFile;
import 'package:stream_chat/src/core/models/event.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
Matcher isSameMultipartFileAs(MultipartFile targetFile) => Matcher isSameMultipartFileAs(MultipartFile targetFile) =>
@@ -17,3 +20,72 @@ class _IsSameMultipartFileAs extends Matcher {
bool matches(covariant MultipartFile file, Map matchState) => bool matches(covariant MultipartFile file, Map matchState) =>
file.length == targetFile.length; file.length == targetFile.length;
} }
Matcher isSameEventAs(Event targetEvent) =>
_IsSameEventAs(targetEvent: targetEvent);
class _IsSameEventAs extends Matcher {
const _IsSameEventAs({required this.targetEvent});
final Event targetEvent;
@override
Description describe(Description description) =>
description.add('is same event as $targetEvent');
@override
bool matches(covariant Event event, Map matchState) =>
event.type == targetEvent.type;
}
Matcher isSameMessageAs(
Message targetMessage, {
bool matchReactions = false,
bool matchSendingStatus = false,
}) =>
_IsSameMessageAs(
targetMessage: targetMessage,
matchReactions: matchReactions,
matchSendingStatus: matchSendingStatus,
);
class _IsSameMessageAs extends Matcher {
const _IsSameMessageAs({
required this.targetMessage,
this.matchReactions = false,
this.matchSendingStatus = false,
});
final Message targetMessage;
final bool matchReactions;
final bool matchSendingStatus;
@override
Description describe(Description description) =>
description.add('is same message as $targetMessage');
@override
bool matches(covariant Message message, Map matchState) {
var matches = message.id == targetMessage.id;
if (matchSendingStatus) {
matches &= message.status == targetMessage.status;
}
if (matchReactions) {
matches &= const ListEquality().equals(
message.ownReactions
?.map((it) => '${it.type}-${it.messageId}')
.toList(),
targetMessage.ownReactions
?.map((it) => '${it.type}-${it.messageId}')
.toList());
matches &= const ListEquality().equals(
message.latestReactions
?.map((it) => '${it.type}-${it.messageId}')
.toList(),
targetMessage.latestReactions
?.map((it) => '${it.type}-${it.messageId}')
.toList());
}
return matches;
}
}
+27
View File
@@ -1,6 +1,7 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/client/client.dart';
import 'package:stream_chat/src/core/api/attachment_file_uploader.dart'; import 'package:stream_chat/src/core/api/attachment_file_uploader.dart';
import 'package:stream_chat/src/core/api/channel_api.dart'; import 'package:stream_chat/src/core/api/channel_api.dart';
import 'package:stream_chat/src/core/api/device_api.dart'; import 'package:stream_chat/src/core/api/device_api.dart';
@@ -12,8 +13,13 @@ import 'package:stream_chat/src/core/api/user_api.dart';
import 'package:stream_chat/src/core/http/connection_id_manager.dart'; import 'package:stream_chat/src/core/http/connection_id_manager.dart';
import 'package:stream_chat/src/core/http/stream_http_client.dart'; import 'package:stream_chat/src/core/http/stream_http_client.dart';
import 'package:stream_chat/src/core/http/token_manager.dart'; import 'package:stream_chat/src/core/http/token_manager.dart';
import 'package:stream_chat/src/core/models/channel_config.dart';
import 'package:stream_chat/src/core/models/channel_model.dart';
import 'package:stream_chat/src/db/chat_persistence_client.dart';
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
import 'db/chat_persistence_client_test.dart';
class MockWebSocketChannel extends Mock implements WebSocketChannel {} class MockWebSocketChannel extends Mock implements WebSocketChannel {}
class MockWebSocketSink extends Mock implements WebSocketSink {} class MockWebSocketSink extends Mock implements WebSocketSink {}
@@ -57,3 +63,24 @@ class MockGeneralApi extends Mock implements GeneralApi {}
class MockAttachmentFileUploader extends Mock class MockAttachmentFileUploader extends Mock
implements AttachmentFileUploader {} implements AttachmentFileUploader {}
class MockPersistenceClient extends Mock implements ChatPersistenceClient {}
class MockStreamChatClient extends Mock implements StreamChatClient {
@override
bool get persistenceEnabled => false;
}
class MockStreamChatClientWithPersistence extends Mock
implements StreamChatClient {
ChatPersistenceClient? _persistenceClient;
@override
ChatPersistenceClient get chatPersistenceClient =>
_persistenceClient ??= MockPersistenceClient();
@override
bool get persistenceEnabled => true;
}
class MockChannelConfig extends Mock implements ChannelConfig {}