Skip to content

Commit 81825c7

Browse files
committed
test: fix user.messages.deleted event tests
Refactors the tests for the `user.messages.deleted` WebSocket event to improve reliability and correctness. - Connects the client and waits for a connected status in `setUp`, ensuring a realistic test environment. - Simplifies event dispatching by sending a single global event instead of one per channel. - Removes an unnecessary test for channel-specific `user.messages.deleted` events, as these are handled by the channel itself. - Adds tests to verify that messages are correctly deleted from the persistence client when `hardDelete` is true.
1 parent 7e82150 commit 81825c7

File tree

2 files changed

+141
-119
lines changed

2 files changed

+141
-119
lines changed

packages/stream_chat/test/src/client/channel_test.dart

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5200,8 +5200,18 @@ void main() {
52005200
const channelId = 'test-channel-id';
52015201
const channelType = 'test-channel-type';
52025202
late Channel channel;
5203+
late MockPersistenceClient persistenceClient;
52035204

52045205
setUp(() {
5206+
persistenceClient = MockPersistenceClient();
5207+
when(() => client.chatPersistenceClient).thenReturn(persistenceClient);
5208+
when(() => persistenceClient.deleteMessageByIds(any()))
5209+
.thenAnswer((_) async {});
5210+
when(() => persistenceClient.deletePinnedMessageByIds(any()))
5211+
.thenAnswer((_) async {});
5212+
when(() => persistenceClient.getChannelThreads(any()))
5213+
.thenAnswer((_) async => <String, List<Message>>{});
5214+
52055215
final channelState = _generateChannelState(channelId, channelType);
52065216
channel = Channel.fromState(client, channelState);
52075217
});
@@ -5468,6 +5478,106 @@ void main() {
54685478
expect(channel.state?.messages.length, equals(0));
54695479
},
54705480
);
5481+
5482+
test(
5483+
'should delete messages from persistence when hardDelete is true',
5484+
() async {
5485+
// Setup: Add messages from different users
5486+
final user1 = User(id: 'user-1', name: 'User 1');
5487+
final user2 = User(id: 'user-2', name: 'User 2');
5488+
5489+
final message1 = Message(
5490+
id: 'msg-1',
5491+
text: 'Message from user 1',
5492+
user: user1,
5493+
);
5494+
final message2 = Message(
5495+
id: 'msg-2',
5496+
text: 'Another message from user 1',
5497+
user: user1,
5498+
);
5499+
final message3 = Message(
5500+
id: 'msg-3',
5501+
text: 'Message from user 2',
5502+
user: user2,
5503+
);
5504+
5505+
channel.state?.updateMessage(message1);
5506+
channel.state?.updateMessage(message2);
5507+
channel.state?.updateMessage(message3);
5508+
5509+
// Verify initial state
5510+
expect(channel.state?.messages.length, equals(3));
5511+
5512+
// Create user.messages.deleted event (hard delete)
5513+
final userMessagesDeletedEvent = Event(
5514+
cid: channel.cid,
5515+
type: EventType.userMessagesDeleted,
5516+
user: user1,
5517+
hardDelete: true,
5518+
);
5519+
5520+
// Dispatch event
5521+
client.addEvent(userMessagesDeletedEvent);
5522+
5523+
// Wait for the event to be processed
5524+
await Future.delayed(Duration.zero);
5525+
5526+
// Verify messages are removed from persistence
5527+
verify(
5528+
() => persistenceClient.deleteMessageByIds(['msg-1', 'msg-2']),
5529+
).called(1);
5530+
verify(
5531+
() =>
5532+
persistenceClient.deletePinnedMessageByIds(['msg-1', 'msg-2']),
5533+
).called(1);
5534+
5535+
// Verify user1's messages are removed from state
5536+
expect(channel.state?.messages.length, equals(1));
5537+
expect(
5538+
channel.state?.messages.any((m) => m.user?.id == 'user-1'),
5539+
isFalse,
5540+
);
5541+
},
5542+
);
5543+
5544+
test(
5545+
'should not delete from persistence when hardDelete is false',
5546+
() async {
5547+
// Setup: Add messages
5548+
final user1 = User(id: 'user-1', name: 'User 1');
5549+
final message1 = Message(
5550+
id: 'msg-1',
5551+
text: 'Message from user 1',
5552+
user: user1,
5553+
);
5554+
5555+
channel.state?.updateMessage(message1);
5556+
5557+
// Create user.messages.deleted event (soft delete)
5558+
final userMessagesDeletedEvent = Event(
5559+
cid: channel.cid,
5560+
type: EventType.userMessagesDeleted,
5561+
user: user1,
5562+
hardDelete: false,
5563+
);
5564+
5565+
// Dispatch event
5566+
client.addEvent(userMessagesDeletedEvent);
5567+
5568+
// Wait for the event to be processed
5569+
await Future.delayed(Duration.zero);
5570+
5571+
// Verify persistence deletion methods were NOT called
5572+
verifyNever(() => persistenceClient.deleteMessageByIds(any()));
5573+
verifyNever(() => persistenceClient.deletePinnedMessageByIds(any()));
5574+
5575+
// Verify message is soft deleted (still in state)
5576+
expect(channel.state?.messages.length, equals(1));
5577+
expect(
5578+
channel.state?.messages.first.type, equals(MessageType.deleted));
5579+
},
5580+
);
54715581
});
54725582
});
54735583

