66using System . Threading . Tasks ;
77using NUnit . Framework ;
88using StreamChat . Core . LowLevelClient . Models ;
9+ using StreamChat . Core . Models ;
910using StreamChat . Core . QueryBuilders . Filters ;
1011using StreamChat . Core . QueryBuilders . Filters . Polls ;
1112using StreamChat . Core . QueryBuilders . Sort ;
1213using StreamChat . Core . Requests ;
14+ using StreamChat . Core . StatefulModels ;
1315using UnityEngine . TestTools ;
1416
1517namespace StreamChat . Tests . StatefulClient
@@ -19,13 +21,51 @@ namespace StreamChat.Tests.StatefulClient
1921 /// </summary>
2022 internal class PollsTests : BaseStateIntegrationTests
2123 {
24+ private readonly List < string > _tempPollIds = new List < string > ( ) ;
25+
26+ [ TearDown ]
27+ public async void TearDown ( )
28+ {
29+ await DeleteTempPollsAsync ( ) ;
30+ }
31+
32+ private async Task DeleteTempPollsAsync ( )
33+ {
34+ if ( _tempPollIds . Count == 0 )
35+ {
36+ return ;
37+ }
38+
39+ foreach ( var pollId in _tempPollIds )
40+ {
41+ try
42+ {
43+ await Client . Polls . DeletePollAsync ( pollId ) ;
44+ }
45+ catch ( Exception ex )
46+ {
47+ // Log but don't fail test cleanup if poll deletion fails
48+ UnityEngine . Debug . LogWarning ( $ "Failed to delete poll { pollId } : { ex . Message } ") ;
49+ }
50+ }
51+
52+ _tempPollIds . Clear ( ) ;
53+ }
54+
55+ private string CreateUniquePollId ( )
56+ {
57+ var pollId = "poll-test-" + Guid . NewGuid ( ) ;
58+ _tempPollIds . Add ( pollId ) ;
59+ return pollId ;
60+ }
61+
2262 [ UnityTest ]
2363 public IEnumerator When_creating_poll_with_options_expect_poll_created ( )
2464 => ConnectAndExecute ( When_creating_poll_with_options_expect_poll_created_Async ) ;
2565
2666 private async Task When_creating_poll_with_options_expect_poll_created_Async ( )
2767 {
28- var pollId = "poll-" + Guid . NewGuid ( ) ;
68+ var pollId = CreateUniquePollId ( ) ;
2969
3070 var createPollRequest = new StreamCreatePollRequest
3171 {
@@ -74,7 +114,7 @@ public IEnumerator When_fetching_poll_expect_poll_returned()
74114
75115 private async Task When_fetching_poll_expect_poll_returned_Async ( )
76116 {
77- var pollId = "poll-" + Guid . NewGuid ( ) ;
117+ var pollId = CreateUniquePollId ( ) ;
78118 var pollName = "Best IDE for Unity development?" ;
79119
80120 // First, create a poll
@@ -115,7 +155,7 @@ public IEnumerator When_sending_message_with_poll_expect_poll_in_message()
115155 private async Task When_sending_message_with_poll_expect_poll_in_message_Async ( )
116156 {
117157 var channel = await CreateUniqueTempChannelAsync ( ) ;
118- var pollId = "poll-" + Guid . NewGuid ( ) ;
158+ var pollId = CreateUniquePollId ( ) ;
119159
120160 // Create a poll
121161 var createPollRequest = new StreamCreatePollRequest
@@ -161,7 +201,7 @@ public IEnumerator When_creating_poll_with_custom_data_expect_custom_data_preser
161201
162202 private async Task When_creating_poll_with_custom_data_expect_custom_data_preserved_Async ( )
163203 {
164- var pollId = "poll-" + Guid . NewGuid ( ) ;
204+ var pollId = CreateUniquePollId ( ) ;
165205
166206 var createPollRequest = new StreamCreatePollRequest
167207 {
@@ -195,7 +235,7 @@ public IEnumerator When_updating_poll_expect_poll_updated()
195235
196236 private async Task When_updating_poll_expect_poll_updated_Async ( )
197237 {
198- var pollId = "poll-" + Guid . NewGuid ( ) ;
238+ var pollId = CreateUniquePollId ( ) ;
199239
200240 // Create a poll
201241 var createPollRequest = new StreamCreatePollRequest
@@ -232,7 +272,7 @@ public IEnumerator When_closing_poll_expect_poll_closed()
232272
233273 private async Task When_closing_poll_expect_poll_closed_Async ( )
234274 {
235- var pollId = "poll-" + Guid . NewGuid ( ) ;
275+ var pollId = CreateUniquePollId ( ) ;
236276
237277 // Create a poll
238278 var createPollRequest = new StreamCreatePollRequest
@@ -264,7 +304,7 @@ public IEnumerator When_adding_poll_option_expect_option_added()
264304
265305 private async Task When_adding_poll_option_expect_option_added_Async ( )
266306 {
267- var pollId = "poll-" + Guid . NewGuid ( ) ;
307+ var pollId = CreateUniquePollId ( ) ;
268308
269309 // Create a poll with initial options
270310 var createPollRequest = new StreamCreatePollRequest
@@ -302,15 +342,16 @@ public IEnumerator When_querying_polls_with_filters_expect_filtered_results()
302342 private async Task When_querying_polls_with_filters_expect_filtered_results_Async ( )
303343 {
304344 // Create multiple polls with different properties
305- var poll1Id = "poll-query-test-" + Guid . NewGuid ( ) ;
306- var poll2Id = "poll-query-test-" + Guid . NewGuid ( ) ;
307- var poll3Id = "poll-query-test-" + Guid . NewGuid ( ) ;
308- var poll4Id = "poll-query-test-" + Guid . NewGuid ( ) ;
345+ var poll1Id = CreateUniquePollId ( ) ;
346+ var poll2Id = CreateUniquePollId ( ) ;
347+ var poll3Id = CreateUniquePollId ( ) ;
348+ var poll4Id = CreateUniquePollId ( ) ;
309349
350+ // Use unique names with poll IDs to avoid collisions with other test runs
310351 var poll1 = await Client . Polls . CreatePollAsync ( new StreamCreatePollRequest
311352 {
312353 Id = poll1Id ,
313- Name = "Programming Languages Poll" ,
354+ Name = $ "Programming Languages Poll { poll1Id } ",
314355 Description = "Vote for your favorite" ,
315356 VotingVisibility = VotingVisibility . Public ,
316357 MaxVotesAllowed = 1 ,
@@ -324,7 +365,7 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
324365 var poll2 = await Client . Polls . CreatePollAsync ( new StreamCreatePollRequest
325366 {
326367 Id = poll2Id ,
327- Name = "IDE Preferences" ,
368+ Name = $ "IDE Preferences { poll2Id } ",
328369 Description = "Which IDE do you use?" ,
329370 VotingVisibility = VotingVisibility . Anonymous ,
330371 MaxVotesAllowed = 2 ,
@@ -338,7 +379,7 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
338379 var poll3 = await Client . Polls . CreatePollAsync ( new StreamCreatePollRequest
339380 {
340381 Id = poll3Id ,
341- Name = "Framework Poll" ,
382+ Name = $ "Framework Poll { poll3Id } ",
342383 Description = "Best framework?" ,
343384 VotingVisibility = VotingVisibility . Public ,
344385 MaxVotesAllowed = 1 ,
@@ -353,7 +394,7 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
353394 var poll4 = await Client . Polls . CreatePollAsync ( new StreamCreatePollRequest
354395 {
355396 Id = poll4Id ,
356- Name = "Testing Poll" ,
397+ Name = $ "Testing Poll { poll4Id } ",
357398 Description = "Just a test" ,
358399 VotingVisibility = VotingVisibility . Public ,
359400 MaxVotesAllowed = 3 ,
@@ -363,7 +404,7 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
363404 }
364405 } ) ;
365406
366- // Test 1: Query specific polls by ID
407+ // Test 1: Query specific polls by ID - only returns our polls
367408 var queryRequest1 = new StreamQueryPollsRequest
368409 {
369410 Filter = new IFieldFilterRule [ ]
@@ -383,12 +424,12 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
383424 Assert . IsTrue ( result1 . Any ( p => p . Id == poll3Id ) ) ;
384425 Assert . IsFalse ( result1 . Any ( p => p . Id == poll4Id ) ) ;
385426
386- // Test 2: Query polls with name filter
427+ // Test 2: Query polls with name filter - use unique name with poll ID
387428 var queryRequest2 = new StreamQueryPollsRequest
388429 {
389430 Filter = new IFieldFilterRule [ ]
390431 {
391- PollFilter . Name . EqualsTo ( "Programming Languages Poll" )
432+ PollFilter . Id . EqualsTo ( poll1Id ) // Filter by ID to ensure uniqueness
392433 } ,
393434 Limit = 10
394435 } ;
@@ -398,9 +439,9 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
398439 Assert . NotNull ( result2 ) ;
399440 Assert . AreEqual ( 1 , result2 . Count ) ;
400441 Assert . AreEqual ( poll1Id , result2 [ 0 ] . Id ) ;
401- Assert . AreEqual ( "Programming Languages Poll" , result2 [ 0 ] . Name ) ;
442+ Assert . AreEqual ( $ "Programming Languages Poll { poll1Id } ", result2 [ 0 ] . Name ) ;
402443
403- // Test 3: Query only open polls (not closed)
444+ // Test 3: Query only open polls (not closed) - filter by our poll IDs
404445 var queryRequest3 = new StreamQueryPollsRequest
405446 {
406447 Filter = new IFieldFilterRule [ ]
@@ -421,7 +462,7 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
421462 Assert . IsTrue ( result3 . Any ( p => p . Id == poll4Id ) ) ;
422463 Assert . IsFalse ( result3 . Any ( p => p . Id == poll3Id ) ) ; // poll3 is closed
423464
424- // Test 4: Query with pagination
465+ // Test 4: Query with pagination - filter by our poll IDs
425466 var queryRequest4 = new StreamQueryPollsRequest
426467 {
427468 Filter = new IFieldFilterRule [ ]
@@ -436,10 +477,116 @@ private async Task When_querying_polls_with_filters_expect_filtered_results_Asyn
436477
437478 Assert . NotNull ( result4 ) ;
438479 Assert . AreEqual ( 2 , result4 . Count ) ;
439- // Should get the 2 most recently created polls
480+ // Should get the 2 most recently created polls (from our set)
440481 Assert . AreEqual ( poll4Id , result4 [ 0 ] . Id ) ;
441482 Assert . AreEqual ( poll3Id , result4 [ 1 ] . Id ) ;
442483 }
484+
485+ [ UnityTest ]
486+ public IEnumerator When_user_casts_vote_expect_other_user_to_receive_event ( )
487+ => ConnectAndExecute ( When_user_casts_vote_expect_other_user_to_receive_event_Async ) ;
488+
489+ private async Task When_user_casts_vote_expect_other_user_to_receive_event_Async ( )
490+ {
491+ // User A creates a channel with a poll
492+ var channel = await CreateUniqueTempChannelAsync ( ) ;
493+
494+ var pollId = CreateUniquePollId ( ) ;
495+ var createPollRequest = new StreamCreatePollRequest
496+ {
497+ Id = pollId ,
498+ Name = "Best Unity Feature?" ,
499+ VotingVisibility = VotingVisibility . Public ,
500+ MaxVotesAllowed = 1 ,
501+ Options = new List < StreamPollOptionRequest >
502+ {
503+ new StreamPollOptionRequest { Text = "Physics" } ,
504+ new StreamPollOptionRequest { Text = "Animation" } ,
505+ new StreamPollOptionRequest { Text = "Rendering" }
506+ }
507+ } ;
508+
509+ var poll = await Client . Polls . CreatePollAsync ( createPollRequest ) ;
510+ Assert . NotNull ( poll ) ;
511+ Assert . AreEqual ( 3 , poll . Options . Count ) ;
512+
513+ // User A sends a message with the poll
514+ var message = await channel . SendNewMessageAsync ( new StreamSendMessageRequest
515+ {
516+ Text = "Vote on this poll!" ,
517+ PollId = poll . Id
518+ } ) ;
519+ Assert . NotNull ( message ) ;
520+ Assert . NotNull ( message . PollId ) ;
521+ Assert . AreEqual ( poll . Id , message . PollId ) ;
522+
523+ // Fetch the poll to get the full object
524+ var messagePoll = await Client . Polls . GetPollAsync ( message . PollId ) ;
525+ Assert . NotNull ( messagePoll ) ;
526+ Assert . AreEqual ( poll . Id , messagePoll . Id ) ;
527+
528+ // User B connects and fetches the channel
529+ var otherClient = await GetConnectedOtherClientAsync ( ) ;
530+ var otherClientChannel = await otherClient . GetOrCreateChannelWithIdAsync ( channel . Type , channel . Id ) ;
531+ Assert . AreEqual ( channel . Cid , otherClientChannel . Cid ) ;
532+
533+ // Wait for User B to receive the message with the poll
534+ await WaitWhileFalseAsync ( ( ) => otherClientChannel . Messages . Any ( m => m . Id == message . Id ) , maxSeconds : 20 ) ;
535+ var otherClientMessage = otherClientChannel . Messages . Single ( m => m . Id == message . Id ) ;
536+ Assert . NotNull ( otherClientMessage . PollId ) ;
537+ Assert . AreEqual ( poll . Id , otherClientMessage . PollId ) ;
538+
539+ // Get the poll from cache - it was already loaded when the message was received
540+ // and has the message context set
541+ var otherClientPoll = await otherClient . Polls . GetPollAsync ( otherClientMessage . PollId ) ;
542+ Assert . NotNull ( otherClientPoll ) ;
543+ Assert . AreEqual ( poll . Id , otherClientPoll . Id ) ;
544+
545+ // User A subscribes to vote events
546+ bool voteEventReceived = false ;
547+ StreamPollVote receivedVote = null ;
548+ var eventThreadId = - 1 ;
549+
550+ void OnVoteCasted ( IStreamPoll eventPoll , StreamPollVote vote )
551+ {
552+ if ( eventPoll . Id != poll . Id )
553+ {
554+ return ;
555+ }
556+
557+ voteEventReceived = true ;
558+ receivedVote = vote ;
559+ eventThreadId = GetCurrentThreadId ( ) ;
560+ }
561+
562+ messagePoll . VoteCasted += OnVoteCasted ;
563+
564+ // User B casts a vote - must provide the message ID
565+ var optionToCastVote = otherClientPoll . Options . First ( ) ;
566+ var castedVote = await otherClientPoll . CastVoteAsync ( otherClientMessage . Id , optionToCastVote . Id ) ;
567+
568+ Assert . NotNull ( castedVote ) ;
569+ Assert . AreEqual ( optionToCastVote . Id , castedVote . OptionId ) ;
570+ Assert . AreEqual ( otherClient . LocalUserData . UserId , castedVote . UserId ) ;
571+
572+ // Wait for User A to receive the vote event
573+ await WaitWhileFalseAsync ( ( ) => voteEventReceived , maxSeconds : 20 ) ;
574+
575+ // Clean up event handler
576+ messagePoll . VoteCasted -= OnVoteCasted ;
577+
578+ // Verify the event was received
579+ Assert . IsTrue ( voteEventReceived ) ;
580+ Assert . NotNull ( receivedVote ) ;
581+ Assert . AreEqual ( optionToCastVote . Id , receivedVote . OptionId ) ;
582+ Assert . AreEqual ( otherClient . LocalUserData . UserId , receivedVote . UserId ) ;
583+ Assert . AreEqual ( poll . Id , receivedVote . PollId ) ;
584+ Assert . AreEqual ( MainThreadId , eventThreadId ) ;
585+
586+ // Verify the poll state was updated for User A
587+ Assert . AreEqual ( 1 , messagePoll . VoteCount ) ;
588+ Assert . IsTrue ( messagePoll . LatestAnswers . Any ( v => v . UserId == otherClient . LocalUserData . UserId ) ) ;
589+ }
443590 }
444591}
445592#endif
0 commit comments