Merge branch 'develop' into v4

This commit is contained in:
Salvatore Giordano
2022-03-07 11:45:04 +01:00
35 changed files with 1059 additions and 281 deletions
@@ -1130,6 +1130,135 @@ void main() {
verify(() => client.sendReaction(message.id, type)).called(1);
});
test('should work fine with score passed explicitly', () async {
const type = 'test-reaction-type';
final message = Message(id: 'test-message-id');
const score = 5;
final reaction = Reaction(
type: type,
messageId: message.id,
score: score,
);
when(() => client.sendReaction(
message.id,
type,
score: score,
)).thenAnswer(
(_) async => SendReactionResponse()
..message = message
..reaction = reaction,
);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
reactionCounts: {type: 1},
reactionScores: {type: score},
latestReactions: [reaction],
ownReactions: [reaction],
),
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.sendReaction(
message,
type,
score: score,
);
expect(res, isNotNull);
expect(res.reaction.type, type);
expect(res.reaction.messageId, message.id);
expect(res.reaction.score, score);
verify(() => client.sendReaction(
message.id,
type,
score: score,
)).called(1);
});
test('should work fine with score passed explicitly and in extraData',
() async {
const type = 'test-reaction-type';
final message = Message(id: 'test-message-id');
const score = 5;
const extraDataScore = 3;
const extraData = {
'score': extraDataScore,
};
final reaction = Reaction(
type: type,
messageId: message.id,
score: extraDataScore,
);
when(() => client.sendReaction(
message.id,
type,
score: score,
extraData: extraData,
)).thenAnswer(
(_) async => SendReactionResponse()
..message = message
..reaction = reaction,
);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.messagesStream.skip(1),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
reactionCounts: {type: 1},
reactionScores: {type: extraDataScore},
latestReactions: [reaction],
ownReactions: [reaction],
),
matchReactions: true,
matchSendingStatus: true,
),
],
]),
);
final res = await channel.sendReaction(
message,
type,
score: score,
extraData: extraData,
);
expect(res, isNotNull);
expect(res.reaction.type, type);
expect(res.reaction.messageId, message.id);
expect(
res.reaction.score,
extraDataScore,
);
verify(() => client.sendReaction(
message.id,
type,
score: score,
extraData: extraData,
)).called(1);
});
test(
'should restore previous message if `client.sendReaction` throws',
() async {
@@ -1257,6 +1386,189 @@ void main() {
);
});
group('`.sendReaction in thread`', () {
test('should work fine', () async {
const type = 'test-reaction-type';
final message = Message(
id: 'test-message-id',
parentId: 'test-parent-id', // is thread message
);
final reaction = Reaction(type: type, messageId: message.id);
when(() => client.sendReaction(message.id, type)).thenAnswer(
(_) async => SendReactionResponse()
..message = message
..reaction = reaction,
);
expectLater(
channel.state?.threadsStream
// skipping first seed message list -> [] messages
.skip(1)
.map((event) => event['test-parent-id']),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
reactionCounts: {type: 1},
reactionScores: {type: 1},
latestReactions: [reaction],
ownReactions: [reaction],
),
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
]),
);
final res = await channel.sendReaction(message, type);
expect(res, isNotNull);
expect(res.reaction.type, type);
expect(res.reaction.messageId, message.id);
verify(() => client.sendReaction(message.id, type)).called(1);
});
test(
'''should restore previous thread message if `client.sendReaction` throws''',
() async {
const type = 'test-reaction-type';
final message = Message(
id: 'test-message-id',
parentId: 'test-parent-id', // is thread message
);
final reaction = Reaction(type: type, messageId: message.id);
when(() => client.sendReaction(message.id, type))
.thenThrow(StreamChatNetworkError(ChatErrorCode.inputError));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.threadsStream
.skip(1)
.map((event) => event['test-parent-id']),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
reactionCounts: {type: 1},
reactionScores: {type: 1},
latestReactions: [reaction],
ownReactions: [reaction],
),
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
[
isSameMessageAs(
message,
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
]),
);
try {
await channel.sendReaction(message, type);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
}
verify(() => client.sendReaction(message.id, type)).called(1);
},
);
test(
'''should override previous thread reaction if present and `enforceUnique` is true''',
() async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
const parentId = 'test-parent-id';
const prevType = 'test-reaction-type';
final prevReaction = Reaction(
type: prevType,
messageId: messageId,
userId: userId,
);
final message = Message(
id: messageId,
parentId: parentId,
ownReactions: [prevReaction],
latestReactions: [prevReaction],
reactionScores: const {prevType: 1},
reactionCounts: const {prevType: 1},
);
const type = 'test-reaction-type-2';
final newReaction = Reaction(
type: type,
messageId: messageId,
userId: userId,
);
final newMessage = message.copyWith(
ownReactions: [newReaction],
latestReactions: [newReaction],
);
const enforceUnique = true;
when(() => client.sendReaction(
messageId,
type,
enforceUnique: enforceUnique,
)).thenAnswer(
(_) async => SendReactionResponse()
..message = newMessage
..reaction = newReaction,
);
expectLater(
// skipping first seed message list -> [] messages
channel.state?.threadsStream
.skip(1)
.map((event) => event['test-parent-id']),
emitsInOrder([
[
isSameMessageAs(
newMessage.copyWith(status: MessageSendingStatus.sent),
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
]),
);
final res = await channel.sendReaction(
message,
type,
enforceUnique: enforceUnique,
);
expect(res, isNotNull);
expect(res.reaction.type, type);
expect(res.reaction.messageId, messageId);
verify(() => client.sendReaction(
messageId,
type,
enforceUnique: enforceUnique,
)).called(1);
},
);
});
group('`.deleteReaction`', () {
test('should work fine', () async {
const userId = 'test-user-id';
@@ -1363,6 +1675,121 @@ void main() {
);
});
group('`.deleteReaction in thread`', () {
test('should work fine', () async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
const parentId = 'test-parent-id';
const type = 'test-reaction-type';
final reaction = Reaction(
type: type,
messageId: messageId,
userId: userId,
);
final message = Message(
id: messageId,
parentId: parentId, // is thread
ownReactions: [reaction],
latestReactions: [reaction],
reactionScores: const {type: 1},
reactionCounts: const {type: 1},
);
when(() => client.deleteReaction(messageId, type))
.thenAnswer((_) async => EmptyResponse());
expectLater(
// skipping first seed message list -> [] messages
channel.state?.threadsStream
.skip(1)
.map((event) => event['test-parent-id']),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
latestReactions: [],
ownReactions: [],
),
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
]),
);
final res = await channel.deleteReaction(message, reaction);
expect(res, isNotNull);
verify(() => client.deleteReaction(messageId, type)).called(1);
});
test(
'should restore prev message state if `client.deleteReaction` throws',
() async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
const parentId = 'test-parent-id';
const type = 'test-reaction-type';
final reaction = Reaction(
type: type,
messageId: messageId,
userId: userId,
);
final message = Message(
id: messageId,
parentId: parentId,
ownReactions: [reaction],
latestReactions: [reaction],
reactionScores: const {type: 1},
reactionCounts: const {type: 1},
);
when(() => client.deleteReaction(messageId, type))
.thenThrow(StreamChatNetworkError(ChatErrorCode.inputError));
expectLater(
// skipping first seed message list -> [] messages
channel.state?.threadsStream
.skip(1)
.map((event) => event['test-parent-id']),
emitsInOrder([
[
isSameMessageAs(
message.copyWith(
status: MessageSendingStatus.sent,
latestReactions: [],
ownReactions: [],
),
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
[
isSameMessageAs(
message,
matchReactions: true,
matchSendingStatus: true,
matchParentId: true,
),
],
]),
);
try {
await channel.deleteReaction(message, reaction);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
}
verify(() => client.deleteReaction(messageId, type)).called(1);
},
);
});
test('`.update`', () async {
const channelData = {
'name': 'Stream Team',
@@ -1772,6 +1772,46 @@ void main() {
verifyNoMoreInteractions(api.user);
});
test('`.partialUpdateUser`', () async {
const userId = 'test-user-id';
final set = {'color': 'yellow'};
final unset = <String>[];
final partialUpdateRequest = PartialUpdateUserRequest(
id: userId,
set: set,
unset: unset,
);
final updatedUser = User(
id: userId,
extraData: {'color': set['color']},
);
when(() => api.user.partialUpdateUsers([partialUpdateRequest]))
.thenAnswer(
(_) async => UpdateUsersResponse()
..users = {
updatedUser.id: updatedUser,
},
);
final res = await client.partialUpdateUser(
userId,
set: set,
unset: unset,
);
expect(res, isNotNull);
expect(res.users, {updatedUser.id: updatedUser});
verify(
() => api.user.partialUpdateUsers([partialUpdateRequest]),
).called(1);
verifyNoMoreInteractions(api.user);
});
test('`.banUser`', () async {
const userId = 'test-user-id';
@@ -1956,23 +1996,109 @@ void main() {
verifyNoMoreInteractions(api.channel);
});
test('`.sendReaction`', () async {
const messageId = 'test-message-id';
const reactionType = 'like';
group('`.sendReaction`', () {
test('`.sendReaction with default params`', () async {
const messageId = 'test-message-id';
const reactionType = 'like';
const extraData = {'score': 1};
when(() => api.message.sendReaction(messageId, reactionType))
.thenAnswer((_) async => SendReactionResponse()
..message = Message(id: messageId)
..reaction = Reaction(type: reactionType, messageId: messageId));
when(() => api.message.sendReaction(
messageId,
reactionType,
extraData: extraData,
)).thenAnswer((_) async => SendReactionResponse()
..message = Message(id: messageId)
..reaction = Reaction(type: reactionType, messageId: messageId));
final res = await client.sendReaction(messageId, reactionType);
expect(res, isNotNull);
expect(res.message.id, messageId);
expect(res.reaction.type, reactionType);
expect(res.reaction.messageId, messageId);
final res = await client.sendReaction(messageId, reactionType);
expect(res, isNotNull);
expect(res.message.id, messageId);
expect(res.reaction.type, reactionType);
expect(res.reaction.messageId, messageId);
verify(() => api.message.sendReaction(messageId, reactionType)).called(1);
verifyNoMoreInteractions(api.message);
verify(() => api.message.sendReaction(
messageId,
reactionType,
extraData: extraData,
)).called(1);
verifyNoMoreInteractions(api.message);
});
test('`.sendReaction with score`', () async {
const messageId = 'test-message-id';
const reactionType = 'like';
const score = 3;
const extraData = {'score': score};
when(() => api.message.sendReaction(
messageId,
reactionType,
extraData: extraData,
)).thenAnswer((_) async => SendReactionResponse()
..message = Message(id: messageId)
..reaction = Reaction(
type: reactionType,
messageId: messageId,
score: score,
));
final res = await client.sendReaction(
messageId,
reactionType,
score: score,
);
expect(res, isNotNull);
expect(res.message.id, messageId);
expect(res.reaction.type, reactionType);
expect(res.reaction.messageId, messageId);
expect(res.reaction.score, score);
verify(() => api.message.sendReaction(
messageId,
reactionType,
extraData: extraData,
)).called(1);
verifyNoMoreInteractions(api.message);
});
test('`.sendReaction with score passed in extradata also`', () async {
const messageId = 'test-message-id';
const reactionType = 'like';
const score = 3;
const extraDataScore = 5;
const extraData = {'score': extraDataScore};
when(() => api.message.sendReaction(
messageId,
reactionType,
extraData: extraData,
)).thenAnswer((_) async => SendReactionResponse()
..message = Message(id: messageId)
..reaction = Reaction(
type: reactionType,
messageId: messageId,
score: extraDataScore,
));
final res = await client.sendReaction(
messageId,
reactionType,
score: score,
extraData: extraData,
);
expect(res, isNotNull);
expect(res.message.id, messageId);
expect(res.reaction.type, reactionType);
expect(res.reaction.messageId, messageId);
expect(res.reaction.score, extraDataScore);
verify(() => api.message.sendReaction(
messageId,
reactionType,
extraData: extraData,
)).called(1);
verifyNoMoreInteractions(api.message);
});
});
test('`.deleteReaction`', () async {
@@ -175,14 +175,14 @@ void main() {
test('markAllRead', () async {
const path = '/channels/read';
when(() => client.post(path)).thenAnswer(
when(() => client.post(path, data: {})).thenAnswer(
(_) async => successResponse(path, data: <String, dynamic>{}));
final res = await channelApi.markAllRead();
expect(res, isNotNull);
verify(() => client.post(path)).called(1);
verify(() => client.post(path, data: {})).called(1);
verifyNoMoreInteractions(client);
});
@@ -82,4 +82,37 @@ void main() {
verify(() => client.post(path, data: any(named: 'data'))).called(1);
verifyNoMoreInteractions(client);
});
test('partialUpdateUsers', () async {
const user = PartialUpdateUserRequest(
id: 'test-user-id',
set: {'color': 'yellow'},
);
const path = '/users';
final updatedUser = {user.id: User(id: user.id, extraData: user.set!)};
when(() => client.patch(path, data: {
'users': [user],
})).thenAnswer(
(_) async => successResponse(
path,
data: {
'users':
updatedUser.map((key, value) => MapEntry(key, value.toJson()))
},
),
);
final res = await userApi.partialUpdateUsers([user]);
expect(res, isNotNull);
expect(res.users.length, updatedUser.length);
verify(() => client.patch(path, data: {
'users': [user]
})).called(1);
verifyNoMoreInteractions(client);
});
}
@@ -49,6 +49,7 @@ Matcher isSameMessageAs(
bool matchSendingStatus = false,
bool matchAttachments = false,
bool matchAttachmentsUploadState = false,
bool matchParentId = false,
}) =>
_IsSameMessageAs(
targetMessage: targetMessage,
@@ -57,6 +58,7 @@ Matcher isSameMessageAs(
matchSendingStatus: matchSendingStatus,
matchAttachments: matchAttachments,
matchAttachmentsUploadState: matchAttachmentsUploadState,
matchParentId: matchParentId,
);
class _IsSameMessageAs extends Matcher {
@@ -67,6 +69,7 @@ class _IsSameMessageAs extends Matcher {
this.matchSendingStatus = false,
this.matchAttachments = false,
this.matchAttachmentsUploadState = false,
this.matchParentId = false,
});
final Message targetMessage;
@@ -75,6 +78,7 @@ class _IsSameMessageAs extends Matcher {
final bool matchSendingStatus;
final bool matchAttachments;
final bool matchAttachmentsUploadState;
final bool matchParentId;
@override
Description describe(Description description) =>
@@ -123,6 +127,9 @@ class _IsSameMessageAs extends Matcher {
matches &= matchAttachments();
}
if (matchParentId) {
matches &= message.parentId == targetMessage.parentId;
}
return matches;
}
}