packages/stream_chat/test/src/client/client_test.dart

Lines changed: 31 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,22 +3734,28 @@ void main() {
37343734
verify(() => api.general.sync(cids, lastSyncAt)).called(1);
37353735
});
37363736
});
3737+
});
37373738

3738-
group('User messages deleted event', () {
3739-
const apiKey = 'test-api-key';
3740-
late StreamChatClient client;
3741-
3742-
setUp(() {
3743-
final ws = FakeWebSocket();
3744-
final api = FakeChatApi();
3745-
client = StreamChatClient(apiKey, ws: ws, chatApi: api);
3746-
client.state.currentUser = OwnUser(id: 'test-user');
3747-
});
3739+
group('WS events', () {
3740+
late StreamChatClient client;
37483741

3749-
tearDown(() {
3750-
client.dispose();
3751-
});
3742+
setUp(() async {
3743+
final ws = FakeWebSocket();
3744+
client = StreamChatClient('test-api-key', ws: ws);
3745+
3746+
final user = User(id: 'test-user-id');
3747+
final token = Token.development(user.id).rawValue;
3748+
3749+
await client.connectUser(user, token);
3750+
await delay(300);
3751+
expect(client.wsConnectionStatus, ConnectionStatus.connected);
3752+
});
37523753

3754+
tearDown(() async {
3755+
await client.dispose();
3756+
});
3757+
3758+
group('User messages deleted event', () {
37533759
test(
37543760
'should broadcast global user.messages.deleted event to all channels',
37553761
() async {
@@ -3780,32 +3786,24 @@ void main() {
37803786
final channel2 = Channel.fromState(client, channelState2);
37813787

37823788
// Register channels in client state
3783-
client.state.channels['messaging:channel-1'] = channel1;
3784-
client.state.channels['messaging:channel-2'] = channel2;
3789+
client.state.addChannels({
3790+
'messaging:channel-1': channel1,
3791+
'messaging:channel-2': channel2,
3792+
});
37853793

37863794
// Verify initial state
37873795
expect(channel1.state?.messages.length, equals(1));
37883796
expect(channel2.state?.messages.length, equals(1));
37893797

37903798
// Simulate global user.messages.deleted event being broadcast to channels
37913799
// (In production, ClientState._listenUserMessagesDeleted does this)
3792-
final event1 = Event(
3793-
cid: channel1.cid,
3794-
type: EventType.userMessagesDeleted,
3795-
user: bannedUser,
3796-
hardDelete: false,
3797-
);
3798-
3799-
client.handleEvent(event1);
3800-
3801-
final event2 = Event(
3802-
cid: channel2.cid,
3800+
final event = Event(
38033801
type: EventType.userMessagesDeleted,
38043802
user: bannedUser,
38053803
hardDelete: false,
38063804
);
38073805

3808-
client.handleEvent(event2);
3806+
client.handleEvent(event);
38093807

38103808
// Wait for the events to be processed
38113809
await Future.delayed(Duration.zero);
@@ -3818,10 +3816,6 @@ void main() {
38183816
final channel2Message = channel2.state?.messages.first;
38193817
expect(channel2Message?.type, equals(MessageType.deleted));
38203818
expect(channel2Message?.state.isDeleted, isTrue);
3821-
3822-
// Cleanup
3823-
channel1.dispose();
3824-
channel2.dispose();
38253819
},
38263820
);
38273821

@@ -3862,32 +3856,24 @@ void main() {
38623856
final channel2 = Channel.fromState(client, channelState2);
38633857

38643858
// Register channels in client state
3865-
client.state.channels['messaging:channel-1'] = channel1;
3866-
client.state.channels['messaging:channel-2'] = channel2;
3859+
client.state.addChannels({
3860+
'messaging:channel-1': channel1,
3861+
'messaging:channel-2': channel2,
3862+
});
38673863

38683864
// Verify initial state
38693865
expect(channel1.state?.messages.length, equals(2));
38703866
expect(channel2.state?.messages.length, equals(1));
38713867

38723868
// Simulate global user.messages.deleted event being broadcast to channels
38733869
// (In production, ClientState._listenUserMessagesDeleted does this)
3874-
final event1 = Event(
3875-
cid: channel1.cid,
3876-
type: EventType.userMessagesDeleted,
3877-
user: bannedUser,
3878-
hardDelete: true,
3879-
);
3880-
3881-
client.handleEvent(event1);
3882-
3883-
final event2 = Event(
3884-
cid: channel2.cid,
3870+
final event = Event(
38853871
type: EventType.userMessagesDeleted,
38863872
user: bannedUser,
38873873
hardDelete: true,
38883874
);
38893875

3890-
client.handleEvent(event2);
3876+
client.handleEvent(event);
38913877

38923878
// Wait for the events to be processed
38933879
await Future.delayed(Duration.zero);
@@ -3904,80 +3890,6 @@ void main() {
39043890
final safeMessage =
39053891
channel1.state?.messages.firstWhere((m) => m.id == 'msg-3');
39063892
expect(safeMessage?.user?.id, equals('other-user'));
3907-
3908-
// Cleanup
3909-
channel1.dispose();
3910-
channel2.dispose();
3911-
},
3912-
);
3913-
3914-
test(
3915-
'should not handle channel-specific user.messages.deleted events',
3916-
() async {
3917-
// Add messages
3918-
final bannedUser = User(id: 'banned-user', name: 'Banned User');
3919-
final message1 = Message(
3920-
id: 'msg-1',
3921-
text: 'Message in channel 1',
3922-
user: bannedUser,
3923-
);
3924-
final message2 = Message(
3925-
id: 'msg-2',
3926-
text: 'Message in channel 2',
3927-
user: bannedUser,
3928-
);
3929-
3930-
// Setup: Create channels with state
3931-
final channelState1 = ChannelState(
3932-
channel: ChannelModel(id: 'channel-1', type: 'messaging'),
3933-
messages: [message1],
3934-
);
3935-
final channelState2 = ChannelState(
3936-
channel: ChannelModel(id: 'channel-2', type: 'messaging'),
3937-
messages: [message2],
3938-
);
3939-
3940-
final channel1 = Channel.fromState(client, channelState1);
3941-
final channel2 = Channel.fromState(client, channelState2);
3942-
3943-
// Register channels in client state
3944-
client.state.channels['messaging:channel-1'] = channel1;
3945-
client.state.channels['messaging:channel-2'] = channel2;
3946-
3947-
// Verify initial state
3948-
expect(channel1.state?.messages.length, equals(1));
3949-
expect(channel2.state?.messages.length, equals(1));
3950-
3951-
// Create channel-specific event (has cid)
3952-
final channelSpecificEvent = Event(
3953-
cid: channel1.cid,
3954-
type: EventType.userMessagesDeleted,
3955-
user: bannedUser,
3956-
hardDelete: true,
3957-
);
3958-
3959-
// Dispatch event - this should be handled by channel, not client state
3960-
client.handleEvent(channelSpecificEvent);
3961-
3962-
// Wait for the event to be processed
3963-
await Future.delayed(Duration.zero);
3964-
3965-
// Verify only channel1 is affected (by channel's own handler)
3966-
expect(
3967-
channel1.state?.messages.any((m) => m.user?.id == 'banned-user'),
3968-
isFalse,
3969-
);
3970-
3971-
// Verify channel2 is unaffected (client state didn't broadcast it)
3972-
expect(channel2.state?.messages.length, equals(1));
3973-
expect(
3974-
channel2.state?.messages.any((m) => m.user?.id == 'banned-user'),
3975-
isTrue,
3976-
);
3977-
3978-
// Cleanup
3979-
channel1.dispose();
3980-
channel2.dispose();
39813893
},
39823894
);
39833895
});

0 commit comments

Comments
 (0)