Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feat/logger-with-u…
Browse files Browse the repository at this point in the history
…serid-and-clientid-cherry-pick
  • Loading branch information
saleniuk committed Oct 17, 2023
2 parents 6d092d6 + a71fb6d commit b8f2c17
Show file tree
Hide file tree
Showing 9 changed files with 367 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ interface MessageRepository {
targetConversation: ConversationId
): Either<StorageFailure, Unit>

suspend fun getConversationMessagesFromSearch(
searchQuery: String,
conversationId: ConversationId
): Either<CoreFailure, List<Message.Standalone>>

val extensions: MessageRepositoryExtensions
}

Expand Down Expand Up @@ -636,4 +641,14 @@ class MessageDataSource(
to = targetConversation.toDao()
)
}

override suspend fun getConversationMessagesFromSearch(
searchQuery: String,
conversationId: ConversationId
): Either<CoreFailure, List<Message.Standalone>> = wrapStorageRequest {
messageDAO.getConversationMessagesFromSearch(
searchQuery = searchQuery,
conversationId = conversationId.toDao()
).map(messageMapper::fromEntityToMessage)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.message

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.Message
import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.logic.functional.Either

/**
* Retrieves a list of messages defined by the search query.
* @param searchQuery The search term used to define which messages will be returned.
* @param conversationId The conversation ID that it will look for messages in.
* @return A [Either<CoreFailure, List<Message.Standalone>>] indicating the success of the operation.
*/
interface GetConversationMessagesFromSearchQueryUseCase {

suspend operator fun invoke(
searchQuery: String,
conversationId: ConversationId
): Either<CoreFailure, List<Message.Standalone>>
}

internal class GetConversationMessagesFromSearchQueryUseCaseImpl internal constructor(
private val messageRepository: MessageRepository
) : GetConversationMessagesFromSearchQueryUseCase {

override suspend fun invoke(
searchQuery: String,
conversationId: ConversationId
): Either<CoreFailure, List<Message.Standalone>> = messageRepository.getConversationMessagesFromSearch(
searchQuery = searchQuery,
conversationId = conversationId
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,9 @@ class MessageScope internal constructor(
selfUserId = selfUserId,
selfConversationIdProvider = selfConversationIdProvider
)

val getConversationMessagesFromSearchQuery: GetConversationMessagesFromSearchQueryUseCase
get() = GetConversationMessagesFromSearchQueryUseCaseImpl(
messageRepository = messageRepository
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ import io.mockative.matching
import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
Expand Down Expand Up @@ -472,6 +471,58 @@ class MessageRepositoryTest {
}
}

@Test
fun givenConversationWithMessages_whenSearchingForSpecificMessages_thenReturnOnlyMetCriteriaMessages() = runTest {
// given
val qualifiedIdEntity = TEST_QUALIFIED_ID_ENTITY
val conversationId = TEST_CONVERSATION_ID
val searchTerm = "message 1"

val messageEntity1 = TEST_MESSAGE_ENTITY.copy(
id = "msg1",
conversationId = qualifiedIdEntity,
content = MessageEntityContent.Text("message 10")
)

val messages = listOf(messageEntity1)

val message1 = TEST_MESSAGE.copy(
id = "msg1",
conversationId = conversationId,
content = MessageContent.Text("message 10")
)

val expectedMessages = listOf(message1)

val (_, messageRepository) = Arrangement()
.withMessagesFromSearch(
searchTerm = searchTerm,
conversationId = qualifiedIdEntity,
messages = messages
)
.withMappedMessageModel(
result = message1,
param = messageEntity1
)
.arrange()

// when
val result = messageRepository.getConversationMessagesFromSearch(
searchQuery = searchTerm,
conversationId = conversationId
)

// then
assertEquals(
expectedMessages.size,
(result as Either.Right).value.size
)
assertEquals(
expectedMessages.first().id,
(result as Either.Right).value.first().id
)
}

private class Arrangement {

@Mock
Expand Down Expand Up @@ -514,6 +565,14 @@ class MessageRepositoryTest {
return this
}

fun withMappedMessageModel(result: Message.Regular, param: MessageEntity.Regular): Arrangement {
given(messageMapper)
.function(messageMapper::fromEntityToMessage)
.whenInvokedWith(eq(param))
.then { result }
return this
}

fun withMappedMessageEntity(message: MessageEntity.Regular): Arrangement {
given(messageMapper)
.function(messageMapper::fromMessageToEntity)
Expand Down Expand Up @@ -605,6 +664,17 @@ class MessageRepositoryTest {
.thenThrow(throwable)
}

fun withMessagesFromSearch(
searchTerm: String,
conversationId: QualifiedIDEntity,
messages: List<MessageEntity>
) = apply {
given(messageDAO)
.suspendFunction(messageDAO::getConversationMessagesFromSearch)
.whenInvokedWith(eq(searchTerm), eq(conversationId))
.thenReturn(messages)
}

fun arrange() = this to MessageDataSource(
messageApi = messageApi,
mlsMessageApi = mlsMessageApi,
Expand Down Expand Up @@ -668,4 +738,3 @@ class MessageRepositoryTest {
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.message

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.Message
import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.logic.framework.TestConversation
import com.wire.kalium.logic.framework.TestMessage
import com.wire.kalium.logic.functional.Either
import io.mockative.Mock
import io.mockative.given
import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

class GetConversationMessagesFromSearchQueryUseCaseTest {

@Test
fun givenSearchTermAndConversationId_whenInvokingUseCase_thenShouldCallMessageRepository() = runTest {
// given
val searchTerm = "message 1"
val (arrangement, useCase) = Arrangement()
.withRepositoryMessagesBySearchTermReturning(
conversationId = CONVERSATION_ID,
searchTerm = searchTerm,
response = Either.Left(StorageFailure.DataNotFound)
)
.arrange()

// when
useCase(
searchQuery = searchTerm,
conversationId = CONVERSATION_ID
)

// then
verify(arrangement.messageRepository)
.coroutine { arrangement.messageRepository.getConversationMessagesFromSearch(searchTerm, CONVERSATION_ID) }
.wasInvoked(exactly = once)
}

@Test
fun givenRepositoryFails_whenInvokingUseCase_thenShouldPropagateTheFailure() = runTest {
// given
val searchTerm = "message 1"
val cause = StorageFailure.DataNotFound
val (_, useCase) = Arrangement()
.withRepositoryMessagesBySearchTermReturning(
conversationId = CONVERSATION_ID,
searchTerm = searchTerm,
response = Either.Left(cause)
)
.arrange()

// when
val result = useCase(
searchQuery = searchTerm,
conversationId = CONVERSATION_ID
)

// then
assertIs<Either.Left<StorageFailure.DataNotFound>>(result)
assertEquals(cause, result.value)
}

@Test
fun givenRepositorySucceeds_whenInvokingUseCase_thenShouldPropagateTheSuccess() = runTest {
// given
val searchTerm = "message 1"
val (_, useCase) = Arrangement()
.withRepositoryMessagesBySearchTermReturning(
conversationId = CONVERSATION_ID,
searchTerm = searchTerm,
response = Either.Right(MESSAGES)
)
.arrange()

// when
val result = useCase(
searchQuery = searchTerm,
conversationId = CONVERSATION_ID
)

// then
assertIs<Either.Right<List<Message.Standalone>>>(result)
}

private inner class Arrangement {

@Mock
val messageRepository: MessageRepository = mock(MessageRepository::class)

private val getMessageById by lazy {
GetConversationMessagesFromSearchQueryUseCaseImpl(messageRepository)
}

suspend fun withRepositoryMessagesBySearchTermReturning(
conversationId: ConversationId,
searchTerm: String,
response: Either<CoreFailure, List<Message.Standalone>>
) = apply {
given(messageRepository)
.coroutine { messageRepository.getConversationMessagesFromSearch(searchTerm, conversationId) }
.thenReturn(response)
}

fun arrange() = this to getMessageById
}

private companion object {
val MESSAGES = listOf(TestMessage.TEXT_MESSAGE)
val CONVERSATION_ID = TestConversation.ID
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -511,3 +511,10 @@ moveMessages:
UPDATE OR REPLACE Message
SET conversation_id = :to
WHERE conversation_id = :from;

selectConversationMessagesFromSearch:
SELECT * FROM MessageDetailsView
WHERE text LIKE ('%' || :searchQuery || '%')
AND conversationId = :conversationId
AND contentType = 'TEXT'
ORDER BY date DESC;
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,10 @@ interface MessageDAO {

suspend fun moveMessages(from: ConversationIDEntity, to: ConversationIDEntity)

suspend fun getConversationMessagesFromSearch(
searchQuery: String,
conversationId: QualifiedIDEntity
): List<MessageEntity>

val platformExtensions: MessageExtensions
}
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,17 @@ internal class MessageDAOImpl internal constructor(
.distinctUntilChanged()
}

override suspend fun getConversationMessagesFromSearch(
searchQuery: String,
conversationId: QualifiedIDEntity
): List<MessageEntity> = withContext(coroutineContext) {
queries.selectConversationMessagesFromSearch(
searchQuery,
conversationId,
mapper::toEntityMessageFromView
).executeAsList()
}

override val platformExtensions: MessageExtensions = MessageExtensionsImpl(queries, mapper, coroutineContext)

}
Loading

0 comments on commit b8f2c17

Please sign in to comment.