@@ -19,6 +19,7 @@ package io.getstream.chat.android.compose.viewmodel.channels
1919import io.getstream.chat.android.client.ChatClient
2020import io.getstream.chat.android.client.api.models.QueryChannelsRequest
2121import io.getstream.chat.android.client.channel.ChannelClient
22+ import io.getstream.chat.android.client.persistance.repository.RepositoryFacade
2223import io.getstream.chat.android.client.setup.state.ClientState
2324import io.getstream.chat.android.compose.state.channels.list.ItemState
2425import io.getstream.chat.android.compose.state.channels.list.SearchQuery
@@ -30,10 +31,12 @@ import io.getstream.chat.android.models.FilterObject
3031import io.getstream.chat.android.models.Filters
3132import io.getstream.chat.android.models.InitializationState
3233import io.getstream.chat.android.models.OrFilterObject
34+ import io.getstream.chat.android.models.SearchMessagesResult
3335import io.getstream.chat.android.models.TypingEvent
3436import io.getstream.chat.android.models.User
3537import io.getstream.chat.android.models.querysort.QuerySortByField
3638import io.getstream.chat.android.models.querysort.QuerySorter
39+ import io.getstream.chat.android.randomMessage
3740import io.getstream.chat.android.state.event.handler.chat.factory.ChatEventHandlerFactory
3841import io.getstream.chat.android.state.plugin.internal.StatePlugin
3942import io.getstream.chat.android.state.plugin.state.StateRegistry
@@ -48,10 +51,15 @@ import kotlinx.coroutines.flow.MutableStateFlow
4851import kotlinx.coroutines.test.TestScope
4952import kotlinx.coroutines.test.advanceUntilIdle
5053import kotlinx.coroutines.test.runTest
51- import org.amshove.kluent.`should be equal to`
54+ import org.junit.jupiter.api.Assertions.assertEquals
55+ import org.junit.jupiter.api.Assertions.assertFalse
56+ import org.junit.jupiter.api.Assertions.assertInstanceOf
57+ import org.junit.jupiter.api.Assertions.assertNull
58+ import org.junit.jupiter.api.Assertions.assertTrue
5259import org.junit.jupiter.api.Test
5360import org.junit.jupiter.api.extension.ExtendWith
5461import org.mockito.kotlin.any
62+ import org.mockito.kotlin.anyOrNull
5563import org.mockito.kotlin.argumentCaptor
5664import org.mockito.kotlin.doReturn
5765import org.mockito.kotlin.eq
@@ -76,8 +84,8 @@ internal class ChannelListViewModelTest {
7684 .get(this )
7785
7886 val channelsState = viewModel.channelsState
79- channelsState.channelItems.size `should be equal to` 0
80- channelsState.isLoading `should be equal to` true
87+ assertEquals( 0 , channelsState.channelItems.size)
88+ assertTrue( channelsState.isLoading)
8189 }
8290
8391 @Test
@@ -95,8 +103,8 @@ internal class ChannelListViewModelTest {
95103 .get(this )
96104
97105 val channelsState = viewModel.channelsState
98- channelsState.channelItems.size `should be equal to` 2
99- channelsState.isLoading `should be equal to` false
106+ assertEquals( 2 , channelsState.channelItems.size)
107+ assertFalse( channelsState.isLoading)
100108 }
101109
102110 @Test
@@ -118,8 +126,8 @@ internal class ChannelListViewModelTest {
118126 viewModel.performChannelAction(DeleteConversation (channel1))
119127 viewModel.deleteConversation(channel1)
120128
121- viewModel.activeChannelAction `should be equal to` null
122- viewModel.selectedChannel.value `should be equal to` null
129+ assertNull( viewModel.activeChannelAction)
130+ assertNull( viewModel.selectedChannel.value)
123131 verify(channelClient).delete()
124132 }
125133
@@ -140,8 +148,8 @@ internal class ChannelListViewModelTest {
140148 viewModel.selectChannel(channel1)
141149 viewModel.muteChannel(channel1)
142150
143- viewModel.activeChannelAction `should be equal to` null
144- viewModel.selectedChannel.value `should be equal to` null
151+ assertNull( viewModel.activeChannelAction)
152+ assertNull( viewModel.selectedChannel.value)
145153 verify(chatClient).muteChannel(" messaging" , " channel1" , null )
146154 }
147155
@@ -171,9 +179,9 @@ internal class ChannelListViewModelTest {
171179 viewModel.selectChannel(channel1)
172180 viewModel.unmuteChannel(channel1)
173181
174- ( viewModel.channelsState.channelItems.first() as ItemState .ChannelItemState ).isMuted `should be equal to` true
175- viewModel.activeChannelAction `should be equal to` null
176- viewModel.selectedChannel.value `should be equal to` null
182+ assertTrue(( viewModel.channelsState.channelItems.first() as ItemState .ChannelItemState ).isMuted)
183+ assertNull( viewModel.activeChannelAction)
184+ assertNull( viewModel.selectedChannel.value)
177185 verify(chatClient).unmuteChannel(" messaging" , " channel1" )
178186 }
179187
@@ -193,8 +201,8 @@ internal class ChannelListViewModelTest {
193201 viewModel.selectChannel(channel1)
194202 viewModel.dismissChannelAction()
195203
196- viewModel.activeChannelAction `should be equal to` null
197- viewModel.selectedChannel.value `should be equal to` null
204+ assertNull( viewModel.activeChannelAction)
205+ assertNull( viewModel.selectedChannel.value)
198206 }
199207
200208 @Test
@@ -223,8 +231,8 @@ internal class ChannelListViewModelTest {
223231
224232 val captor = argumentCaptor<QueryChannelsRequest >()
225233 verify(chatClient, times(2 )).queryChannels(captor.capture())
226- captor.firstValue.offset `should be equal to` 0
227- captor.secondValue.offset `should be equal to` 30
234+ assertEquals( 0 , captor.firstValue.offset)
235+ assertEquals( 30 , captor.secondValue.offset)
228236 }
229237
230238 @Test
@@ -246,7 +254,7 @@ internal class ChannelListViewModelTest {
246254
247255 val captor = argumentCaptor<QueryChannelsRequest >()
248256 verify(chatClient, times(1 )).queryChannels(captor.capture())
249- captor.firstValue.offset `should be equal to` 0
257+ assertEquals( 0 , captor.firstValue.offset)
250258 }
251259
252260 @Test
@@ -271,8 +279,8 @@ internal class ChannelListViewModelTest {
271279 val andFilterObject = captor.secondValue.filter as AndFilterObject
272280 val orFilterObject = andFilterObject.filterObjects.last() as OrFilterObject
273281 val autoCompleteFilterObject = orFilterObject.filterObjects.last() as AutocompleteFilterObject
274- autoCompleteFilterObject.fieldName `should be equal to` " name"
275- autoCompleteFilterObject.value `should be equal to` " Search query"
282+ assertEquals( " name" , autoCompleteFilterObject.fieldName)
283+ assertEquals( " Search query" , autoCompleteFilterObject.value)
276284 }
277285
278286 @Test
@@ -301,8 +309,8 @@ internal class ChannelListViewModelTest {
301309 val andFilterObject = captor.secondValue.filter as AndFilterObject
302310 val orFilterObject = andFilterObject.filterObjects.last() as OrFilterObject
303311 val autoCompleteFilterObject = orFilterObject.filterObjects.last() as AutocompleteFilterObject
304- autoCompleteFilterObject.fieldName `should be equal to` " name"
305- autoCompleteFilterObject.value `should be equal to` " Search query"
312+ assertEquals( " name" , autoCompleteFilterObject.fieldName)
313+ assertEquals( " Search query" , autoCompleteFilterObject.value)
306314 }
307315
308316 @Test
@@ -333,9 +341,125 @@ internal class ChannelListViewModelTest {
333341
334342 val captor = argumentCaptor<QueryChannelsRequest >()
335343 verify(chatClient, times(2 )).queryChannels(captor.capture())
336- captor.allValues.size `should be equal to` 2
337- captor.firstValue.offset `should be equal to` 0
338- captor.secondValue.offset `should be equal to` 30
344+ assertEquals(2 , captor.allValues.size)
345+ assertEquals(0 , captor.firstValue.offset)
346+ assertEquals(30 , captor.secondValue.offset)
347+ }
348+
349+ @Test
350+ fun `Given channel list When setting message search query Should search messages without offset or cursor` () =
351+ runTest {
352+ val chatClient: ChatClient = mock()
353+ val messages = listOf (randomMessage(cid = " messaging:channel1" ))
354+ val searchResult = SearchMessagesResult (messages = messages, next = " cursor_page2" )
355+ val viewModel = Fixture (chatClient)
356+ .givenCurrentUser()
357+ .givenChannelsQuery()
358+ .givenChannelsState(
359+ channelsStateData = ChannelsStateData .Result (listOf (channel1)),
360+ loading = false ,
361+ )
362+ .givenChannelMutes()
363+ .givenSearchMessagesResult(searchResult)
364+ .givenRepositorySelectChannels(listOf (channel1))
365+ .get(this )
366+
367+ viewModel.setSearchQuery(SearchQuery .Messages (" hello" ))
368+ advanceUntilIdle()
369+
370+ verify(chatClient).searchMessages(
371+ channelFilter = any(),
372+ messageFilter = any(),
373+ offset = eq(null ),
374+ limit = any(),
375+ next = eq(null ),
376+ sort = eq(null ),
377+ )
378+ val items = viewModel.channelsState.channelItems
379+ assertEquals(1 , items.size)
380+ assertInstanceOf(ItemState .SearchResultItemState ::class .java, items.first())
381+ }
382+
383+ @Test
384+ fun `Given message search results with next cursor When loading more Should pass the cursor` () =
385+ runTest {
386+ val chatClient: ChatClient = mock()
387+ val firstPageMessages = listOf (randomMessage(cid = " messaging:channel1" ))
388+ val firstPageResult = SearchMessagesResult (messages = firstPageMessages, next = " cursor_page2" )
389+ val secondPageMessages = listOf (randomMessage(cid = " messaging:channel1" ))
390+ val secondPageResult = SearchMessagesResult (messages = secondPageMessages, next = null )
391+
392+ whenever(
393+ chatClient.searchMessages(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull()),
394+ ).doReturn(
395+ firstPageResult.asCall(),
396+ secondPageResult.asCall(),
397+ )
398+
399+ val viewModel = Fixture (chatClient)
400+ .givenCurrentUser()
401+ .givenChannelsQuery()
402+ .givenChannelsState(
403+ channelsStateData = ChannelsStateData .Result (listOf (channel1)),
404+ loading = false ,
405+ )
406+ .givenChannelMutes()
407+ .givenRepositorySelectChannels(listOf (channel1))
408+ .get(this )
409+
410+ viewModel.setSearchQuery(SearchQuery .Messages (" hello" ))
411+ advanceUntilIdle()
412+
413+ viewModel.loadMore()
414+ advanceUntilIdle()
415+
416+ val captor = argumentCaptor<String >()
417+ verify(chatClient, times(2 )).searchMessages(
418+ channelFilter = any(),
419+ messageFilter = any(),
420+ offset = anyOrNull(),
421+ limit = anyOrNull(),
422+ next = captor.capture(),
423+ sort = anyOrNull(),
424+ )
425+ assertNull(captor.firstValue)
426+ assertEquals(" cursor_page2" , captor.secondValue)
427+ }
428+
429+ @Test
430+ fun `Given message search results without next cursor When loading more Should not load more` () =
431+ runTest {
432+ val chatClient: ChatClient = mock()
433+ val messages = listOf (randomMessage(cid = " messaging:channel1" ))
434+ val searchResult = SearchMessagesResult (messages = messages, next = null )
435+ val viewModel = Fixture (chatClient)
436+ .givenCurrentUser()
437+ .givenChannelsQuery()
438+ .givenChannelsState(
439+ channelsStateData = ChannelsStateData .Result (listOf (channel1)),
440+ loading = false ,
441+ )
442+ .givenChannelMutes()
443+ .givenSearchMessagesResult(searchResult)
444+ .givenRepositorySelectChannels(listOf (channel1))
445+ .get(this )
446+
447+ viewModel.setSearchQuery(SearchQuery .Messages (" hello" ))
448+ advanceUntilIdle()
449+
450+ assertTrue(viewModel.channelsState.endOfChannels)
451+
452+ viewModel.loadMore()
453+ advanceUntilIdle()
454+
455+ verify(chatClient, times(1 )).searchMessages(
456+ channelFilter = any(),
457+ messageFilter = any(),
458+ offset = anyOrNull(),
459+ limit = anyOrNull(),
460+ next = anyOrNull(),
461+ sort = anyOrNull(),
462+ )
339463 }
340464
341465 private class Fixture (
@@ -347,6 +471,7 @@ internal class ChannelListViewModelTest {
347471 private val clientState: ClientState = mock()
348472 private val stateRegistry: StateRegistry = mock()
349473 private val globalState: GlobalState = mock()
474+ private val repositoryFacade: RepositoryFacade = mock()
350475
351476 init {
352477 val statePlugin: StatePlugin = mock()
@@ -357,6 +482,7 @@ internal class ChannelListViewModelTest {
357482 whenever(chatClient.channel(any())) doReturn channelClient
358483 whenever(chatClient.channel(any(), any())) doReturn channelClient
359484 whenever(chatClient.clientState) doReturn clientState
485+ whenever(chatClient.repositoryFacade) doReturn repositoryFacade
360486 whenever(globalState.channelDraftMessages) doReturn MutableStateFlow (emptyMap())
361487 }
362488
@@ -394,6 +520,16 @@ internal class ChannelListViewModelTest {
394520 whenever(chatClient.unmuteChannel(any(), any())) doReturn Unit .asCall()
395521 }
396522
523+ fun givenSearchMessagesResult (result : SearchMessagesResult ) = apply {
524+ whenever(
525+ chatClient.searchMessages(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull()),
526+ ) doReturn result.asCall()
527+ }
528+
529+ suspend fun givenRepositorySelectChannels (channels : List <Channel > = emptyList()) = apply {
530+ whenever(repositoryFacade.selectChannels(any<List <String >>())) doReturn channels
531+ }
532+
397533 fun givenChannelsState (
398534 channelsStateData : ChannelsStateData = ChannelsStateData .Loading ,
399535 channels : List <Channel >? = null,
0 commit comments