Skip to content

Commit

Permalink
fix: exclude the self user from the search results (AR-1830) (#609)
Browse files Browse the repository at this point in the history
* fix: exlucde the self user from the search results

* fix failing test

* fix the test failing

* fix

* fix

* fix tests
  • Loading branch information
trOnk12 committed Jun 27, 2022
1 parent 65aa918 commit 50b2ffc
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,42 @@ package com.wire.kalium.logic.feature.publicuser
import com.wire.kalium.logic.data.id.FEDERATION_REGEX
import com.wire.kalium.logic.data.id.parseIntoQualifiedID
import com.wire.kalium.logic.data.publicuser.SearchUserRepository
import com.wire.kalium.logic.data.publicuser.model.UserSearchResult
import com.wire.kalium.logic.data.user.UserRepository
import kotlinx.coroutines.flow.firstOrNull


interface SearchKnownUsersUseCase {
suspend operator fun invoke(searchQuery: String): Result
}

internal class SearchKnownUsersUseCaseImpl(
private val searchUserRepository: SearchUserRepository
private val searchUserRepository: SearchUserRepository,
private val userRepository: UserRepository,
) : SearchKnownUsersUseCase {

//TODO:handle failure
override suspend operator fun invoke(searchQuery: String): Result {
return if (isUserLookingForHandle(searchQuery)) {
Result.Success(searchUserRepository.searchKnownUsersByHandle(searchQuery))
val searchResult = if (isUserLookingForHandle(searchQuery)) {
searchUserRepository.searchKnownUsersByHandle(searchQuery)
} else {
Result.Success(
searchUserRepository.searchKnownUsersByNameOrHandleOrEmail(
if (searchQuery.matches(FEDERATION_REGEX))
searchQuery.parseIntoQualifiedID().value
else searchQuery
)
searchUserRepository.searchKnownUsersByNameOrHandleOrEmail(
if (searchQuery.matches(FEDERATION_REGEX))
searchQuery.parseIntoQualifiedID().value
else searchQuery
)
}

return Result.Success(excludeSelfUser(searchResult))
}

private fun isUserLookingForHandle(searchQuery: String) = searchQuery.startsWith('@')

//TODO: we should think about the way to exclude the self user on TABLE level
private suspend fun excludeSelfUser(searchResult: UserSearchResult): UserSearchResult {
val selfUser = userRepository.getSelfUser()

return searchResult.copy(result = searchResult.result.filter { it.id != selfUser?.id })
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ sealed class Result {
object InvalidQuery : Failure()
object InvalidRequest : Failure()
class Generic(val genericFailure: CoreFailure) : Failure()

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class UserScope(
val syncContacts: SyncContactsUseCase get() = SyncContactsUseCaseImpl(userRepository)
val uploadUserAvatar: UploadUserAvatarUseCase get() = UploadUserAvatarUseCaseImpl(userRepository, assetRepository)
val searchUsers: SearchUsersUseCase get() = SearchUsersUseCaseImpl(userRepository, searchUserRepository)
val searchKnownUsers: SearchKnownUsersUseCase get() = SearchKnownUsersUseCaseImpl(searchUserRepository)
val searchKnownUsers: SearchKnownUsersUseCase get() = SearchKnownUsersUseCaseImpl(searchUserRepository,userRepository)
val getPublicAsset: GetAvatarAssetUseCase get() = GetAvatarAssetUseCaseImpl(assetRepository)
val searchUserDirectory: SearchUserDirectoryUseCase get() = SearchUserDirectoryUseCaseImpl(searchUserRepository)
val setUserHandle: SetUserHandleUseCase get() = SetUserHandleUseCase(userRepository, validateUserHandleUseCase, syncManager)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ class SearchUserRepositoryTest {
private companion object {
const val TEST_QUERY = "testQuery"
const val TEST_DOMAIN = "testDomain"

val CONTACTS = buildList {
for (i in 1..5) {
add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,50 @@ package com.wire.kalium.logic.feature.user
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.publicuser.SearchUserRepository
import com.wire.kalium.logic.data.publicuser.model.OtherUser
import com.wire.kalium.logic.data.publicuser.model.UserSearchResult
import com.wire.kalium.logic.data.user.ConnectionState
import com.wire.kalium.logic.data.user.UserAvailabilityStatus
import com.wire.kalium.logic.data.user.type.UserType
import com.wire.kalium.logic.feature.publicuser.SearchKnownUsersUseCase
import com.wire.kalium.logic.feature.publicuser.SearchKnownUsersUseCaseImpl
import io.mockative.Mock
import io.mockative.any
import io.mockative.anything
import io.mockative.classOf
import io.mockative.eq
import io.mockative.given
import io.mockative.mock
import io.mockative.verify
import kotlinx.coroutines.test.runTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertIs
import com.wire.kalium.logic.data.publicuser.model.UserSearchResult
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.feature.publicuser.Result
import com.wire.kalium.logic.framework.TestUser
import kotlinx.coroutines.flow.flowOf
import kotlin.test.assertFalse

class SearchKnownUserUseCaseTest {

@Mock
private val searchUserRepository = mock(classOf<SearchUserRepository>())

private lateinit var searchKnownUsersUseCase: SearchKnownUsersUseCase

@BeforeTest
fun setUp() {
searchKnownUsersUseCase = SearchKnownUsersUseCaseImpl(searchUserRepository)
}

@Test
fun givenAnInputStartingWithAtSymbol_whenSearchingUsers_thenSearchOnlyByHandle() = runTest {
//given
val handleSearchQuery = "@someHandle"

given(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
.whenInvokedWith(eq(handleSearchQuery))
.thenReturn(
UserSearchResult(
listOf(
OtherUser(
id = QualifiedID(
value = "someValue",
domain = "someDomain",
),
name = null,
handle = null,
email = null,
phone = null,
accentId = 0,
team = null,
connectionStatus = ConnectionState.ACCEPTED,
previewPicture = null,
completePicture = null,
availabilityStatus = UserAvailabilityStatus.NONE,
userType = UserType.EXTERNAL
)
)
)
)
val (arrangement, searchKnownUsersUseCase) = Arrangement()
.withSuccessFullSelfUserRetrieve()
.withSearchByHandle(handleSearchQuery).arrange()

//when
searchKnownUsersUseCase(handleSearchQuery)
//then
verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
verify(arrangement.searchUserRepository)
.suspendFunction(arrangement.searchUserRepository::searchKnownUsersByHandle)
.with(eq(handleSearchQuery))
.wasInvoked()

verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
verify(arrangement.searchUserRepository)
.suspendFunction(arrangement.searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.with(anything())
.wasNotInvoked()
}
Expand All @@ -82,9 +56,119 @@ class SearchKnownUserUseCaseTest {
//given
val searchQuery = "someSearchQuery"

val (arrangement, searchKnownUsersUseCase) = Arrangement()
.withSuccessFullSelfUserRetrieve()
.withSearchKnownUsersByNameOrHandleOrEmail(searchQuery)
.arrange()
//when
searchKnownUsersUseCase(searchQuery)
//then
with(arrangement) {
verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
.with(anything())
.wasNotInvoked()

verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.with(eq(searchQuery))
.wasInvoked()
}
}

@Test
fun givenFederatedInput_whenSearchingUsers_thenSearchByNameOrHandleOrEmail() = runTest {
//given
val searchQuery = "someSearchQuery@wire.com"

val (arrangement, searchKnownUsersUseCase) = Arrangement()
.withSuccessFullSelfUserRetrieve()
.withSearchKnownUsersByNameOrHandleOrEmail()
.arrange()
//when
searchKnownUsersUseCase(searchQuery)
//then
with(arrangement) {
verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
.with(anything())
.wasNotInvoked()

verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.with(anything())
.wasInvoked()
}
}

@Test
fun test() = runTest {
//given
val searchQuery = "someSearchQuery"

val selfUserId = QualifiedID(
value = "selfUser",
domain = "wire.com",
)

val otherUserContainingSelfUserId = OtherUser(
id = selfUserId,
name = null,
handle = null,
email = null,
phone = null,
accentId = 0,
team = null,
connectionStatus = ConnectionState.ACCEPTED,
previewPicture = null,
completePicture = null,
availabilityStatus = UserAvailabilityStatus.NONE,
userType = UserType.EXTERNAL
)

val (_, searchKnownUsersUseCase) = Arrangement()
.withSuccessFullSelfUserRetrieve(selfUserId)
.withSearchKnownUsersByNameOrHandleOrEmail(searchQuery, otherUserContainingSelfUserId)
.arrange()
//when
val result = searchKnownUsersUseCase(searchQuery)
//then
assertIs<Result.Success>(result)
assertFalse(result.userSearchResult.result.contains(otherUserContainingSelfUserId))
}

}

class Arrangement {

@Mock
val searchUserRepository = mock(classOf<SearchUserRepository>())

@Mock
val userRepository = mock(classOf<UserRepository>())

fun withSuccessFullSelfUserRetrieve(
id: QualifiedID = QualifiedID(
value = "selfUser",
domain = "wire.com",
)
): Arrangement {
val selfUser = TestUser.SELF.copy(id = id)

given(userRepository)
.suspendFunction(userRepository::getSelfUser)
.whenInvoked()
.thenReturn(
selfUser
)

return this
}

fun withSearchByHandle(searchQuery: String? = null): Arrangement {
given(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.whenInvokedWith(eq(searchQuery))
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
.whenInvokedWith(if (searchQuery == null) any() else eq(searchQuery))
.thenReturn(
UserSearchResult(
listOf(
Expand All @@ -108,63 +192,51 @@ class SearchKnownUserUseCaseTest {
)
)
)
//when
searchKnownUsersUseCase(searchQuery)
//then
verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
.with(anything())
.wasNotInvoked()

verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.with(eq(searchQuery))
.wasInvoked()
return this
}

@Test
fun givenFederatedInput_whenSearchingUsers_thenSearchByNameOrHandleOrEmail() = runTest {
//given
val searchQuery = "someSearchQuery@wire.com"
fun withSearchKnownUsersByNameOrHandleOrEmail(
searchQuery: String? = null,
extraOtherUser: OtherUser? = null
): Arrangement {
val otherUsers = listOf(
OtherUser(
id = QualifiedID(
value = "someSearchQuery",
domain = "wire.com",
),
name = null,
handle = null,
email = null,
phone = null,
accentId = 0,
team = null,
connectionStatus = ConnectionState.ACCEPTED,
previewPicture = null,
completePicture = null,
availabilityStatus = UserAvailabilityStatus.NONE,
userType = UserType.FEDERATED
),
)

if (extraOtherUser != null) {
otherUsers.plus(extraOtherUser)
}

given(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.whenInvokedWith(eq("someSearchQuery"))
.whenInvokedWith(if (searchQuery == null) any() else eq(searchQuery))
.thenReturn(
UserSearchResult(
listOf(
OtherUser(
id = QualifiedID(
value = "someSearchQuery",
domain = "wire.com",
),
name = null,
handle = null,
email = null,
phone = null,
accentId = 0,
team = null,
connectionStatus = ConnectionState.ACCEPTED,
previewPicture = null,
completePicture = null,
availabilityStatus = UserAvailabilityStatus.NONE,
userType = UserType.FEDERATED
)
)
otherUsers
)
)
//when
searchKnownUsersUseCase(searchQuery)
//then
verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByHandle)
.with(anything())
.wasNotInvoked()

verify(searchUserRepository)
.suspendFunction(searchUserRepository::searchKnownUsersByNameOrHandleOrEmail)
.with(eq("someSearchQuery"))
.wasInvoked()
return this
}

fun arrange(): Pair<Arrangement, SearchKnownUsersUseCase> {
return this to SearchKnownUsersUseCaseImpl(searchUserRepository, userRepository)
}
}

0 comments on commit 50b2ffc

Please sign in to comment.