@@ -38,10 +38,6 @@ import 'package:stream_chat/src/core/models/member.dart';
|
|||||||
/// [LogRecord] as the only parameter.
|
/// [LogRecord] as the only parameter.
|
||||||
typedef LogHandlerFunction = void Function(LogRecord record);
|
typedef LogHandlerFunction = void Function(LogRecord record);
|
||||||
|
|
||||||
/// A function which can be used to request a Stream Chat API token from your
|
|
||||||
/// own backend server. Function requires a single [userId].
|
|
||||||
typedef TokenProvider = Future<String> Function(String userId);
|
|
||||||
|
|
||||||
final _levelEmojiMapper = {
|
final _levelEmojiMapper = {
|
||||||
Level.INFO: 'ℹ️',
|
Level.INFO: 'ℹ️',
|
||||||
Level.WARNING: '⚠️',
|
Level.WARNING: '⚠️',
|
||||||
@@ -583,7 +579,6 @@ class StreamChatClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Query channel start');
|
logger.info('Query channel start');
|
||||||
print('Query Channel Started : ${DateTime.now()}');
|
|
||||||
final res = await _chatApi.channel.queryChannels(
|
final res = await _chatApi.channel.queryChannels(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
sort: sort,
|
sort: sort,
|
||||||
@@ -594,7 +589,6 @@ class StreamChatClient {
|
|||||||
messageLimit: messageLimit,
|
messageLimit: messageLimit,
|
||||||
paginationParams: paginationParams,
|
paginationParams: paginationParams,
|
||||||
);
|
);
|
||||||
print('Query Channel Completed : ${DateTime.now()}');
|
|
||||||
|
|
||||||
if (res.channels.isEmpty && paginationParams.offset == 0) {
|
if (res.channels.isEmpty && paginationParams.offset == 0) {
|
||||||
logger.warning('''
|
logger.warning('''
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:stream_chat/src/core/api/responses.dart';
|
import 'package:stream_chat/src/core/api/responses.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/util/extension.dart';
|
|
||||||
import 'package:stream_chat/src/core/models/attachment_file.dart';
|
import 'package:stream_chat/src/core/models/attachment_file.dart';
|
||||||
|
|
||||||
/// Class responsible for uploading images and files to a given channel
|
/// Class responsible for uploading images and files to a given channel
|
||||||
@@ -70,29 +69,10 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
|
|||||||
ProgressCallback? onSendProgress,
|
ProgressCallback? onSendProgress,
|
||||||
CancelToken? cancelToken,
|
CancelToken? cancelToken,
|
||||||
}) async {
|
}) async {
|
||||||
final filename = file.path?.split('/').last ?? file.name;
|
final multiPartFile = await file.toMultipartFile();
|
||||||
final mimeType = filename?.mimeType;
|
final response = await _client.postFile(
|
||||||
|
|
||||||
MultipartFile? multiPartFile;
|
|
||||||
if (file.path != null) {
|
|
||||||
multiPartFile = await MultipartFile.fromFile(
|
|
||||||
file.path!,
|
|
||||||
filename: filename,
|
|
||||||
contentType: mimeType,
|
|
||||||
);
|
|
||||||
} else if (file.bytes != null) {
|
|
||||||
multiPartFile = MultipartFile.fromBytes(
|
|
||||||
file.bytes!,
|
|
||||||
filename: filename,
|
|
||||||
contentType: mimeType,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await _client.post(
|
|
||||||
'/channels/$channelType/$channelId/image',
|
'/channels/$channelType/$channelId/image',
|
||||||
data: FormData.fromMap({
|
multiPartFile,
|
||||||
'file': multiPartFile,
|
|
||||||
}),
|
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
@@ -107,29 +87,10 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
|
|||||||
ProgressCallback? onSendProgress,
|
ProgressCallback? onSendProgress,
|
||||||
CancelToken? cancelToken,
|
CancelToken? cancelToken,
|
||||||
}) async {
|
}) async {
|
||||||
final filename = file.path?.split('/').last ?? file.name;
|
final multiPartFile = await file.toMultipartFile();
|
||||||
final mimeType = filename?.mimeType;
|
final response = await _client.postFile(
|
||||||
|
|
||||||
MultipartFile? multiPartFile;
|
|
||||||
if (file.path != null) {
|
|
||||||
multiPartFile = await MultipartFile.fromFile(
|
|
||||||
file.path!,
|
|
||||||
filename: filename,
|
|
||||||
contentType: mimeType,
|
|
||||||
);
|
|
||||||
} else if (file.bytes != null) {
|
|
||||||
multiPartFile = MultipartFile.fromBytes(
|
|
||||||
file.bytes!,
|
|
||||||
filename: filename,
|
|
||||||
contentType: mimeType,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await _client.post(
|
|
||||||
'/channels/$channelType/$channelId/file',
|
'/channels/$channelType/$channelId/file',
|
||||||
data: FormData.fromMap({
|
multiPartFile,
|
||||||
'file': multiPartFile,
|
|
||||||
}),
|
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ class ChannelApi {
|
|||||||
bool presence = false,
|
bool presence = false,
|
||||||
PaginationParams paginationParams = const PaginationParams(),
|
PaginationParams paginationParams = const PaginationParams(),
|
||||||
}) async {
|
}) async {
|
||||||
print('Query Channel Started 2 : ${DateTime.now()}');
|
|
||||||
final response = await _client.get(
|
final response = await _client.get(
|
||||||
'/channels',
|
'/channels',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
@@ -80,7 +79,6 @@ class ChannelApi {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
print('Query Channel Completed 2 : ${DateTime.now()}');
|
|
||||||
return QueryChannelsResponse.fromJson(response.data);
|
return QueryChannelsResponse.fromJson(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ enum PushProvider {
|
|||||||
apn
|
apn
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on PushProvider {
|
/// Helper extension for [PushProvider]
|
||||||
|
extension PushProviderX on PushProvider {
|
||||||
/// Returns the string notion for [PushProvider].
|
/// Returns the string notion for [PushProvider].
|
||||||
String get name => {
|
String get name => {
|
||||||
PushProvider.apn: 'apn',
|
PushProvider.apn: 'apn',
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart' show MultipartFile;
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:stream_chat/src/core/platform_detector/platform_detector.dart';
|
||||||
|
import 'package:stream_chat/src/core/util/extension.dart';
|
||||||
|
|
||||||
part 'attachment_file.freezed.dart';
|
part 'attachment_file.freezed.dart';
|
||||||
|
|
||||||
part 'attachment_file.g.dart';
|
part 'attachment_file.g.dart';
|
||||||
|
|
||||||
/// Union class to hold various [UploadState] of a attachment.
|
/// Union class to hold various [UploadState] of a attachment.
|
||||||
@@ -58,14 +62,18 @@ String? _toString(Uint8List? bytes) {
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AttachmentFile {
|
class AttachmentFile {
|
||||||
/// Creates a new [AttachmentFile] instance.
|
/// Creates a new [AttachmentFile] instance.
|
||||||
const AttachmentFile({
|
AttachmentFile({
|
||||||
required this.size,
|
required this.size,
|
||||||
this.path,
|
this.path,
|
||||||
this.name,
|
this.name,
|
||||||
this.bytes,
|
this.bytes,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
path != null || bytes != null,
|
path != null || bytes != null,
|
||||||
'Either path or bytes should be != null',
|
'Either path or bytes should be != null',
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
!CurrentPlatform.isWeb || bytes != null,
|
||||||
|
'File by path is not supported in web, Please provide bytes',
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
@@ -95,4 +103,27 @@ class AttachmentFile {
|
|||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
|
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
|
||||||
|
|
||||||
|
/// Converts this into a [MultipartFile]
|
||||||
|
Future<MultipartFile> toMultipartFile() async {
|
||||||
|
final filename = path?.split('/').last ?? name;
|
||||||
|
final mimeType = filename?.mimeType;
|
||||||
|
|
||||||
|
late MultipartFile multiPartFile;
|
||||||
|
|
||||||
|
if (CurrentPlatform.isWeb) {
|
||||||
|
multiPartFile = MultipartFile.fromBytes(
|
||||||
|
bytes!,
|
||||||
|
filename: filename,
|
||||||
|
contentType: mimeType,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
multiPartFile = await MultipartFile.fromFile(
|
||||||
|
path!,
|
||||||
|
filename: filename,
|
||||||
|
contentType: mimeType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return multiPartFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
%µí®û
|
||||||
|
3 0 obj
|
||||||
|
<< /Length 4 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB cs /DeviceRGB CS
|
||||||
|
0 0 0.972549 SC
|
||||||
|
21.68 194 136.64 26 re
|
||||||
|
10 10 m 20 20 l S
|
||||||
|
BT
|
||||||
|
/F0 24 Tf
|
||||||
|
25.68 200 Td
|
||||||
|
(Hello World!) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
132
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<< /Type /Page
|
||||||
|
/Parent 2 0 R
|
||||||
|
/Resources << /Font << /F0 5 0 R >> >>
|
||||||
|
/MediaBox [ 0 0 180 240 ]
|
||||||
|
/Contents 3 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<< /Type /Pages
|
||||||
|
/Count 1
|
||||||
|
/Kids [ 6 0 R ]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 2 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000522 00000 n
|
||||||
|
0000000457 00000 n
|
||||||
|
0000000015 00000 n
|
||||||
|
0000000199 00000 n
|
||||||
|
0000000218 00000 n
|
||||||
|
0000000317 00000 n
|
||||||
|
trailer
|
||||||
|
<< /Size 7
|
||||||
|
/Root 1 0 R
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
574
|
||||||
|
%%EOF
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/attachment_file_uploader.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/attachment_file.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../fakes.dart';
|
||||||
|
import '../../matchers.dart';
|
||||||
|
import '../../mocks.dart';
|
||||||
|
import '../../utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late StreamAttachmentFileUploader fileUploader;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
fileUploader = StreamAttachmentFileUploader(client);
|
||||||
|
registerFallbackValue<MultipartFile>(FakeMultiPartFile());
|
||||||
|
});
|
||||||
|
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
test('sendImage', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
const path = '/channels/$channelType/$channelId/image';
|
||||||
|
final file = assetFile('test_image.jpeg');
|
||||||
|
final attachmentFile = AttachmentFile(
|
||||||
|
size: 333,
|
||||||
|
path: file.path,
|
||||||
|
bytes: file.readAsBytesSync(),
|
||||||
|
);
|
||||||
|
final multipartFile = await attachmentFile.toMultipartFile();
|
||||||
|
|
||||||
|
when(() => client.postFile(
|
||||||
|
path,
|
||||||
|
any(that: isSameMultipartFileAs(multipartFile)),
|
||||||
|
)).thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'file': 'test-file-url',
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await fileUploader.sendImage(
|
||||||
|
attachmentFile,
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.file, isNotNull);
|
||||||
|
expect(res.file, isNotEmpty);
|
||||||
|
|
||||||
|
verify(() => client.postFile(
|
||||||
|
path,
|
||||||
|
any(that: isSameMultipartFileAs(multipartFile)),
|
||||||
|
)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sendFile', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
const path = '/channels/$channelType/$channelId/file';
|
||||||
|
final file = assetFile('example.pdf');
|
||||||
|
final attachmentFile = AttachmentFile(
|
||||||
|
size: 333,
|
||||||
|
path: file.path,
|
||||||
|
bytes: file.readAsBytesSync(),
|
||||||
|
);
|
||||||
|
final multipartFile = await attachmentFile.toMultipartFile();
|
||||||
|
|
||||||
|
when(() => client.postFile(
|
||||||
|
path,
|
||||||
|
any(that: isSameMultipartFileAs(multipartFile)),
|
||||||
|
)).thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'file': 'test-file-url',
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await fileUploader.sendFile(
|
||||||
|
attachmentFile,
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.file, isNotNull);
|
||||||
|
expect(res.file, isNotEmpty);
|
||||||
|
|
||||||
|
verify(() => client.postFile(
|
||||||
|
path,
|
||||||
|
any(that: isSameMultipartFileAs(multipartFile)),
|
||||||
|
)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteImage', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const path = '/channels/$channelType/$channelId/image';
|
||||||
|
|
||||||
|
const url = 'test-image-url';
|
||||||
|
|
||||||
|
when(() => client.delete(path, queryParameters: {'url': url})).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await fileUploader.deleteImage(url, channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.delete(path, queryParameters: {'url': url})).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteFile', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const path = '/channels/$channelType/$channelId/file';
|
||||||
|
|
||||||
|
const url = 'test-file-url';
|
||||||
|
|
||||||
|
when(() => client.delete(path, queryParameters: {'url': url})).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await fileUploader.deleteFile(url, channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.delete(path, queryParameters: {'url': url})).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,492 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/channel_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/channel_model.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/channel_state.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
String _getChannelUrl(String channelId, String channelType) =>
|
||||||
|
'/channels/$channelType/$channelId';
|
||||||
|
|
||||||
|
ChannelState _generateChannelState(
|
||||||
|
String channelId,
|
||||||
|
String channelType,
|
||||||
|
) {
|
||||||
|
final channel = ChannelModel(id: channelId, type: channelType);
|
||||||
|
final messages = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Message(
|
||||||
|
id: 'test-message-id-$index',
|
||||||
|
text: 'test-message-text-$index',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final members = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Member(userId: 'test-user-id-$index'),
|
||||||
|
);
|
||||||
|
final reads = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Read(
|
||||||
|
lastRead: DateTime.now(),
|
||||||
|
user: User(id: 'test-user-id-$index'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final watchers = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => User(id: 'test-user-id-$index'),
|
||||||
|
);
|
||||||
|
final state = ChannelState(
|
||||||
|
channel: channel,
|
||||||
|
messages: messages,
|
||||||
|
pinnedMessages: messages,
|
||||||
|
members: members,
|
||||||
|
read: reads,
|
||||||
|
watchers: watchers,
|
||||||
|
watcherCount: watchers.length,
|
||||||
|
);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late ChannelApi channelApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
channelApi = ChannelApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('queryChannel', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const channelData = <String, Object>{'name': 'test-channel'};
|
||||||
|
const messagePagination = PaginationParams();
|
||||||
|
const membersPagination = PaginationParams();
|
||||||
|
const watchersPagination = PaginationParams();
|
||||||
|
|
||||||
|
const channelPath = '/channels/$channelType/$channelId';
|
||||||
|
const path = '$channelPath/query';
|
||||||
|
|
||||||
|
final channelState = _generateChannelState(channelId, channelType);
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(
|
||||||
|
path,
|
||||||
|
data: channelState.toJson(),
|
||||||
|
));
|
||||||
|
|
||||||
|
final res = await channelApi.queryChannel(
|
||||||
|
channelType,
|
||||||
|
channelId: channelId,
|
||||||
|
channelData: channelData,
|
||||||
|
messagesPagination: messagePagination,
|
||||||
|
membersPagination: membersPagination,
|
||||||
|
watchersPagination: watchersPagination,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.messages.length, channelState.messages.length);
|
||||||
|
expect(res.pinnedMessages.length, channelState.pinnedMessages.length);
|
||||||
|
expect(res.members.length, channelState.members.length);
|
||||||
|
expect(res.read.length, channelState.read.length);
|
||||||
|
expect(res.watchers.length, channelState.watchers.length);
|
||||||
|
expect(res.watcherCount, channelState.watcherCount);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('queryChannels', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid']);
|
||||||
|
const sort = [SortOption<ChannelModel>('test-field')];
|
||||||
|
const memberLimit = 33;
|
||||||
|
const messageLimit = 33;
|
||||||
|
|
||||||
|
const path = '/channels';
|
||||||
|
|
||||||
|
final channelState = _generateChannelState(channelId, channelType);
|
||||||
|
when(() => client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(
|
||||||
|
path,
|
||||||
|
data: {
|
||||||
|
'channels': [channelState.toJson()]
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
final res = await channelApi.queryChannels(
|
||||||
|
filter: filter,
|
||||||
|
sort: sort,
|
||||||
|
memberLimit: memberLimit,
|
||||||
|
messageLimit: messageLimit,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channels, isNotEmpty);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markAllRead', () async {
|
||||||
|
const path = 'channels/read';
|
||||||
|
when(() => client.post(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.markAllRead();
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateChannel', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const data = {'name': 'test-channel-name'};
|
||||||
|
final message = Message(id: 'test-message-id', text: 'channel-updated');
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
final channelModel = ChannelModel(
|
||||||
|
id: channelId,
|
||||||
|
type: channelType,
|
||||||
|
extraData: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.updateChannel(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
data,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channel.cid, channelModel.cid);
|
||||||
|
expect(res.message?.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateChannelPartial', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const data = {'name': 'test-channel-name'};
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
final channelModel = ChannelModel(
|
||||||
|
id: channelId,
|
||||||
|
type: channelType,
|
||||||
|
extraData: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.patch(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.updateChannelPartial(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.patch(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('acceptChannelInvite', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
final message = Message(id: 'test-message-id', text: 'channel-accepted');
|
||||||
|
|
||||||
|
final channelModel = ChannelModel(id: channelId, type: channelType);
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.acceptChannelInvite(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channel.cid, channelModel.cid);
|
||||||
|
expect(res.message?.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejectChannelInvite', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
final message = Message(id: 'test-message-id', text: 'channel-rejected');
|
||||||
|
|
||||||
|
final channelModel = ChannelModel(id: channelId, type: channelType);
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.rejectChannelInvite(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channel.cid, channelModel.cid);
|
||||||
|
expect(res.message?.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inviteChannelMembers', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const memberIds = ['test-member-id-1', 'test-member-id-2'];
|
||||||
|
final channelModel = ChannelModel(id: channelId, type: channelType);
|
||||||
|
final message = Message(id: 'test-message-id', text: 'members-invited');
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.inviteChannelMembers(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
memberIds,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channel.cid, channelModel.cid);
|
||||||
|
expect(res.message?.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('addMembers', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const memberIds = ['test-member-id-1', 'test-member-id-2'];
|
||||||
|
final channelModel = ChannelModel(id: channelId, type: channelType);
|
||||||
|
final message = Message(id: 'test-message-id', text: 'members-added');
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.addMembers(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
memberIds,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channel.cid, channelModel.cid);
|
||||||
|
expect(res.message?.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removeMembers', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const memberIds = ['test-member-id-1', 'test-member-id-2'];
|
||||||
|
final channelModel = ChannelModel(id: channelId, type: channelType);
|
||||||
|
final message = Message(id: 'test-message-id', text: 'members-removed');
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'channel': channelModel.toJson(),
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await channelApi.removeMembers(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
memberIds,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.channel.cid, channelModel.cid);
|
||||||
|
expect(res.message?.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sendEvent', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
final event = Event(type: 'event.test');
|
||||||
|
|
||||||
|
final path = '${_getChannelUrl(channelId, channelType)}/event';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.sendEvent(channelId, channelType, event);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteChannel', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
final path = _getChannelUrl(channelId, channelType);
|
||||||
|
|
||||||
|
when(() => client.delete(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.deleteChannel(channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.delete(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('truncateChannel', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
final path = '${_getChannelUrl(channelId, channelType)}/truncate';
|
||||||
|
|
||||||
|
when(() => client.post(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.truncateChannel(channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hideChannel', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
final path = '${_getChannelUrl(channelId, channelType)}/hide';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.hideChannel(channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('showChannel', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
final path = '${_getChannelUrl(channelId, channelType)}/show';
|
||||||
|
|
||||||
|
when(() => client.post(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.showChannel(channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markRead', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
|
||||||
|
final path = '${_getChannelUrl(channelId, channelType)}/read';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.markRead(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
messageId: messageId,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stopWatching', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
|
||||||
|
final path = '${_getChannelUrl(channelId, channelType)}/stop-watching';
|
||||||
|
|
||||||
|
when(() => client.post(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await channelApi.stopWatching(channelId, channelType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/device_api.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late DeviceApi deviceApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
deviceApi = DeviceApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('addDevice', () async {
|
||||||
|
const deviceId = 'test-device-id';
|
||||||
|
const pushProvider = PushProvider.firebase;
|
||||||
|
|
||||||
|
const path = '/devices';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await deviceApi.addDevice(deviceId, pushProvider);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getDevices', () async {
|
||||||
|
const path = '/devices';
|
||||||
|
|
||||||
|
final devices = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Device(
|
||||||
|
id: 'test-device-id-$index',
|
||||||
|
pushProvider: PushProvider.firebase.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.get(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: {
|
||||||
|
'devices': [...devices.map((it) => it.toJson())]
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final res = await deviceApi.getDevices();
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.devices.length, devices.length);
|
||||||
|
|
||||||
|
verify(() => client.get(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removeDevice', () async {
|
||||||
|
const deviceId = 'test-device-id';
|
||||||
|
|
||||||
|
const path = '/devices';
|
||||||
|
|
||||||
|
when(
|
||||||
|
() => client.delete(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).thenAnswer((_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await deviceApi.removeDevice(deviceId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.delete(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/general_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/requests.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/channel_model.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/event.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/filter.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late GeneralApi generalApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
generalApi = GeneralApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sync', () async {
|
||||||
|
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
|
||||||
|
final lastSyncAt = DateTime.now();
|
||||||
|
|
||||||
|
const path = '/sync';
|
||||||
|
|
||||||
|
final events =
|
||||||
|
List.generate(3, (index) => Event(type: 'test-event-type-$index'));
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'events': [...events.map((it) => it.toJson())]
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await generalApi.sync(cids, lastSyncAt);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('searchMessages', () {
|
||||||
|
test(
|
||||||
|
'should throw if `query` and `messageFilters` is not provided',
|
||||||
|
() async {
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
try {
|
||||||
|
await generalApi.searchMessages(filter);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e, isA<ArgumentError>());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should throw if `query` and `messageFilters` both are provided',
|
||||||
|
() async {
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const query = 'test-query';
|
||||||
|
final messageFilter = Filter.query('key', 'text');
|
||||||
|
try {
|
||||||
|
await generalApi.searchMessages(
|
||||||
|
filter,
|
||||||
|
query: query,
|
||||||
|
messageFilters: messageFilter,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e, isA<ArgumentError>());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test('should run successfully with `query`', () async {
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const query = 'test-query';
|
||||||
|
const sort = [SortOption<ChannelModel>('test-field')];
|
||||||
|
const pagination = PaginationParams();
|
||||||
|
|
||||||
|
const path = '/search';
|
||||||
|
|
||||||
|
when(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).thenAnswer((_) async => successResponse(path, data: {'results': []}));
|
||||||
|
|
||||||
|
final res = await generalApi.searchMessages(
|
||||||
|
filter,
|
||||||
|
query: query,
|
||||||
|
sort: sort,
|
||||||
|
pagination: pagination,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.results, isEmpty);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should run successfully with `messageFilter`', () async {
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const sort = [SortOption<ChannelModel>('test-field')];
|
||||||
|
final messageFilter = Filter.query('key', 'text');
|
||||||
|
const pagination = PaginationParams();
|
||||||
|
|
||||||
|
const path = '/search';
|
||||||
|
|
||||||
|
when(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).thenAnswer((_) async => successResponse(path, data: {'results': []}));
|
||||||
|
|
||||||
|
final res = await generalApi.searchMessages(
|
||||||
|
filter,
|
||||||
|
messageFilters: messageFilter,
|
||||||
|
sort: sort,
|
||||||
|
pagination: pagination,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.results, isEmpty);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('queryMembers', () {
|
||||||
|
test('with `channelId`', () async {
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const pagination = PaginationParams();
|
||||||
|
const sort = [SortOption('test-field')];
|
||||||
|
|
||||||
|
const path = '/members';
|
||||||
|
|
||||||
|
final members = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Member(userId: 'test-user-id=$index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() =>
|
||||||
|
client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'members': [...members.map((it) => it.toJson())]
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await generalApi.queryMembers(
|
||||||
|
channelType,
|
||||||
|
channelId: channelId,
|
||||||
|
filter: filter,
|
||||||
|
pagination: pagination,
|
||||||
|
sort: sort,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.members.length, members.length);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with `members`', () async {
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const pagination = PaginationParams();
|
||||||
|
const sort = [SortOption('test-field')];
|
||||||
|
|
||||||
|
const path = '/members';
|
||||||
|
|
||||||
|
final members = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Member(userId: 'test-user-id=$index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() =>
|
||||||
|
client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'members': [...members.map((it) => it.toJson())]
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await generalApi.queryMembers(
|
||||||
|
channelType,
|
||||||
|
filter: filter,
|
||||||
|
pagination: pagination,
|
||||||
|
sort: sort,
|
||||||
|
members: members,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.members.length, members.length);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/guest_api.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late GuestApi guestApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
guestApi = GuestApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getGuestUser', () async {
|
||||||
|
const accessToken = 'test-guest-token';
|
||||||
|
final user = User(id: 'test-user-id');
|
||||||
|
|
||||||
|
const path = '/guest';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'access_token': accessToken,
|
||||||
|
'user': user.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await guestApi.getGuestUser(user);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.accessToken, accessToken);
|
||||||
|
expect(res.user.id, user.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/message_api.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late MessageApi messageApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
messageApi = MessageApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sendMessage', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
final message = Message(id: 'test-message-id', text: 'test-message-text');
|
||||||
|
|
||||||
|
const path = '/channels/$channelType/$channelId/message';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'message': message.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await messageApi.sendMessage(channelId, channelType, message);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getMessagesById', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const messageIds = ['test-message-id-1', 'test-message-id-2'];
|
||||||
|
|
||||||
|
const path = '/channels/$channelType/$channelId/messages';
|
||||||
|
|
||||||
|
final messages = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Message(id: 'test-message-id-$index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'messages': [...messages.map((it) => it.toJson())],
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await messageApi.getMessagesById(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
messageIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.messages.length, messages.length);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getMessage', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
|
||||||
|
const path = '/messages/$messageId';
|
||||||
|
|
||||||
|
final message = Message(id: messageId);
|
||||||
|
|
||||||
|
when(() => client.get(path)).thenAnswer((_) async =>
|
||||||
|
successResponse(path, data: {'message': message.toJson()}));
|
||||||
|
|
||||||
|
final res = await messageApi.getMessage(messageId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.id, messageId);
|
||||||
|
|
||||||
|
verify(() => client.get(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateMessage', () async {
|
||||||
|
final message = Message(id: 'test-message-id');
|
||||||
|
|
||||||
|
final path = '/messages/${message.id}';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: {'message': message.toJson()}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final res = await messageApi.updateMessage(message);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.id, message.id);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteMessage', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
|
||||||
|
const path = '/messages/$messageId';
|
||||||
|
|
||||||
|
when(() => client.delete(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final res = await messageApi.deleteMessage(messageId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.delete(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sendAction', () async {
|
||||||
|
const channelId = 'test-channel-id';
|
||||||
|
const channelType = 'test-channel-type';
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const formData = {'test-key': 'test-data'};
|
||||||
|
|
||||||
|
const path = '/messages/$messageId/action';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await messageApi.sendAction(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
messageId,
|
||||||
|
formData,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sendReaction', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const reactionType = 'test-reaction-type';
|
||||||
|
const extraData = {'test-key': 'test-data'};
|
||||||
|
|
||||||
|
const path = '/messages/$messageId/reaction';
|
||||||
|
|
||||||
|
final message = Message(id: messageId);
|
||||||
|
final reaction = Reaction(type: reactionType, messageId: messageId);
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'message': message.toJson(),
|
||||||
|
'reaction': reaction.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await messageApi.sendReaction(
|
||||||
|
messageId,
|
||||||
|
reactionType,
|
||||||
|
extraData: extraData,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.id, messageId);
|
||||||
|
expect(res.reaction.messageId, messageId);
|
||||||
|
expect(res.reaction.type, reactionType);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deleteReaction', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const reactionType = 'test-reaction-type';
|
||||||
|
|
||||||
|
const path = '/messages/$messageId/reaction/$reactionType';
|
||||||
|
|
||||||
|
when(() => client.delete(path)).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await messageApi.deleteReaction(messageId, reactionType);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.delete(path)).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getReactions', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const options = PaginationParams();
|
||||||
|
|
||||||
|
const path = '/messages/$messageId/reactions';
|
||||||
|
|
||||||
|
final reactions = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Reaction(
|
||||||
|
type: 'test-reaction-type-$index',
|
||||||
|
messageId: messageId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'reactions': [...reactions.map((it) => it.toJson())]
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await messageApi.getReactions(messageId, options);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.reactions.length, reactions.length);
|
||||||
|
expect(res.reactions.every((it) => it.messageId == messageId), isTrue);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('translateMessage', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
const messageText = 'hello';
|
||||||
|
const language = 'hi'; // Hindi
|
||||||
|
final message = Message(id: messageId, text: messageText);
|
||||||
|
|
||||||
|
final path = '/messages/${message.id}/translate';
|
||||||
|
|
||||||
|
const translatedMessageText = 'नमस्ते';
|
||||||
|
final translatedMessage = TranslatedMessage(const {
|
||||||
|
language: translatedMessageText,
|
||||||
|
});
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'message': translatedMessage.toJson(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await messageApi.translateMessage(messageId, language);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.message.i18n?.containsKey(language), isTrue);
|
||||||
|
expect(res.message.i18n?[language], translatedMessageText);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getReplies', () async {
|
||||||
|
const parentId = 'test-parent-id';
|
||||||
|
const options = PaginationParams();
|
||||||
|
|
||||||
|
const path = '/messages/$parentId/replies';
|
||||||
|
|
||||||
|
final messages = List.generate(
|
||||||
|
3,
|
||||||
|
(index) => Message(
|
||||||
|
id: 'test-message-id-$index',
|
||||||
|
parentId: parentId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'messages': [...messages.map((it) => it.toJson())]
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await messageApi.getReplies(parentId, options);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.messages.length, messages.length);
|
||||||
|
expect(res.messages.every((it) => it.parentId == parentId), isTrue);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/moderation_api.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late ModerationApi moderationApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
moderationApi = ModerationApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('muteUser', () async {
|
||||||
|
const userId = 'test-user-id';
|
||||||
|
|
||||||
|
const path = '/moderation/mute';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.muteUser(userId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unmuteUser', () async {
|
||||||
|
const userId = 'test-user-id';
|
||||||
|
|
||||||
|
const path = '/moderation/unmute';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.unmuteUser(userId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('muteChannel', () async {
|
||||||
|
const channelCid = 'test-channel-cid';
|
||||||
|
const expiration = Duration(days: 3);
|
||||||
|
|
||||||
|
const path = '/moderation/mute/channel';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.muteChannel(
|
||||||
|
channelCid,
|
||||||
|
expiration: expiration,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unmuteChannel', () async {
|
||||||
|
const channelCid = 'test-channel-cid';
|
||||||
|
|
||||||
|
const path = '/moderation/unmute/channel';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.unmuteChannel(channelCid);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('flagMessage', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
|
||||||
|
const path = '/moderation/flag';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.flagMessage(messageId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unflagMessage', () async {
|
||||||
|
const messageId = 'test-message-id';
|
||||||
|
|
||||||
|
const path = '/moderation/unflag';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.unflagMessage(messageId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('flagUser', () async {
|
||||||
|
const userId = 'test-message-id';
|
||||||
|
|
||||||
|
const path = '/moderation/flag';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.flagUser(userId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unflagUser', () async {
|
||||||
|
const userId = 'test-message-id';
|
||||||
|
|
||||||
|
const path = '/moderation/unflag';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.unflagUser(userId);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('banUser', () async {
|
||||||
|
const targetUserId = 'test-target-user-id';
|
||||||
|
const options = {'key': 'value'};
|
||||||
|
|
||||||
|
const path = '/moderation/ban';
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.banUser(targetUserId, options: options);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unbanUser', () async {
|
||||||
|
const targetUserId = 'test-target-user-id';
|
||||||
|
const options = {'key': 'value'};
|
||||||
|
|
||||||
|
const path = '/moderation/ban';
|
||||||
|
|
||||||
|
when(
|
||||||
|
() => client.delete(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).thenAnswer((_) async => successResponse(path, data: <String, dynamic>{}));
|
||||||
|
|
||||||
|
final res = await moderationApi.unbanUser(targetUserId, options: options);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.delete(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import 'package:stream_chat/src/core/api/stream_chat_api.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const apiKey = 'test-api-key';
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late StreamChatApi streamChatApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
streamChatApi = StreamChatApi(
|
||||||
|
apiKey,
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.user`', () {
|
||||||
|
expect(streamChatApi.user, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.guest`', () {
|
||||||
|
expect(streamChatApi.guest, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.message`', () {
|
||||||
|
expect(streamChatApi.message, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.channel`', () {
|
||||||
|
expect(streamChatApi.channel, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.device`', () {
|
||||||
|
expect(streamChatApi.device, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.moderation`', () {
|
||||||
|
expect(streamChatApi.moderation, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.general`', () {
|
||||||
|
expect(streamChatApi.general, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('`.fileUploader`', () {
|
||||||
|
expect(streamChatApi.fileUploader, isNotNull);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/requests.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/user_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/models/filter.dart';
|
||||||
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Response successResponse(String path, {Object? data}) => Response(
|
||||||
|
data: data,
|
||||||
|
requestOptions: RequestOptions(path: path),
|
||||||
|
statusCode: 200,
|
||||||
|
);
|
||||||
|
|
||||||
|
late final client = MockHttpClient();
|
||||||
|
late UserApi userApi;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
userApi = UserApi(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('queryUsers', () async {
|
||||||
|
const presence = true;
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const sort = [SortOption('test-field')];
|
||||||
|
const pagination = PaginationParams();
|
||||||
|
|
||||||
|
const path = '/users';
|
||||||
|
|
||||||
|
final users = List.generate(3, (index) => User(id: 'test-user-id-$index'));
|
||||||
|
|
||||||
|
when(() => client.get(path, queryParameters: any(named: 'queryParameters')))
|
||||||
|
.thenAnswer((_) async => successResponse(path, data: {
|
||||||
|
'users': [...users.map((it) => it.toJson())]
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await userApi.queryUsers(
|
||||||
|
presence: presence,
|
||||||
|
filter: filter,
|
||||||
|
sort: sort,
|
||||||
|
pagination: pagination,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.users.length, users.length);
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => client.get(path, queryParameters: any(named: 'queryParameters')),
|
||||||
|
).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateUsers', () async {
|
||||||
|
final users = List.generate(3, (index) => User(id: 'test-user-id-$index'));
|
||||||
|
|
||||||
|
const path = '/users';
|
||||||
|
|
||||||
|
final updatedUsers = {for (final user in users) user.id: user};
|
||||||
|
|
||||||
|
when(() => client.post(path, data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => successResponse(path, data: {
|
||||||
|
'users': updatedUsers
|
||||||
|
.map((key, value) => MapEntry(key, value.toJson()))
|
||||||
|
}));
|
||||||
|
|
||||||
|
final res = await userApi.updateUsers(users);
|
||||||
|
|
||||||
|
expect(res, isNotNull);
|
||||||
|
expect(res.users.length, updatedUsers.length);
|
||||||
|
|
||||||
|
verify(() => client.post(path, data: any(named: 'data'))).called(1);
|
||||||
|
verifyNoMoreInteractions(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,6 +1,18 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.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/general_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/message_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/moderation_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/stream_chat_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/user_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/guest_api.dart';
|
||||||
import 'package:stream_chat/src/core/http/token.dart';
|
import 'package:stream_chat/src/core/http/token.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/stream_chat.dart';
|
||||||
|
|
||||||
|
import 'mocks.dart';
|
||||||
|
|
||||||
class FakeTokenManager extends Fake implements TokenManager {
|
class FakeTokenManager extends Fake implements TokenManager {
|
||||||
final token = Token.development('test-user-id');
|
final token = Token.development('test-user-id');
|
||||||
@@ -25,3 +37,48 @@ class FakeTokenManager extends Fake implements TokenManager {
|
|||||||
@override
|
@override
|
||||||
void reset() {}
|
void reset() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeMultiPartFile extends Fake implements MultipartFile {}
|
||||||
|
|
||||||
|
class FakeChatApi extends Fake implements StreamChatApi {
|
||||||
|
UserApi? _user;
|
||||||
|
|
||||||
|
@override
|
||||||
|
UserApi get user => _user ??= MockUserApi();
|
||||||
|
|
||||||
|
GuestApi? _guest;
|
||||||
|
|
||||||
|
@override
|
||||||
|
GuestApi get guest => _guest ??= MockGuestApi();
|
||||||
|
|
||||||
|
MessageApi? _message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
MessageApi get message => _message ??= MockMessageApi();
|
||||||
|
|
||||||
|
ChannelApi? _channel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ChannelApi get channel => _channel ??= MockChannelApi();
|
||||||
|
|
||||||
|
DeviceApi? _device;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DeviceApi get device => _device ??= MockDeviceApi();
|
||||||
|
|
||||||
|
ModerationApi? _moderation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ModerationApi get moderation => _moderation ??= MockModerationApi();
|
||||||
|
|
||||||
|
GeneralApi? _general;
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneralApi get general => _general ??= MockGeneralApi();
|
||||||
|
|
||||||
|
AttachmentFileUploader? _fileUploader;
|
||||||
|
|
||||||
|
@override
|
||||||
|
AttachmentFileUploader get fileUploader =>
|
||||||
|
_fileUploader ??= MockAttachmentFileUploader();
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import 'package:dio/dio.dart' show MultipartFile;
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
Matcher isSameMultipartFileAs(MultipartFile targetFile) =>
|
||||||
|
_IsSameMultipartFileAs(targetFile: targetFile);
|
||||||
|
|
||||||
|
class _IsSameMultipartFileAs extends Matcher {
|
||||||
|
const _IsSameMultipartFileAs({required this.targetFile});
|
||||||
|
|
||||||
|
final MultipartFile targetFile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Description describe(Description description) =>
|
||||||
|
description.add('is same multipartFile as $targetFile');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool matches(covariant MultipartFile file, Map matchState) =>
|
||||||
|
file.length == targetFile.length;
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
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/core/api/attachment_file_uploader.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/general_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/guest_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/message_api.dart';
|
||||||
|
import 'package:stream_chat/src/core/api/moderation_api.dart';
|
||||||
|
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';
|
||||||
@@ -32,3 +40,20 @@ class MockHttpClient extends Mock implements StreamHttpClient {}
|
|||||||
class MockTokenManager extends Mock implements TokenManager {}
|
class MockTokenManager extends Mock implements TokenManager {}
|
||||||
|
|
||||||
class MockConnectionIdManager extends Mock implements ConnectionIdManager {}
|
class MockConnectionIdManager extends Mock implements ConnectionIdManager {}
|
||||||
|
|
||||||
|
class MockUserApi extends Mock implements UserApi {}
|
||||||
|
|
||||||
|
class MockGuestApi extends Mock implements GuestApi {}
|
||||||
|
|
||||||
|
class MockMessageApi extends Mock implements MessageApi {}
|
||||||
|
|
||||||
|
class MockChannelApi extends Mock implements ChannelApi {}
|
||||||
|
|
||||||
|
class MockDeviceApi extends Mock implements DeviceApi {}
|
||||||
|
|
||||||
|
class MockModerationApi extends Mock implements ModerationApi {}
|
||||||
|
|
||||||
|
class MockGeneralApi extends Mock implements GeneralApi {}
|
||||||
|
|
||||||
|
class MockAttachmentFileUploader extends Mock
|
||||||
|
implements AttachmentFileUploader {}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
File assetFile(String name) {
|
||||||
|
final dir = currentDirectory.path;
|
||||||
|
return File('$dir/test/assets/$name');
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/flutter/flutter/issues/20907
|
||||||
|
Directory get currentDirectory {
|
||||||
|
var directory = Directory.current;
|
||||||
|
if (directory.path.endsWith('/test')) {
|
||||||
|
directory = directory.parent;
|
||||||
|
}
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user