forked from dsutanto/bChot-android
First Commit
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2022-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.matrix.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.libraries.core)
|
||||
api(projects.libraries.matrix.api)
|
||||
api(libs.coroutines.core)
|
||||
implementation(libs.coroutines.test)
|
||||
implementation(projects.libraries.matrix.impl)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(projects.tests.testutils)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2025 Element Creations Ltd.
|
||||
~ Copyright 2022 New Vector Ltd.
|
||||
~
|
||||
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
~ Please see LICENSE files in the repository root for full details.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
</manifest>
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewService
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.NotJoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||
import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaPreviewService
|
||||
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.pushers.FakePushersService
|
||||
import io.element.android.libraries.matrix.test.roomdirectory.FakeRoomDirectoryService
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceService
|
||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import java.util.Optional
|
||||
|
||||
class FakeMatrixClient(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val deviceId: DeviceId = A_DEVICE_ID,
|
||||
override val sessionCoroutineScope: CoroutineScope = TestScope(),
|
||||
private val userDisplayName: String? = A_USER_NAME,
|
||||
private val userAvatarUrl: String? = AN_AVATAR_URL,
|
||||
override val roomListService: RoomListService = FakeRoomListService(),
|
||||
override val spaceService: SpaceService = FakeSpaceService(),
|
||||
override val matrixMediaLoader: MatrixMediaLoader = FakeMatrixMediaLoader(),
|
||||
override val sessionVerificationService: SessionVerificationService = FakeSessionVerificationService(),
|
||||
override val pushersService: PushersService = FakePushersService(),
|
||||
override val notificationService: NotificationService = FakeNotificationService(),
|
||||
override val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
|
||||
override val syncService: SyncService = FakeSyncService(),
|
||||
override val encryptionService: EncryptionService = FakeEncryptionService(),
|
||||
override val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(),
|
||||
override val mediaPreviewService: MediaPreviewService = FakeMediaPreviewService(),
|
||||
override val roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(),
|
||||
private val accountManagementUrlResult: (AccountManagementAction?) -> Result<String?> = { lambdaError() },
|
||||
private val resolveRoomAliasResult: (RoomAlias) -> Result<Optional<ResolvedRoomAlias>> = {
|
||||
Result.success(
|
||||
Optional.of(ResolvedRoomAlias(A_ROOM_ID, emptyList()))
|
||||
)
|
||||
},
|
||||
private val getNotJoinedRoomResult: (RoomIdOrAlias, List<String>) -> Result<NotJoinedRoom> = { _, _ -> lambdaError() },
|
||||
private val clearCacheLambda: () -> Unit = { lambdaError() },
|
||||
private val userIdServerNameLambda: () -> String = { lambdaError() },
|
||||
private val getUrlLambda: (String) -> Result<ByteArray> = { lambdaError() },
|
||||
private val canDeactivateAccountResult: () -> Boolean = { lambdaError() },
|
||||
private val deactivateAccountResult: (String, Boolean) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val currentSlidingSyncVersionLambda: () -> Result<SlidingSyncVersion> = { lambdaError() },
|
||||
private val ignoreUserResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private var unIgnoreUserResult: (UserId) -> Result<Unit> = { Result.success(Unit) },
|
||||
private val canReportRoomLambda: () -> Boolean = { false },
|
||||
private val isLivekitRtcSupportedLambda: () -> Boolean = { false },
|
||||
override val ignoredUsersFlow: StateFlow<ImmutableList<UserId>> = MutableStateFlow(persistentListOf()),
|
||||
private val getMaxUploadSizeResult: () -> Result<Long> = { lambdaError() },
|
||||
private val getJoinedRoomIdsResult: () -> Result<Set<RoomId>> = { Result.success(emptySet()) },
|
||||
private val getRecentEmojisLambda: () -> Result<List<String>> = { Result.success(emptyList()) },
|
||||
private val addRecentEmojiLambda: (String) -> Result<Unit> = { Result.success(Unit) },
|
||||
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
) : MatrixClient {
|
||||
var setDisplayNameCalled: Boolean = false
|
||||
private set
|
||||
var uploadAvatarCalled: Boolean = false
|
||||
private set
|
||||
var removeAvatarCalled: Boolean = false
|
||||
private set
|
||||
|
||||
private val _userProfile: MutableStateFlow<MatrixUser> = MutableStateFlow(MatrixUser(sessionId, userDisplayName, userAvatarUrl))
|
||||
override val userProfile: StateFlow<MatrixUser> = _userProfile
|
||||
|
||||
private var createRoomResult: Result<RoomId> = Result.success(A_ROOM_ID)
|
||||
private var createDmResult: Result<RoomId> = Result.success(A_ROOM_ID)
|
||||
private var findDmResult: Result<RoomId?> = Result.success(A_ROOM_ID)
|
||||
private val getRoomResults = mutableMapOf<RoomId, BaseRoom>()
|
||||
private val searchUserResults = mutableMapOf<String, Result<MatrixSearchUserResults>>()
|
||||
private val getProfileResults = mutableMapOf<UserId, Result<MatrixUser>>()
|
||||
private var uploadMediaResult: Result<String> = Result.success(AN_AVATAR_URL)
|
||||
private var setDisplayNameResult: Result<Unit> = Result.success(Unit)
|
||||
private var uploadAvatarResult: Result<Unit> = Result.success(Unit)
|
||||
private var removeAvatarResult: Result<Unit> = Result.success(Unit)
|
||||
var joinRoomLambda: (RoomId) -> Result<RoomInfo?> = {
|
||||
Result.success(null)
|
||||
}
|
||||
var joinRoomByIdOrAliasLambda: (RoomIdOrAlias, List<String>) -> Result<RoomInfo?> = { _, _ ->
|
||||
Result.success(null)
|
||||
}
|
||||
var knockRoomLambda: (RoomIdOrAlias, String, List<String>) -> Result<RoomInfo?> = { _, _, _ ->
|
||||
Result.success(null)
|
||||
}
|
||||
var getRoomInfoFlowLambda = { _: RoomId ->
|
||||
flowOf<Optional<RoomInfo>>(Optional.empty())
|
||||
}
|
||||
var logoutLambda: (Boolean, Boolean) -> Unit = { _, _ -> }
|
||||
|
||||
override suspend fun getRoom(roomId: RoomId): BaseRoom? {
|
||||
return getRoomResults[roomId]
|
||||
}
|
||||
|
||||
override suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom? {
|
||||
return getRoomResults[roomId] as? JoinedRoom
|
||||
}
|
||||
|
||||
override suspend fun findDM(userId: UserId): Result<RoomId?> {
|
||||
return findDmResult
|
||||
}
|
||||
|
||||
override suspend fun getJoinedRoomIds(): Result<Set<RoomId>> {
|
||||
return getJoinedRoomIdsResult()
|
||||
}
|
||||
|
||||
override suspend fun ignoreUser(userId: UserId): Result<Unit> = simulateLongTask {
|
||||
return ignoreUserResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun unignoreUser(userId: UserId): Result<Unit> = simulateLongTask {
|
||||
return unIgnoreUserResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId> = simulateLongTask {
|
||||
return createRoomResult
|
||||
}
|
||||
|
||||
override suspend fun createDM(userId: UserId): Result<RoomId> = simulateLongTask {
|
||||
return createDmResult
|
||||
}
|
||||
|
||||
override suspend fun getProfile(userId: UserId): Result<MatrixUser> {
|
||||
return getProfileResults[userId] ?: Result.failure(IllegalStateException("No profile found for $userId"))
|
||||
}
|
||||
|
||||
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> {
|
||||
return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm"))
|
||||
}
|
||||
|
||||
override suspend fun getCacheSize(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override suspend fun clearCache() = simulateLongTask {
|
||||
clearCacheLambda()
|
||||
}
|
||||
|
||||
override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean) = simulateLongTask {
|
||||
logoutLambda(ignoreSdkError, userInitiated)
|
||||
}
|
||||
|
||||
override fun canDeactivateAccount() = canDeactivateAccountResult()
|
||||
|
||||
override suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit> = simulateLongTask {
|
||||
deactivateAccountResult(password, eraseData)
|
||||
}
|
||||
|
||||
override suspend fun getUserProfile(): Result<MatrixUser> = simulateLongTask {
|
||||
val result = getProfileResults[sessionId]?.getOrNull() ?: MatrixUser(sessionId, userDisplayName, userAvatarUrl)
|
||||
_userProfile.tryEmit(result)
|
||||
return Result.success(result)
|
||||
}
|
||||
|
||||
override suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?> = simulateLongTask {
|
||||
accountManagementUrlResult(action)
|
||||
}
|
||||
|
||||
override suspend fun uploadMedia(
|
||||
mimeType: String,
|
||||
data: ByteArray,
|
||||
): Result<String> {
|
||||
return uploadMediaResult
|
||||
}
|
||||
|
||||
override suspend fun setDisplayName(displayName: String): Result<Unit> = simulateLongTask {
|
||||
setDisplayNameCalled = true
|
||||
return setDisplayNameResult
|
||||
}
|
||||
|
||||
override suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result<Unit> = simulateLongTask {
|
||||
uploadAvatarCalled = true
|
||||
return uploadAvatarResult
|
||||
}
|
||||
|
||||
override suspend fun removeAvatar(): Result<Unit> = simulateLongTask {
|
||||
removeAvatarCalled = true
|
||||
return removeAvatarResult
|
||||
}
|
||||
|
||||
override suspend fun joinRoom(roomId: RoomId): Result<RoomInfo?> = joinRoomLambda(roomId)
|
||||
|
||||
override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<RoomInfo?> {
|
||||
return joinRoomByIdOrAliasLambda(roomIdOrAlias, serverNames)
|
||||
}
|
||||
|
||||
override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List<String>): Result<RoomInfo?> {
|
||||
return knockRoomLambda(roomIdOrAlias, message, serverNames)
|
||||
}
|
||||
|
||||
// Mocks
|
||||
|
||||
fun givenCreateRoomResult(result: Result<RoomId>) {
|
||||
createRoomResult = result
|
||||
}
|
||||
|
||||
fun givenCreateDmResult(result: Result<RoomId>) {
|
||||
createDmResult = result
|
||||
}
|
||||
|
||||
fun givenFindDmResult(result: Result<RoomId?>) {
|
||||
findDmResult = result
|
||||
}
|
||||
|
||||
fun givenGetRoomResult(roomId: RoomId, result: BaseRoom?) {
|
||||
if (result == null) {
|
||||
getRoomResults.remove(roomId)
|
||||
} else {
|
||||
getRoomResults[roomId] = result
|
||||
}
|
||||
}
|
||||
|
||||
fun givenSearchUsersResult(searchTerm: String, result: Result<MatrixSearchUserResults>) {
|
||||
searchUserResults[searchTerm] = result
|
||||
}
|
||||
|
||||
fun givenGetProfileResult(userId: UserId, result: Result<MatrixUser>) {
|
||||
getProfileResults[userId] = result
|
||||
}
|
||||
|
||||
fun givenUploadMediaResult(result: Result<String>) {
|
||||
uploadMediaResult = result
|
||||
}
|
||||
|
||||
fun givenSetDisplayNameResult(result: Result<Unit>) {
|
||||
setDisplayNameResult = result
|
||||
}
|
||||
|
||||
fun givenUploadAvatarResult(result: Result<Unit>) {
|
||||
uploadAvatarResult = result
|
||||
}
|
||||
|
||||
fun givenRemoveAvatarResult(result: Result<Unit>) {
|
||||
removeAvatarResult = result
|
||||
}
|
||||
|
||||
private val visitedRoomsId: MutableList<RoomId> = mutableListOf()
|
||||
|
||||
override suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result<Unit> {
|
||||
visitedRoomsId.removeAll { it == roomId }
|
||||
visitedRoomsId.add(0, roomId)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result<Optional<ResolvedRoomAlias>> = simulateLongTask {
|
||||
resolveRoomAliasResult(roomAlias)
|
||||
}
|
||||
|
||||
override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>): Result<NotJoinedRoom> = simulateLongTask {
|
||||
getNotJoinedRoomResult(roomIdOrAlias, serverNames)
|
||||
}
|
||||
|
||||
override suspend fun getRecentlyVisitedRooms(): Result<List<RoomId>> {
|
||||
return Result.success(visitedRoomsId)
|
||||
}
|
||||
|
||||
override fun getRoomInfoFlow(roomId: RoomId) = getRoomInfoFlowLambda(roomId)
|
||||
|
||||
var setAllSendQueuesEnabledLambda = lambdaRecorder(ensureNeverCalled = true) { _: Boolean ->
|
||||
// no-op
|
||||
}
|
||||
|
||||
override suspend fun setAllSendQueuesEnabled(enabled: Boolean) = setAllSendQueuesEnabledLambda(enabled)
|
||||
|
||||
var sendQueueDisabledFlow = emptyFlow<RoomId>()
|
||||
override fun sendQueueDisabledFlow(): Flow<RoomId> = sendQueueDisabledFlow
|
||||
|
||||
override fun userIdServerName(): String {
|
||||
return userIdServerNameLambda()
|
||||
}
|
||||
|
||||
override suspend fun getUrl(url: String): Result<ByteArray> {
|
||||
return getUrlLambda(url)
|
||||
}
|
||||
|
||||
override suspend fun currentSlidingSyncVersion(): Result<SlidingSyncVersion> {
|
||||
return currentSlidingSyncVersionLambda()
|
||||
}
|
||||
|
||||
override suspend fun canReportRoom(): Boolean {
|
||||
return canReportRoomLambda()
|
||||
}
|
||||
|
||||
override suspend fun isLivekitRtcSupported(): Boolean {
|
||||
return isLivekitRtcSupportedLambda()
|
||||
}
|
||||
|
||||
override suspend fun getMaxFileUploadSize(): Result<Long> {
|
||||
return getMaxUploadSizeResult()
|
||||
}
|
||||
|
||||
override suspend fun addRecentEmoji(emoji: String): Result<Unit> {
|
||||
return addRecentEmojiLambda(emoji)
|
||||
}
|
||||
|
||||
override suspend fun getRecentEmojis(): Result<List<String>> {
|
||||
return getRecentEmojisLambda()
|
||||
}
|
||||
|
||||
override suspend fun markRoomAsFullyRead(roomId: RoomId, eventId: EventId): Result<Unit> {
|
||||
return markRoomAsFullyReadResult(roomId, eventId)
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
class FakeMatrixClientProvider(
|
||||
var getClient: (SessionId) -> Result<MatrixClient> = { Result.success(FakeMatrixClient()) }
|
||||
) : MatrixClientProvider {
|
||||
override suspend fun getOrRestore(sessionId: SessionId): Result<MatrixClient> = getClient(sessionId)
|
||||
|
||||
override fun getOrNull(sessionId: SessionId): MatrixClient? = getClient(sessionId).getOrNull()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.SdkMetadata
|
||||
|
||||
class FakeSdkMetadata(override val sdkGitSha: String) : SdkMetadata
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test
|
||||
|
||||
import androidx.annotation.ColorInt
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
|
||||
const val A_USER_NAME = "alice"
|
||||
const val A_USER_NAME_2 = "Bob"
|
||||
const val A_PASSWORD = "password"
|
||||
const val A_PASSPHRASE = "passphrase"
|
||||
const val A_SECRET = "secret"
|
||||
const val AN_APPLICATION_NAME = "AppName"
|
||||
const val AN_APPLICATION_NAME_DESKTOP = "AppNameDesktop"
|
||||
|
||||
val A_USER_ID = UserId("@alice:server.org")
|
||||
val A_USER_ID_2 = UserId("@bob:server.org")
|
||||
val A_USER_ID_3 = UserId("@carol:server.org")
|
||||
val A_USER_ID_4 = UserId("@david:server.org")
|
||||
val A_USER_ID_5 = UserId("@eve:server.org")
|
||||
val A_USER_ID_6 = UserId("@justin:server.org")
|
||||
val A_USER_ID_7 = UserId("@mallory:server.org")
|
||||
val A_USER_ID_8 = UserId("@susie:server.org")
|
||||
val A_USER_ID_9 = UserId("@victor:server.org")
|
||||
val A_USER_ID_10 = UserId("@walter:server.org")
|
||||
val A_SESSION_ID: SessionId = A_USER_ID
|
||||
val A_SESSION_ID_2: SessionId = A_USER_ID_2
|
||||
val A_SPACE_ID = SpaceId("!aSpaceId:domain")
|
||||
val A_SPACE_ID_2 = SpaceId("!aSpaceId2:domain")
|
||||
val A_ROOM_ID = RoomId("!aRoomId:domain")
|
||||
val A_ROOM_ID_2 = RoomId("!aRoomId2:domain")
|
||||
val A_ROOM_ID_3 = RoomId("!aRoomId3:domain")
|
||||
val A_THREAD_ID = ThreadId("\$aThreadId")
|
||||
val A_THREAD_ID_2 = ThreadId("\$aThreadId2")
|
||||
val AN_EVENT_ID = EventId("\$anEventId")
|
||||
val AN_EVENT_ID_2 = EventId("\$anEventId2")
|
||||
val AN_EVENT_ID_3 = EventId("\$anEventId3")
|
||||
val A_ROOM_ALIAS = RoomAlias("#alias1:domain")
|
||||
val A_TRANSACTION_ID = TransactionId("aTransactionId")
|
||||
val A_DEVICE_ID = DeviceId("ILAKNDNASDLK")
|
||||
|
||||
val A_UNIQUE_ID = UniqueId("aUniqueId")
|
||||
val A_UNIQUE_ID_2 = UniqueId("aUniqueId2")
|
||||
|
||||
const val A_ROOM_NAME = "A room name"
|
||||
const val A_ROOM_TOPIC = "A room topic"
|
||||
const val A_ROOM_RAW_NAME = "A room raw name"
|
||||
const val A_MESSAGE = "Hello world!"
|
||||
const val A_REPLY = "OK, I'll be there!"
|
||||
const val ANOTHER_MESSAGE = "Hello universe!"
|
||||
const val A_CAPTION = "A media caption"
|
||||
const val A_REASON = "A reason"
|
||||
|
||||
const val A_SPACE_NAME = "A space name"
|
||||
|
||||
const val A_REDACTION_REASON = "A redaction reason"
|
||||
|
||||
const val A_HOMESERVER_URL = "matrix.org"
|
||||
const val A_HOMESERVER_URL_2 = "matrix-client.org"
|
||||
|
||||
const val AN_ACCOUNT_PROVIDER_URL = "https://account.provider.org"
|
||||
const val AN_ACCOUNT_PROVIDER = "matrix.org"
|
||||
const val AN_ACCOUNT_PROVIDER_2 = "element.io"
|
||||
const val AN_ACCOUNT_PROVIDER_3 = "other.io"
|
||||
|
||||
val A_ROOM_NOTIFICATION_MODE = RoomNotificationMode.MUTE
|
||||
|
||||
const val AN_AVATAR_URL = "mxc://data"
|
||||
|
||||
const val A_FAILURE_REASON = "There has been a failure"
|
||||
|
||||
@Suppress("unused")
|
||||
val A_THROWABLE = Throwable(A_FAILURE_REASON)
|
||||
val AN_EXCEPTION = Exception(A_FAILURE_REASON)
|
||||
|
||||
const val A_RECOVERY_KEY = "1234 5678"
|
||||
|
||||
val A_SERVER_LIST = listOf("server1", "server2")
|
||||
|
||||
const val A_TIMESTAMP = 567L
|
||||
const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM"
|
||||
|
||||
const val A_LOGIN_HINT = "mxid:@alice:example.org"
|
||||
|
||||
@ColorInt
|
||||
const val A_COLOR_INT: Int = 0xFFFF0000.toInt()
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker
|
||||
|
||||
class FakeHomeServerLoginCompatibilityChecker(
|
||||
private val checkResult: (String) -> Result<Boolean>,
|
||||
) : HomeServerLoginCompatibilityChecker {
|
||||
override suspend fun check(url: String): Result<Boolean> {
|
||||
return checkResult(url)
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||
import io.element.android.libraries.matrix.api.auth.OidcDetails
|
||||
import io.element.android.libraries.matrix.api.auth.OidcPrompt
|
||||
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
val A_OIDC_DATA = OidcDetails(url = "a-url")
|
||||
|
||||
class FakeMatrixAuthenticationService(
|
||||
var matrixClientResult: ((SessionId) -> Result<MatrixClient>)? = null,
|
||||
var loginWithQrCodeResult: (qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) -> Result<SessionId> =
|
||||
lambdaRecorder<MatrixQrCodeLoginData, (QrCodeLoginStep) -> Unit, Result<SessionId>> { _, _ -> Result.success(A_SESSION_ID) },
|
||||
private val importCreatedSessionLambda: (ExternalSession) -> Result<SessionId> = { lambdaError() },
|
||||
private val setHomeserverResult: (String) -> Result<MatrixHomeServerDetails> = { lambdaError() },
|
||||
) : MatrixAuthenticationService {
|
||||
private var oidcError: Throwable? = null
|
||||
private var oidcCancelError: Throwable? = null
|
||||
private var loginError: Throwable? = null
|
||||
private var matrixClient: MatrixClient? = null
|
||||
private var onAuthenticationListener: ((MatrixClient) -> Unit)? = null
|
||||
|
||||
override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> {
|
||||
matrixClientResult?.let {
|
||||
return it.invoke(sessionId)
|
||||
}
|
||||
return if (matrixClient != null) {
|
||||
onAuthenticationListener?.invoke(matrixClient!!)
|
||||
Result.success(matrixClient!!)
|
||||
} else {
|
||||
Result.failure(IllegalStateException())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setHomeserver(homeserver: String): Result<MatrixHomeServerDetails> = simulateLongTask {
|
||||
setHomeserverResult(homeserver)
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String): Result<SessionId> = simulateLongTask {
|
||||
loginError?.let { Result.failure(it) } ?: run {
|
||||
onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient())
|
||||
Result.success(A_USER_ID)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun importCreatedSession(externalSession: ExternalSession): Result<SessionId> = simulateLongTask {
|
||||
return importCreatedSessionLambda(externalSession)
|
||||
}
|
||||
|
||||
override suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails> = simulateLongTask {
|
||||
oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA)
|
||||
}
|
||||
|
||||
override suspend fun cancelOidcLogin(): Result<Unit> {
|
||||
return oidcCancelError?.let { Result.failure(it) } ?: Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> = simulateLongTask {
|
||||
loginError?.let { Result.failure(it) } ?: run {
|
||||
onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient())
|
||||
Result.success(A_USER_ID)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit): Result<SessionId> = simulateLongTask {
|
||||
onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient())
|
||||
loginWithQrCodeResult(qrCodeData, progress)
|
||||
}
|
||||
|
||||
override fun listenToNewMatrixClients(lambda: (MatrixClient) -> Unit) {
|
||||
onAuthenticationListener = lambda
|
||||
}
|
||||
|
||||
fun givenOidcError(throwable: Throwable?) {
|
||||
oidcError = throwable
|
||||
}
|
||||
|
||||
fun givenOidcCancelError(throwable: Throwable?) {
|
||||
oidcCancelError = throwable
|
||||
}
|
||||
|
||||
fun givenLoginError(throwable: Throwable?) {
|
||||
loginError = throwable
|
||||
}
|
||||
|
||||
fun givenMatrixClient(matrixClient: MatrixClient) {
|
||||
this.matrixClient = matrixClient
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider
|
||||
|
||||
const val FAKE_REDIRECT_URL = "io.element.android:/"
|
||||
|
||||
class FakeOidcRedirectUrlProvider(
|
||||
private val provideResult: String = FAKE_REDIRECT_URL,
|
||||
) : OidcRedirectUrlProvider {
|
||||
override fun provide() = provideResult
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
|
||||
fun aMatrixHomeServerDetails(
|
||||
url: String = A_HOMESERVER_URL,
|
||||
supportsPasswordLogin: Boolean = false,
|
||||
supportsOidcLogin: Boolean = false,
|
||||
) = MatrixHomeServerDetails(
|
||||
url = url,
|
||||
supportsPasswordLogin = supportsPasswordLogin,
|
||||
supportsOidcLogin = supportsOidcLogin,
|
||||
)
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.auth.qrlogin
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
||||
class FakeMatrixQrCodeLoginDataFactory(
|
||||
var parseQrCodeLoginDataResult: () -> Result<MatrixQrCodeLoginData> =
|
||||
lambdaRecorder<Result<MatrixQrCodeLoginData>> { Result.success(FakeMatrixQrCodeLoginData()) },
|
||||
) : MatrixQrCodeLoginDataFactory {
|
||||
override fun parseQrCodeData(data: ByteArray): Result<MatrixQrCodeLoginData> {
|
||||
return parseQrCodeLoginDataResult()
|
||||
}
|
||||
}
|
||||
|
||||
class FakeMatrixQrCodeLoginData(
|
||||
private val serverNameResult: () -> String? = { lambdaError() },
|
||||
) : MatrixQrCodeLoginData {
|
||||
override fun serverName() = serverNameResult()
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.core
|
||||
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
|
||||
fun aBuildMeta(
|
||||
buildType: BuildType = BuildType.DEBUG,
|
||||
isDebuggable: Boolean = true,
|
||||
applicationName: String = "",
|
||||
productionApplicationName: String = applicationName,
|
||||
desktopApplicationName: String = applicationName,
|
||||
applicationId: String = "",
|
||||
isEnterpriseBuild: Boolean = false,
|
||||
lowPrivacyLoggingEnabled: Boolean = true,
|
||||
versionName: String = "",
|
||||
versionCode: Long = 0,
|
||||
gitRevision: String = "",
|
||||
gitBranchName: String = "",
|
||||
flavorDescription: String = "",
|
||||
flavorShortDescription: String = "",
|
||||
) = BuildMeta(
|
||||
buildType = buildType,
|
||||
isDebuggable = isDebuggable,
|
||||
applicationName = applicationName,
|
||||
productionApplicationName = productionApplicationName,
|
||||
desktopApplicationName = desktopApplicationName,
|
||||
applicationId = applicationId,
|
||||
isEnterpriseBuild = isEnterpriseBuild,
|
||||
lowPrivacyLoggingEnabled = lowPrivacyLoggingEnabled,
|
||||
versionName = versionName,
|
||||
versionCode = versionCode,
|
||||
gitRevision = gitRevision,
|
||||
gitBranchName = gitBranchName,
|
||||
flavorDescription = flavorDescription,
|
||||
flavorShortDescription = flavorShortDescription,
|
||||
)
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.core
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.SendHandle
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeSendHandle(
|
||||
var retryLambda: () -> Result<Unit> = { Result.success(Unit) }
|
||||
) : SendHandle {
|
||||
override suspend fun retry(): Result<Unit> = simulateLongTask {
|
||||
return retryLambda()
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.encryption
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class FakeEncryptionService(
|
||||
var startIdentityResetLambda: () -> Result<IdentityResetHandle?> = { lambdaError() },
|
||||
private val pinUserIdentityResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private val withdrawVerificationResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private val getUserIdentityResult: (UserId) -> Result<IdentityState?> = { lambdaError() },
|
||||
private val enableRecoveryLambda: (Boolean) -> Result<Unit> = { lambdaError() },
|
||||
) : EncryptionService {
|
||||
private var disableRecoveryFailure: Exception? = null
|
||||
override val backupStateStateFlow: MutableStateFlow<BackupState> = MutableStateFlow(BackupState.UNKNOWN)
|
||||
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(RecoveryState.UNKNOWN)
|
||||
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
|
||||
override val isLastDevice: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
override val hasDevicesToVerifyAgainst: MutableStateFlow<AsyncData<Boolean>> = MutableStateFlow(AsyncData.Uninitialized)
|
||||
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
|
||||
|
||||
private var recoverFailure: Exception? = null
|
||||
private var doesBackupExistOnServerResult: Result<Boolean> = Result.success(true)
|
||||
|
||||
private var enableBackupsFailure: Exception? = null
|
||||
|
||||
private var curve25519: String? = null
|
||||
private var ed25519: String? = null
|
||||
|
||||
fun givenEnableBackupsFailure(exception: Exception?) {
|
||||
enableBackupsFailure = exception
|
||||
}
|
||||
|
||||
override suspend fun enableBackups(): Result<Unit> = simulateLongTask {
|
||||
enableBackupsFailure?.let { return Result.failure(it) }
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
fun givenDisableRecoveryFailure(exception: Exception) {
|
||||
disableRecoveryFailure = exception
|
||||
}
|
||||
|
||||
fun givenRecoverFailure(exception: Exception?) {
|
||||
recoverFailure = exception
|
||||
}
|
||||
|
||||
override suspend fun disableRecovery(): Result<Unit> = simulateLongTask {
|
||||
disableRecoveryFailure?.let { return Result.failure(it) }
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
fun givenDoesBackupExistOnServerResult(result: Result<Boolean>) {
|
||||
doesBackupExistOnServerResult = result
|
||||
}
|
||||
|
||||
override suspend fun doesBackupExistOnServer(): Result<Boolean> = simulateLongTask {
|
||||
return doesBackupExistOnServerResult
|
||||
}
|
||||
|
||||
override suspend fun recover(recoveryKey: String): Result<Unit> = simulateLongTask {
|
||||
recoverFailure?.let { return Result.failure(it) }
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
fun emitIsLastDevice(isLastDevice: Boolean) {
|
||||
this.isLastDevice.value = isLastDevice
|
||||
}
|
||||
|
||||
fun emitHasDevicesToVerifyAgainst(hasDevicesToVerifyAgainst: AsyncData<Boolean>) {
|
||||
this.hasDevicesToVerifyAgainst.value = hasDevicesToVerifyAgainst
|
||||
}
|
||||
|
||||
override suspend fun resetRecoveryKey(): Result<String> = simulateLongTask {
|
||||
return Result.success(FAKE_RECOVERY_KEY)
|
||||
}
|
||||
|
||||
override suspend fun enableRecovery(waitForBackupsToUpload: Boolean): Result<Unit> = simulateLongTask {
|
||||
return enableRecoveryLambda(waitForBackupsToUpload)
|
||||
}
|
||||
|
||||
fun givenWaitForBackupUploadSteadyStateFlow(flow: Flow<BackupUploadState>) {
|
||||
waitForBackupUploadSteadyStateFlow = flow
|
||||
}
|
||||
|
||||
override fun waitForBackupUploadSteadyState(): Flow<BackupUploadState> {
|
||||
return waitForBackupUploadSteadyStateFlow
|
||||
}
|
||||
|
||||
fun givenDeviceKeys(curve25519: String?, ed25519: String?) {
|
||||
this.curve25519 = curve25519
|
||||
this.ed25519 = ed25519
|
||||
}
|
||||
|
||||
override suspend fun deviceCurve25519(): String? = curve25519
|
||||
|
||||
override suspend fun deviceEd25519(): String? = ed25519
|
||||
|
||||
suspend fun emitBackupState(state: BackupState) {
|
||||
backupStateStateFlow.emit(state)
|
||||
}
|
||||
|
||||
suspend fun emitRecoveryState(state: RecoveryState) {
|
||||
recoveryStateStateFlow.emit(state)
|
||||
}
|
||||
|
||||
suspend fun emitEnableRecoveryProgress(state: EnableRecoveryProgress) {
|
||||
enableRecoveryProgressStateFlow.emit(state)
|
||||
}
|
||||
|
||||
override suspend fun startIdentityReset(): Result<IdentityResetHandle?> {
|
||||
return startIdentityResetLambda()
|
||||
}
|
||||
|
||||
override suspend fun pinUserIdentity(userId: UserId): Result<Unit> {
|
||||
return pinUserIdentityResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun withdrawVerification(userId: UserId): Result<Unit> {
|
||||
return withdrawVerificationResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun getUserIdentity(userId: UserId, fallbackToServer: Boolean): Result<IdentityState?> = simulateLongTask {
|
||||
return getUserIdentityResult(userId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FAKE_RECOVERY_KEY = "fake"
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.encryption
|
||||
|
||||
import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle
|
||||
import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle
|
||||
|
||||
class FakeIdentityOidcResetHandle(
|
||||
override val url: String = "",
|
||||
var resetOidcLambda: () -> Result<Unit> = { error("Not implemented") },
|
||||
var cancelLambda: () -> Unit = { error("Not implemented") },
|
||||
) : IdentityOidcResetHandle {
|
||||
override suspend fun resetOidc(): Result<Unit> {
|
||||
return resetOidcLambda()
|
||||
}
|
||||
|
||||
override suspend fun cancel() {
|
||||
cancelLambda()
|
||||
}
|
||||
}
|
||||
|
||||
class FakeIdentityPasswordResetHandle(
|
||||
var resetPasswordLambda: (String) -> Result<Unit> = { _ -> error("Not implemented") },
|
||||
var cancelLambda: () -> Unit = { error("Not implemented") },
|
||||
) : IdentityPasswordResetHandle {
|
||||
override suspend fun resetPassword(password: String): Result<Unit> {
|
||||
return resetPasswordLambda(password)
|
||||
}
|
||||
|
||||
override suspend fun cancel() {
|
||||
cancelLambda()
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeMatrixMediaLoader : MatrixMediaLoader {
|
||||
var shouldFail = false
|
||||
var path: String = ""
|
||||
|
||||
override suspend fun loadMediaContent(source: MediaSource): Result<ByteArray> = simulateLongTask {
|
||||
if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
Result.success(ByteArray(0))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadMediaThumbnail(source: MediaSource, width: Long, height: Long): Result<ByteArray> = simulateLongTask {
|
||||
if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
Result.success(ByteArray(0))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun downloadMediaFile(
|
||||
source: MediaSource,
|
||||
mimeType: String?,
|
||||
filename: String?,
|
||||
useCache: Boolean,
|
||||
): Result<MediaFile> = simulateLongTask {
|
||||
if (shouldFail) {
|
||||
Result.failure(RuntimeException())
|
||||
} else {
|
||||
Result.success(FakeMediaFile(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaFile
|
||||
import java.io.File
|
||||
|
||||
class FakeMediaFile(private val path: String) : MediaFile {
|
||||
override fun path(): String {
|
||||
return path
|
||||
}
|
||||
|
||||
override fun persist(path: String): Boolean {
|
||||
return File(path()).renameTo(File(path))
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewConfig
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewService
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class FakeMediaPreviewService(
|
||||
override val mediaPreviewConfigFlow: StateFlow<MediaPreviewConfig> = MutableStateFlow(MediaPreviewConfig.DEFAULT),
|
||||
private val fetchMediaPreviewConfigResult: () -> Result<MediaPreviewConfig?> = { lambdaError() },
|
||||
private val setMediaPreviewValueResult: (MediaPreviewValue) -> Result<Unit> = { lambdaError() },
|
||||
private val setHideInviteAvatarsResult: (Boolean) -> Result<Unit> = { lambdaError() },
|
||||
) : MediaPreviewService {
|
||||
override suspend fun fetchMediaPreviewConfig(): Result<MediaPreviewConfig?> = simulateLongTask {
|
||||
fetchMediaPreviewConfigResult()
|
||||
}
|
||||
|
||||
override suspend fun setMediaPreviewValue(mediaPreviewValue: MediaPreviewValue): Result<Unit> = simulateLongTask {
|
||||
setMediaPreviewValueResult(mediaPreviewValue)
|
||||
}
|
||||
|
||||
override suspend fun setHideInviteAvatars(hide: Boolean): Result<Unit> = simulateLongTask {
|
||||
setHideInviteAvatarsResult(hide)
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
class FakeMediaUploadHandler(
|
||||
private var result: Result<Unit> = Result.success(Unit),
|
||||
) : MediaUploadHandler {
|
||||
override suspend fun await(): Result<Unit> = simulateLongTask { result }
|
||||
|
||||
override fun cancel() {
|
||||
result = Result.failure(CancellationException())
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.media
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
|
||||
fun aMediaSource(url: String = "") = MediaSource(
|
||||
url = url,
|
||||
json = null
|
||||
)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.mxc
|
||||
|
||||
import io.element.android.libraries.matrix.api.mxc.MxcTools
|
||||
import io.element.android.libraries.matrix.impl.mxc.DefaultMxcTools
|
||||
|
||||
class FakeMxcTools(
|
||||
private val delegate: MxcTools = DefaultMxcTools()
|
||||
) : MxcTools by delegate
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.notification
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
|
||||
class FakeNotificationService : NotificationService {
|
||||
private var getNotificationsResult: Result<Map<EventId, Result<NotificationData>>> = Result.success(emptyMap())
|
||||
|
||||
fun givenGetNotificationsResult(result: Result<Map<EventId, Result<NotificationData>>>) {
|
||||
getNotificationsResult = result
|
||||
}
|
||||
|
||||
override suspend fun getNotifications(ids: Map<RoomId, List<EventId>>): Result<Map<EventId, Result<NotificationData>>> {
|
||||
return getNotificationsResult
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.notification
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_TIMESTAMP
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME_2
|
||||
|
||||
fun aNotificationData(
|
||||
content: NotificationContent = NotificationContent.MessageLike.RoomEncrypted,
|
||||
isDirect: Boolean = false,
|
||||
hasMention: Boolean = false,
|
||||
threadId: ThreadId? = null,
|
||||
timestamp: Long = A_TIMESTAMP,
|
||||
senderDisplayName: String? = A_USER_NAME_2,
|
||||
senderIsNameAmbiguous: Boolean = false,
|
||||
roomDisplayName: String? = A_ROOM_NAME
|
||||
): NotificationData {
|
||||
return NotificationData(
|
||||
sessionId = A_SESSION_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
threadId = threadId,
|
||||
roomId = A_ROOM_ID,
|
||||
senderAvatarUrl = null,
|
||||
senderDisplayName = senderDisplayName,
|
||||
senderIsNameAmbiguous = senderIsNameAmbiguous,
|
||||
roomAvatarUrl = null,
|
||||
roomDisplayName = roomDisplayName,
|
||||
isDirect = isDirect,
|
||||
isDm = false,
|
||||
isEncrypted = false,
|
||||
isNoisy = false,
|
||||
timestamp = timestamp,
|
||||
content = content,
|
||||
hasMention = hasMention,
|
||||
)
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.notificationsettings
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_MODE
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
class FakeNotificationSettingsService(
|
||||
initialRoomMode: RoomNotificationMode = A_ROOM_NOTIFICATION_MODE,
|
||||
initialRoomModeIsDefault: Boolean = true,
|
||||
initialGroupDefaultMode: RoomNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
initialEncryptedGroupDefaultMode: RoomNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
initialOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES,
|
||||
initialEncryptedOneToOneDefaultMode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES,
|
||||
private val getRawPushRulesResult: () -> Result<String> = { lambdaError() },
|
||||
private val getRoomsWithUserDefinedRulesResult: () -> Result<List<RoomId>> = { lambdaError() },
|
||||
) : NotificationSettingsService {
|
||||
private val notificationSettingsStateFlow = MutableStateFlow(Unit)
|
||||
private var defaultGroupRoomNotificationMode: RoomNotificationMode = initialGroupDefaultMode
|
||||
private var defaultEncryptedGroupRoomNotificationMode: RoomNotificationMode = initialEncryptedGroupDefaultMode
|
||||
private var defaultOneToOneRoomNotificationMode: RoomNotificationMode = initialOneToOneDefaultMode
|
||||
private var defaultEncryptedOneToOneRoomNotificationMode: RoomNotificationMode = initialEncryptedOneToOneDefaultMode
|
||||
private var roomNotificationMode: RoomNotificationMode = initialRoomMode
|
||||
private var roomNotificationModeIsDefault: Boolean = initialRoomModeIsDefault
|
||||
private var callNotificationsEnabled = false
|
||||
private var inviteNotificationsEnabled = false
|
||||
private var atRoomNotificationsEnabled = false
|
||||
private var setNotificationModeError: Throwable? = null
|
||||
private var restoreDefaultNotificationModeError: Throwable? = null
|
||||
private var setDefaultNotificationModeError: Throwable? = null
|
||||
private var setAtRoomError: Throwable? = null
|
||||
private var canHomeServerPushEncryptedEventsToDeviceResult = Result.success(true)
|
||||
override val notificationSettingsChangeFlow: SharedFlow<Unit>
|
||||
get() = notificationSettingsStateFlow
|
||||
|
||||
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> {
|
||||
return Result.success(
|
||||
RoomNotificationSettings(
|
||||
mode = if (roomNotificationModeIsDefault) defaultEncryptedGroupRoomNotificationMode else roomNotificationMode,
|
||||
isDefault = roomNotificationModeIsDefault
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode> {
|
||||
return if (isOneToOne) {
|
||||
if (isEncrypted) {
|
||||
Result.success(defaultEncryptedOneToOneRoomNotificationMode)
|
||||
} else {
|
||||
Result.success(defaultOneToOneRoomNotificationMode)
|
||||
}
|
||||
} else {
|
||||
if (isEncrypted) {
|
||||
Result.success(defaultEncryptedGroupRoomNotificationMode)
|
||||
} else {
|
||||
Result.success(defaultGroupRoomNotificationMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result<Unit> {
|
||||
val error = setDefaultNotificationModeError
|
||||
if (error != null) {
|
||||
return Result.failure(error)
|
||||
}
|
||||
if (isOneToOne) {
|
||||
if (isEncrypted) {
|
||||
defaultEncryptedOneToOneRoomNotificationMode = mode
|
||||
} else {
|
||||
defaultOneToOneRoomNotificationMode = mode
|
||||
}
|
||||
} else {
|
||||
if (isEncrypted) {
|
||||
defaultEncryptedGroupRoomNotificationMode = mode
|
||||
} else {
|
||||
defaultGroupRoomNotificationMode = mode
|
||||
}
|
||||
}
|
||||
notificationSettingsStateFlow.emit(Unit)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> {
|
||||
val error = setNotificationModeError
|
||||
return if (error != null) {
|
||||
Result.failure(error)
|
||||
} else {
|
||||
roomNotificationModeIsDefault = false
|
||||
roomNotificationMode = mode
|
||||
notificationSettingsStateFlow.emit(Unit)
|
||||
Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> {
|
||||
val error = restoreDefaultNotificationModeError
|
||||
if (error != null) {
|
||||
return Result.failure(error)
|
||||
}
|
||||
roomNotificationModeIsDefault = true
|
||||
roomNotificationMode = defaultEncryptedGroupRoomNotificationMode
|
||||
notificationSettingsStateFlow.emit(Unit)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun muteRoom(roomId: RoomId): Result<Unit> {
|
||||
return setRoomNotificationMode(roomId, RoomNotificationMode.MUTE)
|
||||
}
|
||||
|
||||
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<Unit> {
|
||||
return restoreDefaultRoomNotificationMode(roomId)
|
||||
}
|
||||
|
||||
override suspend fun isRoomMentionEnabled(): Result<Boolean> {
|
||||
return Result.success(atRoomNotificationsEnabled)
|
||||
}
|
||||
|
||||
override suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit> {
|
||||
val error = setAtRoomError
|
||||
if (error != null) {
|
||||
return Result.failure(error)
|
||||
}
|
||||
atRoomNotificationsEnabled = enabled
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun isCallEnabled(): Result<Boolean> {
|
||||
return Result.success(callNotificationsEnabled)
|
||||
}
|
||||
|
||||
override suspend fun setCallEnabled(enabled: Boolean): Result<Unit> {
|
||||
callNotificationsEnabled = enabled
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun isInviteForMeEnabled(): Result<Boolean> {
|
||||
return Result.success(inviteNotificationsEnabled)
|
||||
}
|
||||
|
||||
override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> {
|
||||
inviteNotificationsEnabled = enabled
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getRoomsWithUserDefinedRules(): Result<List<RoomId>> {
|
||||
return getRoomsWithUserDefinedRulesResult()
|
||||
}
|
||||
|
||||
override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result<Boolean> {
|
||||
return canHomeServerPushEncryptedEventsToDeviceResult
|
||||
}
|
||||
|
||||
fun givenSetNotificationModeError(throwable: Throwable?) {
|
||||
setNotificationModeError = throwable
|
||||
}
|
||||
|
||||
fun givenRestoreDefaultNotificationModeError(throwable: Throwable?) {
|
||||
restoreDefaultNotificationModeError = throwable
|
||||
}
|
||||
|
||||
fun givenSetAtRoomError(throwable: Throwable?) {
|
||||
setAtRoomError = throwable
|
||||
}
|
||||
|
||||
fun givenSetDefaultNotificationModeError(throwable: Throwable?) {
|
||||
setDefaultNotificationModeError = throwable
|
||||
}
|
||||
|
||||
fun givenCanHomeServerPushEncryptedEventsToDeviceResult(result: Result<Boolean>) {
|
||||
canHomeServerPushEncryptedEventsToDeviceResult = result
|
||||
}
|
||||
|
||||
override suspend fun getRawPushRules(): Result<String?> {
|
||||
return getRawPushRulesResult()
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.permalink
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePermalinkBuilder(
|
||||
private val permalinkForUserLambda: (UserId) -> Result<String> = { lambdaError() },
|
||||
private val permalinkForRoomAliasLambda: (RoomAlias) -> Result<String> = { lambdaError() },
|
||||
) : PermalinkBuilder {
|
||||
override fun permalinkForUser(userId: UserId): Result<String> {
|
||||
return permalinkForUserLambda(userId)
|
||||
}
|
||||
|
||||
override fun permalinkForRoomAlias(roomAlias: RoomAlias): Result<String> {
|
||||
return permalinkForRoomAliasLambda(roomAlias)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.permalink
|
||||
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePermalinkParser(
|
||||
private var result: (String) -> PermalinkData = { lambdaError() }
|
||||
) : PermalinkParser {
|
||||
fun givenResult(result: PermalinkData) {
|
||||
this.result = { result }
|
||||
}
|
||||
|
||||
override fun parse(uriString: String): PermalinkData {
|
||||
return result(uriString)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.pushers
|
||||
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
|
||||
import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePushersService(
|
||||
private val setHttpPusherResult: (SetHttpPusherData) -> Result<Unit> = { lambdaError() },
|
||||
private val unsetHttpPusherResult: (UnsetHttpPusherData) -> Result<Unit> = { lambdaError() },
|
||||
) : PushersService {
|
||||
override suspend fun setHttpPusher(setHttpPusherData: SetHttpPusherData) = setHttpPusherResult(setHttpPusherData)
|
||||
override suspend fun unsetHttpPusher(unsetHttpPusherData: UnsetHttpPusherData): Result<Unit> = unsetHttpPusherResult(unsetHttpPusherData)
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
|
||||
import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
||||
class FakeBaseRoom(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val roomId: RoomId = A_ROOM_ID,
|
||||
initialRoomInfo: RoomInfo = aRoomInfo(),
|
||||
override val roomCoroutineScope: CoroutineScope = TestScope(),
|
||||
private var roomPermalinkResult: () -> Result<String> = { lambdaError() },
|
||||
private var eventPermalinkResult: (EventId) -> Result<String> = { lambdaError() },
|
||||
private val userDisplayNameResult: (UserId) -> Result<String?> = { lambdaError() },
|
||||
private val userAvatarUrlResult: () -> Result<String?> = { lambdaError() },
|
||||
private val userRoleResult: () -> Result<RoomMember.Role> = { lambdaError() },
|
||||
private val getUpdatedMemberResult: (UserId) -> Result<RoomMember> = { lambdaError() },
|
||||
private val joinRoomResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val canInviteResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canKickResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canBanResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canRedactOwnResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canRedactOtherResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canSendStateResult: (UserId, StateEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
|
||||
private val canUserSendMessageResult: (UserId, MessageEventType) -> Result<Boolean> = { _, _ -> lambdaError() },
|
||||
private val canUserTriggerRoomNotificationResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canUserJoinCallResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val canUserPinUnpinResult: (UserId) -> Result<Boolean> = { lambdaError() },
|
||||
private val setIsFavoriteResult: (Boolean) -> Result<Unit> = { lambdaError() },
|
||||
private val markAsReadResult: (ReceiptType) -> Result<Unit> = { Result.success(Unit) },
|
||||
private val powerLevelsResult: () -> Result<RoomPowerLevelsValues> = { lambdaError() },
|
||||
private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() },
|
||||
private var updateMembersResult: () -> Unit = { lambdaError() },
|
||||
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
|
||||
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
|
||||
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
|
||||
private val clearComposerDraftLambda: () -> Result<Unit> = { Result.success(Unit) },
|
||||
private val subscribeToSyncLambda: () -> Unit = { lambdaError() },
|
||||
private val getRoomVisibilityResult: () -> Result<RoomVisibility> = { lambdaError() },
|
||||
private val forgetResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val reportRoomResult: (String?) -> Result<Unit> = { lambdaError() },
|
||||
private val predecessorRoomResult: () -> PredecessorRoom? = { null },
|
||||
private val threadRootIdForEventResult: (EventId) -> Result<ThreadId?> = { lambdaError() },
|
||||
) : BaseRoom {
|
||||
private val _roomInfoFlow: MutableStateFlow<RoomInfo> = MutableStateFlow(initialRoomInfo)
|
||||
override val roomInfoFlow: StateFlow<RoomInfo> = _roomInfoFlow
|
||||
|
||||
fun givenRoomInfo(roomInfo: RoomInfo) {
|
||||
_roomInfoFlow.tryEmit(roomInfo)
|
||||
}
|
||||
|
||||
private val declineCallFlowMap: MutableMap<EventId, MutableSharedFlow<UserId>> = mutableMapOf()
|
||||
|
||||
suspend fun givenDecliner(userId: UserId, forNotificationEventId: EventId) {
|
||||
declineCallFlowMap[forNotificationEventId]?.emit(userId)
|
||||
}
|
||||
|
||||
override val membersStateFlow: MutableStateFlow<RoomMembersState> = MutableStateFlow(RoomMembersState.Unknown)
|
||||
|
||||
override suspend fun updateMembers() = updateMembersResult()
|
||||
|
||||
override suspend fun getUpdatedMember(userId: UserId): Result<RoomMember> {
|
||||
return getUpdatedMemberResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun getMembers(limit: Int): Result<List<RoomMember>> {
|
||||
return getMembersResult(limit)
|
||||
}
|
||||
|
||||
override suspend fun subscribeToSync() {
|
||||
subscribeToSyncLambda()
|
||||
}
|
||||
|
||||
override suspend fun powerLevels(): Result<RoomPowerLevelsValues> {
|
||||
return powerLevelsResult()
|
||||
}
|
||||
|
||||
private var isDestroyed = false
|
||||
|
||||
override fun destroy() {
|
||||
isDestroyed = true
|
||||
}
|
||||
|
||||
fun assertDestroyed() {
|
||||
check(isDestroyed) { "Room should be destroyed" }
|
||||
}
|
||||
|
||||
override suspend fun userDisplayName(userId: UserId): Result<String?> = simulateLongTask {
|
||||
userDisplayNameResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun userAvatarUrl(userId: UserId): Result<String?> = simulateLongTask {
|
||||
userAvatarUrlResult()
|
||||
}
|
||||
|
||||
override suspend fun userRole(userId: UserId): Result<RoomMember.Role> {
|
||||
return userRoleResult()
|
||||
}
|
||||
|
||||
override suspend fun getPermalink(): Result<String> {
|
||||
return roomPermalinkResult()
|
||||
}
|
||||
|
||||
override suspend fun getPermalinkFor(eventId: EventId): Result<String> {
|
||||
return eventPermalinkResult(eventId)
|
||||
}
|
||||
|
||||
override suspend fun getRoomVisibility(): Result<RoomVisibility> = simulateLongTask {
|
||||
getRoomVisibilityResult()
|
||||
}
|
||||
|
||||
override suspend fun leave(): Result<Unit> = simulateLongTask {
|
||||
return leaveRoomLambda()
|
||||
}
|
||||
|
||||
override suspend fun join(): Result<Unit> {
|
||||
return joinRoomResult()
|
||||
}
|
||||
|
||||
override suspend fun forget(): Result<Unit> {
|
||||
return forgetResult()
|
||||
}
|
||||
|
||||
override suspend fun canUserBan(userId: UserId): Result<Boolean> {
|
||||
return canBanResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserKick(userId: UserId): Result<Boolean> {
|
||||
return canKickResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserInvite(userId: UserId): Result<Boolean> {
|
||||
return canInviteResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> {
|
||||
return canRedactOwnResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> {
|
||||
return canRedactOtherResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> {
|
||||
return canSendStateResult(userId, type)
|
||||
}
|
||||
|
||||
override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> {
|
||||
return canUserSendMessageResult(userId, type)
|
||||
}
|
||||
|
||||
override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> {
|
||||
return canUserTriggerRoomNotificationResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserJoinCall(userId: UserId): Result<Boolean> {
|
||||
return canUserJoinCallResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> {
|
||||
return canUserPinUnpinResult(userId)
|
||||
}
|
||||
|
||||
override suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit> {
|
||||
return setIsFavoriteResult(isFavorite)
|
||||
}
|
||||
|
||||
override suspend fun markAsRead(receiptType: ReceiptType): Result<Unit> {
|
||||
return markAsReadResult(receiptType)
|
||||
}
|
||||
|
||||
var setUnreadFlagCalls = mutableListOf<Boolean>()
|
||||
private set
|
||||
|
||||
override suspend fun setUnreadFlag(isUnread: Boolean): Result<Unit> {
|
||||
setUnreadFlagCalls.add(isUnread)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun saveComposerDraft(
|
||||
composerDraft: ComposerDraft,
|
||||
threadRoot: ThreadId?
|
||||
) = saveComposerDraftLambda(composerDraft)
|
||||
|
||||
override suspend fun loadComposerDraft(threadRoot: ThreadId?) = loadComposerDraftLambda()
|
||||
|
||||
override suspend fun clearComposerDraft(threadRoot: ThreadId?) = clearComposerDraftLambda()
|
||||
|
||||
override suspend fun getUpdatedIsEncrypted(): Result<Boolean> = simulateLongTask {
|
||||
Result.success(info().isEncrypted.orFalse())
|
||||
}
|
||||
|
||||
fun givenRoomMembersState(state: RoomMembersState) {
|
||||
membersStateFlow.value = state
|
||||
}
|
||||
|
||||
override suspend fun clearEventCacheStorage(): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun reportRoom(reason: String?) = reportRoomResult(reason)
|
||||
|
||||
override suspend fun declineCall(notificationEventId: EventId): Result<Unit> {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun subscribeToCallDecline(notificationEventId: EventId): Flow<UserId> {
|
||||
val flow = declineCallFlowMap.getOrPut(notificationEventId, { MutableSharedFlow() })
|
||||
return flow
|
||||
}
|
||||
|
||||
override fun predecessorRoom(): PredecessorRoom? = predecessorRoomResult()
|
||||
|
||||
fun givenUpdateMembersResult(result: () -> Unit) {
|
||||
updateMembersResult = result
|
||||
}
|
||||
|
||||
override suspend fun threadRootIdForEvent(eventId: EventId): Result<ThreadId?> {
|
||||
return threadRootIdForEventResult(eventId)
|
||||
}
|
||||
}
|
||||
|
||||
fun defaultRoomPowerLevelValues() = RoomPowerLevelsValues(
|
||||
ban = 50,
|
||||
invite = 0,
|
||||
kick = 50,
|
||||
sendEvents = 0,
|
||||
redactEvents = 50,
|
||||
roomName = 100,
|
||||
roomAvatar = 100,
|
||||
roomTopic = 100,
|
||||
spaceChild = 100,
|
||||
)
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsState
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
||||
class FakeJoinedRoom(
|
||||
val baseRoom: FakeBaseRoom = FakeBaseRoom(),
|
||||
override val liveTimeline: Timeline = FakeTimeline(),
|
||||
override val roomCoroutineScope: CoroutineScope = TestScope(),
|
||||
override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0),
|
||||
override val roomTypingMembersFlow: Flow<List<UserId>> = MutableStateFlow(emptyList()),
|
||||
override val identityStateChangesFlow: Flow<List<IdentityStateChange>> = MutableStateFlow(emptyList()),
|
||||
override val roomNotificationSettingsStateFlow: StateFlow<RoomNotificationSettingsState> =
|
||||
MutableStateFlow(RoomNotificationSettingsState.Unknown),
|
||||
override val knockRequestsFlow: Flow<List<KnockRequest>> = MutableStateFlow(emptyList()),
|
||||
private val roomNotificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
private var createTimelineResult: (CreateTimelineParams) -> Result<Timeline> = { lambdaError() },
|
||||
private val editMessageLambda: (EventId, String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
|
||||
private val generateWidgetWebViewUrlResult: (MatrixWidgetSettings, String, String?, String?) -> Result<String> = { _, _, _, _ -> lambdaError() },
|
||||
private val getWidgetDriverResult: (MatrixWidgetSettings) -> Result<MatrixWidgetDriver> = { lambdaError() },
|
||||
private val typingNoticeResult: (Boolean) -> Result<Unit> = { lambdaError() },
|
||||
private val inviteUserResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private val setNameResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
private val setTopicResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
private val updateAvatarResult: (String, ByteArray) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val removeAvatarResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val updateUserRoleResult: (List<UserRoleChange>) -> Result<Unit> = { lambdaError() },
|
||||
private val updatePowerLevelsResult: (RoomPowerLevelsValues) -> Result<Unit> = { lambdaError() },
|
||||
private val resetPowerLevelsResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val reportContentResult: (EventId, String, UserId?) -> Result<Unit> = { _, _, _ -> lambdaError() },
|
||||
private val kickUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val banUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val unBanUserResult: (UserId, String?) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val ignoreDeviceTrustAndResendResult: (Map<UserId, List<DeviceId>>, SendHandle) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val withdrawVerificationAndResendResult: (List<UserId>, SendHandle) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val updateCanonicalAliasResult: (RoomAlias?, List<RoomAlias>) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val updateRoomVisibilityResult: (RoomVisibility) -> Result<Unit> = { lambdaError() },
|
||||
private val updateRoomHistoryVisibilityResult: (RoomHistoryVisibility) -> Result<Unit> = { lambdaError() },
|
||||
private val publishRoomAliasInRoomDirectoryResult: (RoomAlias) -> Result<Boolean> = { lambdaError() },
|
||||
private val removeRoomAliasFromRoomDirectoryResult: (RoomAlias) -> Result<Boolean> = { lambdaError() },
|
||||
private val enableEncryptionResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val updateJoinRuleResult: (JoinRule) -> Result<Unit> = { lambdaError() },
|
||||
private val setSendQueueEnabledResult: (Boolean) -> Unit = { _: Boolean -> },
|
||||
) : JoinedRoom, BaseRoom by baseRoom {
|
||||
fun givenRoomMembersState(state: RoomMembersState) {
|
||||
baseRoom.givenRoomMembersState(state)
|
||||
}
|
||||
|
||||
fun givenRoomInfo(roomInfo: RoomInfo) {
|
||||
baseRoom.givenRoomInfo(roomInfo)
|
||||
}
|
||||
|
||||
override suspend fun createTimeline(createTimelineParams: CreateTimelineParams): Result<Timeline> = simulateLongTask {
|
||||
createTimelineResult(createTimelineParams)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>
|
||||
): Result<Unit> = simulateLongTask {
|
||||
editMessageLambda(eventId, body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun typingNotice(isTyping: Boolean): Result<Unit> = simulateLongTask {
|
||||
typingNoticeResult(isTyping)
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> = simulateLongTask {
|
||||
inviteUserResult(id)
|
||||
}
|
||||
|
||||
override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit> = simulateLongTask {
|
||||
simulateSendMediaProgress(null)
|
||||
updateAvatarResult(mimeType, data)
|
||||
}
|
||||
|
||||
override suspend fun removeAvatar(): Result<Unit> = simulateLongTask {
|
||||
removeAvatarResult()
|
||||
}
|
||||
|
||||
override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask {
|
||||
val notificationSettings = roomNotificationSettingsService.getRoomNotificationSettings(roomId, info().isEncrypted.orFalse(), isOneToOne).getOrThrow()
|
||||
(roomNotificationSettingsStateFlow as MutableStateFlow).value = RoomNotificationSettingsState.Ready(notificationSettings)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun updateCanonicalAlias(canonicalAlias: RoomAlias?, alternativeAliases: List<RoomAlias>): Result<Unit> = simulateLongTask {
|
||||
updateCanonicalAliasResult(canonicalAlias, alternativeAliases)
|
||||
}
|
||||
|
||||
override suspend fun updateRoomVisibility(roomVisibility: RoomVisibility): Result<Unit> = simulateLongTask {
|
||||
updateRoomVisibilityResult(roomVisibility)
|
||||
}
|
||||
|
||||
override suspend fun updateHistoryVisibility(historyVisibility: RoomHistoryVisibility): Result<Unit> = simulateLongTask {
|
||||
updateRoomHistoryVisibilityResult(historyVisibility)
|
||||
}
|
||||
|
||||
override suspend fun publishRoomAliasInRoomDirectory(roomAlias: RoomAlias): Result<Boolean> = simulateLongTask {
|
||||
publishRoomAliasInRoomDirectoryResult(roomAlias)
|
||||
}
|
||||
|
||||
override suspend fun removeRoomAliasFromRoomDirectory(roomAlias: RoomAlias): Result<Boolean> = simulateLongTask {
|
||||
removeRoomAliasFromRoomDirectoryResult(roomAlias)
|
||||
}
|
||||
|
||||
override suspend fun enableEncryption(): Result<Unit> = simulateLongTask {
|
||||
enableEncryptionResult().onSuccess {
|
||||
baseRoom.givenRoomInfo(info().copy(isEncrypted = true))
|
||||
emitSyncUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit> = simulateLongTask {
|
||||
updateJoinRuleResult(joinRule)
|
||||
}
|
||||
|
||||
override suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit> = simulateLongTask {
|
||||
updateUserRoleResult(changes)
|
||||
}
|
||||
|
||||
override suspend fun updatePowerLevels(roomPowerLevelsValues: RoomPowerLevelsValues): Result<Unit> = simulateLongTask {
|
||||
updatePowerLevelsResult(roomPowerLevelsValues)
|
||||
}
|
||||
|
||||
override suspend fun resetPowerLevels(): Result<Unit> = simulateLongTask {
|
||||
resetPowerLevelsResult()
|
||||
}
|
||||
|
||||
override suspend fun setName(name: String): Result<Unit> = simulateLongTask {
|
||||
setNameResult(name)
|
||||
}
|
||||
|
||||
override suspend fun setTopic(topic: String): Result<Unit> = simulateLongTask {
|
||||
setTopicResult(topic)
|
||||
}
|
||||
|
||||
override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit> = simulateLongTask {
|
||||
reportContentResult(eventId, reason, blockUserId)
|
||||
}
|
||||
|
||||
override suspend fun kickUser(userId: UserId, reason: String?): Result<Unit> = simulateLongTask {
|
||||
kickUserResult(userId, reason)
|
||||
}
|
||||
|
||||
override suspend fun banUser(userId: UserId, reason: String?): Result<Unit> = simulateLongTask {
|
||||
banUserResult(userId, reason)
|
||||
}
|
||||
|
||||
override suspend fun unbanUser(userId: UserId, reason: String?): Result<Unit> = simulateLongTask {
|
||||
unBanUserResult(userId, reason)
|
||||
}
|
||||
|
||||
override suspend fun generateWidgetWebViewUrl(
|
||||
widgetSettings: MatrixWidgetSettings,
|
||||
clientId: String,
|
||||
languageTag: String?,
|
||||
theme: String?
|
||||
): Result<String> = simulateLongTask {
|
||||
generateWidgetWebViewUrlResult(widgetSettings, clientId, languageTag, theme)
|
||||
}
|
||||
|
||||
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> {
|
||||
return getWidgetDriverResult(widgetSettings)
|
||||
}
|
||||
|
||||
override suspend fun setSendQueueEnabled(enabled: Boolean) = simulateLongTask {
|
||||
setSendQueueEnabledResult(enabled)
|
||||
}
|
||||
|
||||
override suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle): Result<Unit> = simulateLongTask {
|
||||
ignoreDeviceTrustAndResendResult(devices, sendHandle)
|
||||
}
|
||||
|
||||
override suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle): Result<Unit> = simulateLongTask {
|
||||
withdrawVerificationAndResendResult(userIds, sendHandle)
|
||||
}
|
||||
|
||||
private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) {
|
||||
progressCallbackValues.forEach { (current, total) ->
|
||||
progressCallback?.onProgress(current, total)
|
||||
delay(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun emitSyncUpdate() {
|
||||
(syncUpdateFlow as MutableStateFlow).value = syncUpdateFlow.value + 1
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.NotJoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeNotJoinedRoom(
|
||||
override val localRoom: BaseRoom? = null,
|
||||
override val previewInfo: RoomPreviewInfo = aRoomPreviewInfo(),
|
||||
private val roomMembershipDetails: () -> Result<RoomMembershipDetails?> = { lambdaError() },
|
||||
) : NotJoinedRoom {
|
||||
override suspend fun membershipDetails(): Result<RoomMembershipDetails?> = simulateLongTask {
|
||||
roomMembershipDetails()
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.roomlist.LatestEventValue
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.timeline.aMessageContent
|
||||
import io.element.android.libraries.matrix.test.timeline.aProfileDetails
|
||||
|
||||
fun aRemoteLatestEvent(
|
||||
content: EventContent = aMessageContent(),
|
||||
timestamp: Long = 0L,
|
||||
isOwn: Boolean = false,
|
||||
senderId: UserId = A_USER_ID,
|
||||
senderProfile: ProfileDetails = aProfileDetails(),
|
||||
): LatestEventValue.Remote {
|
||||
return LatestEventValue.Remote(
|
||||
timestamp = timestamp,
|
||||
content = content,
|
||||
senderId = senderId,
|
||||
senderProfile = senderProfile,
|
||||
isOwn = isOwn,
|
||||
)
|
||||
}
|
||||
|
||||
fun aLocalLatestEvent(
|
||||
content: EventContent = aMessageContent(),
|
||||
timestamp: Long = 0L,
|
||||
isSending: Boolean = false,
|
||||
senderId: UserId = A_USER_ID,
|
||||
senderProfile: ProfileDetails = aProfileDetails(),
|
||||
): LatestEventValue.Local {
|
||||
return LatestEventValue.Local(
|
||||
timestamp = timestamp,
|
||||
content = content,
|
||||
senderId = senderId,
|
||||
senderProfile = senderProfile,
|
||||
isSending = isSending,
|
||||
)
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_RAW_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
fun aRoomInfo(
|
||||
id: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
rawName: String? = A_ROOM_RAW_NAME,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
isPublic: Boolean = true,
|
||||
isDirect: Boolean = false,
|
||||
isEncrypted: Boolean = false,
|
||||
joinRule: JoinRule? = JoinRule.Public,
|
||||
isSpace: Boolean = false,
|
||||
successorRoom: SuccessorRoom? = null,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 2,
|
||||
invitedMembersCount: Long = 1,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
roomPowerLevels: RoomPowerLevels? = RoomPowerLevels(
|
||||
values = defaultRoomPowerLevelValues(),
|
||||
users = persistentMapOf(),
|
||||
),
|
||||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreators: List<UserId> = emptyList(),
|
||||
isMarkedUnread: Boolean = false,
|
||||
numUnreadMessages: Long = 0,
|
||||
numUnreadNotifications: Long = 0,
|
||||
numUnreadMentions: Long = 0,
|
||||
historyVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Joined,
|
||||
roomVersion: String? = "11",
|
||||
privilegedCreatorRole: Boolean = false,
|
||||
) = RoomInfo(
|
||||
id = id,
|
||||
name = name,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isPublic = isPublic,
|
||||
isDirect = isDirect,
|
||||
isEncrypted = isEncrypted,
|
||||
joinRule = joinRule,
|
||||
isSpace = isSpace,
|
||||
successorRoom = successorRoom,
|
||||
isFavorite = isFavorite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases.toImmutableList(),
|
||||
currentUserMembership = currentUserMembership,
|
||||
inviter = inviter,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
roomPowerLevels = roomPowerLevels,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
|
||||
heroes = heroes.toImmutableList(),
|
||||
pinnedEventIds = pinnedEventIds.toImmutableList(),
|
||||
creators = roomCreators.toImmutableList(),
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
historyVisibility = historyVisibility,
|
||||
roomVersion = roomVersion,
|
||||
privilegedCreatorRole = privilegedCreatorRole,
|
||||
)
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
fun aRoomMember(
|
||||
userId: UserId = UserId("@alice:server.org"),
|
||||
displayName: String? = null,
|
||||
avatarUrl: String? = null,
|
||||
membership: RoomMembershipState = RoomMembershipState.JOIN,
|
||||
isNameAmbiguous: Boolean = false,
|
||||
powerLevel: Long = 0L,
|
||||
isIgnored: Boolean = false,
|
||||
role: RoomMember.Role = RoomMember.Role.User,
|
||||
membershipChangeReason: String? = null,
|
||||
) = RoomMember(
|
||||
userId = userId,
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
membership = membership,
|
||||
isNameAmbiguous = isNameAmbiguous,
|
||||
powerLevel = powerLevel,
|
||||
isIgnored = isIgnored,
|
||||
role = role,
|
||||
membershipChangeReason = membershipChangeReason,
|
||||
)
|
||||
|
||||
fun aRoomMemberList() = persistentListOf(
|
||||
anAlice(),
|
||||
aBob(),
|
||||
aRoomMember(UserId("@carol:server.org"), "Carol"),
|
||||
aRoomMember(UserId("@david:server.org"), "David"),
|
||||
aRoomMember(UserId("@eve:server.org"), "Eve"),
|
||||
aRoomMember(UserId("@justin:server.org"), "Justin"),
|
||||
aRoomMember(UserId("@mallory:server.org"), "Mallory"),
|
||||
aRoomMember(UserId("@susie:server.org"), "Susie"),
|
||||
aVictor(),
|
||||
aWalter(),
|
||||
)
|
||||
|
||||
fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice", role = RoomMember.Role.Admin)
|
||||
fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob", role = RoomMember.Role.Moderator)
|
||||
|
||||
fun aVictor() = aRoomMember(
|
||||
UserId("@victor:server.org"),
|
||||
"Victor",
|
||||
membership = RoomMembershipState.INVITE
|
||||
)
|
||||
|
||||
fun aWalter() = aRoomMember(
|
||||
UserId("@walter:server.org"),
|
||||
"Walter",
|
||||
membership = RoomMembershipState.INVITE
|
||||
)
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
fun aRoomPreview(
|
||||
localRoom: FakeBaseRoom? = null,
|
||||
info: RoomPreviewInfo = aRoomPreviewInfo(),
|
||||
roomMembershipDetails: () -> Result<RoomMembershipDetails?> = { lambdaError() },
|
||||
) = FakeNotJoinedRoom(
|
||||
localRoom = localRoom,
|
||||
previewInfo = info,
|
||||
roomMembershipDetails = roomMembershipDetails,
|
||||
)
|
||||
|
||||
fun aRoomPreviewInfo(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = AN_AVATAR_URL,
|
||||
joinRule: JoinRule = JoinRule.Public,
|
||||
isSpace: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
currentUserMembership: CurrentUserMembership? = null,
|
||||
numberOfJoinedMembers: Long = 1,
|
||||
isHistoryWorldReadable: Boolean = true,
|
||||
) = RoomPreviewInfo(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
joinRule = joinRule,
|
||||
canonicalAlias = canonicalAlias,
|
||||
numberOfJoinedMembers = numberOfJoinedMembers,
|
||||
roomType = if (isSpace) RoomType.Space else RoomType.Room,
|
||||
isHistoryWorldReadable = isHistoryWorldReadable,
|
||||
membership = currentUserMembership,
|
||||
)
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||
import io.element.android.libraries.matrix.api.roomlist.LatestEventValue
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_RAW_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_TOPIC
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
fun aRoomSummary(
|
||||
info: RoomInfo = aRoomInfo(),
|
||||
latestEventValue: LatestEventValue = aRemoteLatestEvent(),
|
||||
) = RoomSummary(
|
||||
info = info,
|
||||
latestEvent = latestEventValue,
|
||||
)
|
||||
|
||||
fun aRoomSummary(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String? = A_ROOM_NAME,
|
||||
rawName: String? = A_ROOM_RAW_NAME,
|
||||
topic: String? = A_ROOM_TOPIC,
|
||||
avatarUrl: String? = null,
|
||||
isPublic: Boolean = true,
|
||||
isDirect: Boolean = false,
|
||||
isEncrypted: Boolean = false,
|
||||
joinRule: JoinRule? = JoinRule.Public,
|
||||
isSpace: Boolean = false,
|
||||
successorRoom: SuccessorRoom? = null,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
invitedMembersCount: Long = 0,
|
||||
joinedMembersCount: Long = 1,
|
||||
highlightCount: Long = 0,
|
||||
notificationCount: Long = 0,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
roomPowerLevels: RoomPowerLevels = RoomPowerLevels(
|
||||
values = defaultRoomPowerLevelValues(),
|
||||
users = persistentMapOf(),
|
||||
),
|
||||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreators: List<UserId> = emptyList(),
|
||||
isMarkedUnread: Boolean = false,
|
||||
numUnreadMessages: Long = 0,
|
||||
numUnreadNotifications: Long = 0,
|
||||
numUnreadMentions: Long = 0,
|
||||
historyVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Joined,
|
||||
latestEvent: LatestEventValue = aRemoteLatestEvent(),
|
||||
roomVersion: String? = "11",
|
||||
privilegedCreatorRole: Boolean = false,
|
||||
) = RoomSummary(
|
||||
info = RoomInfo(
|
||||
id = roomId,
|
||||
name = name,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isPublic = isPublic,
|
||||
isDirect = isDirect,
|
||||
isEncrypted = isEncrypted,
|
||||
joinRule = joinRule,
|
||||
isSpace = isSpace,
|
||||
successorRoom = successorRoom,
|
||||
isFavorite = isFavorite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases.toImmutableList(),
|
||||
currentUserMembership = currentUserMembership,
|
||||
inviter = inviter,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
roomPowerLevels = roomPowerLevels,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
userDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(),
|
||||
heroes = heroes.toImmutableList(),
|
||||
pinnedEventIds = pinnedEventIds.toImmutableList(),
|
||||
creators = roomCreators.toImmutableList(),
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
historyVisibility = historyVisibility,
|
||||
roomVersion = roomVersion,
|
||||
privilegedCreatorRole = privilegedCreatorRole,
|
||||
),
|
||||
latestEvent = latestEvent,
|
||||
)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room.alias
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper
|
||||
|
||||
class FakeRoomAliasHelper(
|
||||
private val roomAliasNameFromRoomDisplayNameLambda: (String) -> String = { name ->
|
||||
name.trimStart().trimEnd().replace(" ", "_")
|
||||
},
|
||||
private val isRoomAliasValidLambda: (RoomAlias) -> Boolean = { true }
|
||||
) : RoomAliasHelper {
|
||||
override fun roomAliasNameFromRoomDisplayName(name: String): String {
|
||||
return roomAliasNameFromRoomDisplayNameLambda(name)
|
||||
}
|
||||
|
||||
override fun isRoomAliasValid(roomAlias: RoomAlias): Boolean {
|
||||
return isRoomAliasValidLambda(roomAlias)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room.join
|
||||
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeJoinRoom(
|
||||
var lambda: (RoomIdOrAlias, List<String>, JoinedRoom.Trigger) -> Result<Unit>
|
||||
) : JoinRoom {
|
||||
override suspend fun invoke(
|
||||
roomIdOrAlias: RoomIdOrAlias,
|
||||
serverNames: List<String>,
|
||||
trigger: JoinedRoom.Trigger,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
lambda(roomIdOrAlias, serverNames, trigger)
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.room.knock
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeKnockRequest(
|
||||
override val eventId: EventId = AN_EVENT_ID,
|
||||
override val userId: UserId = A_USER_ID,
|
||||
override val displayName: String? = A_USER_NAME,
|
||||
override val avatarUrl: String? = AN_AVATAR_URL,
|
||||
override val reason: String? = null,
|
||||
override val timestamp: Long? = null,
|
||||
override val isSeen: Boolean = false,
|
||||
val acceptLambda: () -> Result<Unit> = { lambdaError() },
|
||||
val declineLambda: (String?) -> Result<Unit> = { lambdaError() },
|
||||
val declineAndBanLambda: (String?) -> Result<Unit> = { lambdaError() },
|
||||
val markAsSeenLambda: () -> Result<Unit> = { lambdaError() },
|
||||
) : KnockRequest {
|
||||
override suspend fun accept(): Result<Unit> = simulateLongTask {
|
||||
acceptLambda()
|
||||
}
|
||||
|
||||
override suspend fun decline(reason: String?): Result<Unit> = simulateLongTask {
|
||||
declineLambda(reason)
|
||||
}
|
||||
|
||||
override suspend fun declineAndBan(reason: String?): Result<Unit> = simulateLongTask {
|
||||
declineAndBanLambda(reason)
|
||||
}
|
||||
|
||||
override suspend fun markAsSeen(): Result<Unit> = simulateLongTask {
|
||||
markAsSeenLambda()
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
||||
class FakeRoomDirectoryList(
|
||||
override val state: Flow<RoomDirectoryList.SearchResult> = emptyFlow(),
|
||||
val filterLambda: (String?, Int, String?) -> Result<Unit> = { _, _, _ -> Result.success(Unit) },
|
||||
val loadMoreLambda: () -> Result<Unit> = { Result.success(Unit) }
|
||||
) : RoomDirectoryList {
|
||||
override suspend fun filter(filter: String?, batchSize: Int, viaServerName: String?): Result<Unit> = filterLambda(filter, batchSize, viaServerName)
|
||||
|
||||
override suspend fun loadMore(): Result<Unit> = loadMoreLambda()
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
class FakeRoomDirectoryService(
|
||||
private val createRoomDirectoryListFactory: (CoroutineScope) -> RoomDirectoryList = { throw AssertionError("Configure a proper factory.") }
|
||||
) : RoomDirectoryService {
|
||||
override fun createRoomDirectoryList(scope: CoroutineScope) = createRoomDirectoryListFactory(scope)
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
|
||||
fun aRoomDescription(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String? = null,
|
||||
topic: String? = null,
|
||||
alias: RoomAlias? = null,
|
||||
avatarUrl: String? = null,
|
||||
joinRule: RoomDescription.JoinRule = RoomDescription.JoinRule.UNKNOWN,
|
||||
isWorldReadable: Boolean = true,
|
||||
joinedMembers: Long = 2L
|
||||
) = RoomDescription(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
topic = topic,
|
||||
alias = alias,
|
||||
avatarUrl = avatarUrl,
|
||||
joinRule = joinRule,
|
||||
isWorldReadable = isWorldReadable,
|
||||
numberOfMembers = joinedMembers
|
||||
)
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class FakeRoomListService(
|
||||
var subscribeToVisibleRoomsLambda: (List<RoomId>) -> Unit = {},
|
||||
) : RoomListService {
|
||||
private val allRoomSummariesFlow = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
private val allRoomsLoadingStateFlow = MutableStateFlow<RoomList.LoadingState>(RoomList.LoadingState.NotLoaded)
|
||||
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
|
||||
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
|
||||
|
||||
suspend fun postAllRooms(roomSummaries: List<RoomSummary>) {
|
||||
allRoomSummariesFlow.emit(roomSummaries)
|
||||
}
|
||||
|
||||
suspend fun postAllRoomsLoadingState(loadingState: RoomList.LoadingState) {
|
||||
allRoomsLoadingStateFlow.emit(loadingState)
|
||||
}
|
||||
|
||||
suspend fun postState(state: RoomListService.State) {
|
||||
roomListStateFlow.emit(state)
|
||||
}
|
||||
|
||||
suspend fun postSyncIndicator(value: RoomListService.SyncIndicator) {
|
||||
syncIndicatorStateFlow.emit(value)
|
||||
}
|
||||
|
||||
override fun createRoomList(
|
||||
pageSize: Int,
|
||||
initialFilter: RoomListFilter,
|
||||
source: RoomList.Source
|
||||
): DynamicRoomList {
|
||||
return when (source) {
|
||||
RoomList.Source.All -> allRooms
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun subscribeToVisibleRooms(roomIds: List<RoomId>) {
|
||||
subscribeToVisibleRoomsLambda(roomIds)
|
||||
}
|
||||
|
||||
override val allRooms = SimplePagedRoomList(
|
||||
allRoomSummariesFlow,
|
||||
allRoomsLoadingStateFlow,
|
||||
MutableStateFlow(RoomListFilter.all())
|
||||
)
|
||||
|
||||
override val state: StateFlow<RoomListService.State> = roomListStateFlow
|
||||
|
||||
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> = syncIndicatorStateFlow
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
|
||||
data class SimplePagedRoomList(
|
||||
override val summaries: MutableStateFlow<List<RoomSummary>>,
|
||||
override val loadingState: StateFlow<RoomList.LoadingState>,
|
||||
override val currentFilter: MutableStateFlow<RoomListFilter>
|
||||
) : DynamicRoomList {
|
||||
override val pageSize: Int = Int.MAX_VALUE
|
||||
override val loadedPages = MutableStateFlow(1)
|
||||
|
||||
override val filteredSummaries: SharedFlow<List<RoomSummary>> = summaries
|
||||
|
||||
override suspend fun loadMore() {
|
||||
// No-op
|
||||
loadedPages.getAndUpdate { it + 1 }
|
||||
}
|
||||
|
||||
override suspend fun reset() {
|
||||
loadedPages.emit(1)
|
||||
}
|
||||
|
||||
override suspend fun updateFilter(filter: RoomListFilter) {
|
||||
currentFilter.emit(filter)
|
||||
}
|
||||
|
||||
override suspend fun rebuildSummaries() {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.spaces
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
|
||||
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom
|
||||
import io.element.android.libraries.matrix.test.A_SPACE_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeLeaveSpaceHandle(
|
||||
override val id: RoomId = A_SPACE_ID,
|
||||
private val roomsResult: () -> Result<List<LeaveSpaceRoom>> = { lambdaError() },
|
||||
private val leaveResult: (List<RoomId>) -> Result<Unit> = { lambdaError() },
|
||||
private val closeResult: () -> Unit = { lambdaError() },
|
||||
) : LeaveSpaceHandle {
|
||||
override suspend fun rooms(): Result<List<LeaveSpaceRoom>> = simulateLongTask {
|
||||
roomsResult()
|
||||
}
|
||||
|
||||
override suspend fun leave(roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
leaveResult(roomIds)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
return closeResult()
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.spaces
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import java.util.Optional
|
||||
|
||||
class FakeSpaceRoomList(
|
||||
override val roomId: RoomId = A_ROOM_ID,
|
||||
initialSpaceFlowValue: SpaceRoom? = null,
|
||||
initialSpaceRoomsValue: List<SpaceRoom> = emptyList(),
|
||||
initialSpaceRoomList: SpaceRoomList.PaginationStatus = SpaceRoomList.PaginationStatus.Loading,
|
||||
private val paginateResult: () -> Result<Unit> = { lambdaError() },
|
||||
) : SpaceRoomList {
|
||||
private val currentSpaceMutableStateFlow: MutableStateFlow<Optional<SpaceRoom>> = MutableStateFlow(Optional.ofNullable(initialSpaceFlowValue))
|
||||
override val currentSpaceFlow: StateFlow<Optional<SpaceRoom>> = currentSpaceMutableStateFlow.asStateFlow()
|
||||
|
||||
fun emitCurrentSpace(value: SpaceRoom?) {
|
||||
currentSpaceMutableStateFlow.value = Optional.ofNullable(value)
|
||||
}
|
||||
|
||||
private val _spaceRoomsFlow: MutableStateFlow<List<SpaceRoom>> = MutableStateFlow(initialSpaceRoomsValue)
|
||||
override val spaceRoomsFlow: Flow<List<SpaceRoom>> = _spaceRoomsFlow.asStateFlow()
|
||||
|
||||
fun emitSpaceRooms(value: List<SpaceRoom>) {
|
||||
_spaceRoomsFlow.value = value
|
||||
}
|
||||
|
||||
private val _paginationStatusFlow: MutableStateFlow<SpaceRoomList.PaginationStatus> = MutableStateFlow(initialSpaceRoomList)
|
||||
override val paginationStatusFlow: StateFlow<SpaceRoomList.PaginationStatus> = _paginationStatusFlow.asStateFlow()
|
||||
|
||||
fun emitPaginationStatus(value: SpaceRoomList.PaginationStatus) {
|
||||
_paginationStatusFlow.value = value
|
||||
}
|
||||
|
||||
override suspend fun paginate(): Result<Unit> = simulateLongTask {
|
||||
paginateResult()
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
// No op
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.spaces
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
|
||||
class FakeSpaceService(
|
||||
private val joinedSpacesResult: () -> Result<List<SpaceRoom>> = { lambdaError() },
|
||||
private val spaceRoomListResult: (RoomId) -> SpaceRoomList = { lambdaError() },
|
||||
private val leaveSpaceHandleResult: (RoomId) -> LeaveSpaceHandle = { lambdaError() },
|
||||
) : SpaceService {
|
||||
private val _spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>()
|
||||
override val spaceRoomsFlow: SharedFlow<List<SpaceRoom>>
|
||||
get() = _spaceRoomsFlow.asSharedFlow()
|
||||
|
||||
suspend fun emitSpaceRoomList(value: List<SpaceRoom>) {
|
||||
_spaceRoomsFlow.emit(value)
|
||||
}
|
||||
|
||||
override suspend fun joinedSpaces(): Result<List<SpaceRoom>> = simulateLongTask {
|
||||
return joinedSpacesResult()
|
||||
}
|
||||
|
||||
override fun spaceRoomList(id: RoomId): SpaceRoomList {
|
||||
return spaceRoomListResult(id)
|
||||
}
|
||||
|
||||
override fun getLeaveSpaceHandle(spaceId: RoomId): LeaveSpaceHandle {
|
||||
return leaveSpaceHandleResult(spaceId)
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.sync
|
||||
|
||||
import io.element.android.libraries.core.coroutine.mapState
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class FakeSyncService(
|
||||
initialSyncState: SyncState = SyncState.Idle,
|
||||
) : SyncService {
|
||||
private val syncStateFlow: MutableStateFlow<SyncState> = MutableStateFlow(initialSyncState)
|
||||
|
||||
var startSyncLambda: () -> Result<Unit> = { Result.success(Unit) }
|
||||
override suspend fun startSync(): Result<Unit> {
|
||||
return startSyncLambda()
|
||||
}
|
||||
|
||||
var stopSyncLambda: () -> Result<Unit> = { Result.success(Unit) }
|
||||
override suspend fun stopSync(): Result<Unit> {
|
||||
return stopSyncLambda()
|
||||
}
|
||||
|
||||
override val syncState: StateFlow<SyncState> = syncStateFlow
|
||||
|
||||
override val isOnline: StateFlow<Boolean> = syncState.mapState { it != SyncState.Offline }
|
||||
|
||||
suspend fun emitSyncState(syncState: SyncState) {
|
||||
syncStateFlow.emit(syncState)
|
||||
}
|
||||
}
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.timeline
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import java.io.File
|
||||
|
||||
class FakeTimeline(
|
||||
private val name: String = "FakeTimeline",
|
||||
override val timelineItems: Flow<List<MatrixTimelineItem>> = MutableStateFlow(emptyList()),
|
||||
override val backwardPaginationStatus: MutableStateFlow<Timeline.PaginationStatus> = MutableStateFlow(
|
||||
Timeline.PaginationStatus(
|
||||
isPaginating = false,
|
||||
hasMoreToLoad = true
|
||||
)
|
||||
),
|
||||
override val forwardPaginationStatus: MutableStateFlow<Timeline.PaginationStatus> = MutableStateFlow(
|
||||
Timeline.PaginationStatus(
|
||||
isPaginating = false,
|
||||
hasMoreToLoad = false
|
||||
)
|
||||
),
|
||||
override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(),
|
||||
override val onSyncedEventReceived: Flow<Unit> = MutableSharedFlow(),
|
||||
private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() },
|
||||
override val mode: Timeline.Mode = Timeline.Mode.Live,
|
||||
private val markAsReadResult: (ReceiptType) -> Result<Unit> = { lambdaError() },
|
||||
private val getLatestEventIdResult: () -> Result<EventId?> = { lambdaError() },
|
||||
var sendReadReceiptLambda: (
|
||||
eventId: EventId,
|
||||
receiptType: ReceiptType,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
) : Timeline {
|
||||
var sendMessageLambda: (
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = simulateLongTask {
|
||||
cancelSendResult(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendMessageLambda(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
var redactEventLambda: (eventOrTransactionId: EventOrTransactionId, reason: String?) -> Result<Unit> = { _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun redactEvent(
|
||||
eventOrTransactionId: EventOrTransactionId,
|
||||
reason: String?
|
||||
): Result<Unit> = redactEventLambda(eventOrTransactionId, reason)
|
||||
|
||||
var editMessageLambda: (
|
||||
eventOrTransactionId: EventOrTransactionId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
eventOrTransactionId: EventOrTransactionId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = editMessageLambda(
|
||||
eventOrTransactionId,
|
||||
body,
|
||||
htmlBody,
|
||||
intentionalMentions
|
||||
)
|
||||
|
||||
var editCaptionLambda: (
|
||||
eventOrTransactionId: EventOrTransactionId,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
) -> Result<Unit> = { _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editCaption(
|
||||
eventOrTransactionId: EventOrTransactionId,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
): Result<Unit> = editCaptionLambda(
|
||||
eventOrTransactionId,
|
||||
caption,
|
||||
formattedCaption,
|
||||
)
|
||||
|
||||
var replyMessageLambda: (
|
||||
inReplyToEventId: EventId?,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(
|
||||
repliedToEventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
): Result<Unit> = replyMessageLambda(
|
||||
repliedToEventId,
|
||||
body,
|
||||
htmlBody,
|
||||
intentionalMentions,
|
||||
fromNotification,
|
||||
)
|
||||
|
||||
var sendImageLambda: (
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
body: String?,
|
||||
formattedBody: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
||||
override suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
sendImageLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
var sendVideoLambda: (
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
body: String?,
|
||||
formattedBody: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
||||
override suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
sendVideoLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
var sendAudioLambda: (
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
||||
override suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
sendAudioLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
var sendFileLambda: (
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
||||
override suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
sendFileLambda(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
var sendVoiceMessageLambda: (
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
||||
override suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
sendVoiceMessageLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
var sendLocationLambda: (
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<Unit> = { _, _, _, _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendLocationLambda(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
var toggleReactionLambda: (emoji: String, eventOrTransactionId: EventOrTransactionId) -> Result<Boolean> = { _, _ -> lambdaError() }
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Boolean> = simulateLongTask {
|
||||
toggleReactionLambda(
|
||||
emoji,
|
||||
eventOrTransactionId,
|
||||
)
|
||||
}
|
||||
|
||||
var forwardEventLambda: (eventId: EventId, roomIds: List<RoomId>) -> Result<Unit> = { _, _ -> lambdaError() }
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
forwardEventLambda(eventId, roomIds)
|
||||
}
|
||||
|
||||
var createPollLambda: (
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
) -> Result<Unit> = { _, _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun createPoll(question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = simulateLongTask {
|
||||
createPollLambda(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
var editPollLambda: (
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind
|
||||
): Result<Unit> = simulateLongTask {
|
||||
editPollLambda(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
var sendPollResponseLambda: (
|
||||
pollStartId: EventId,
|
||||
answers: List<String>,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
pollStartId: EventId,
|
||||
answers: List<String>,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendPollResponseLambda(
|
||||
pollStartId,
|
||||
answers,
|
||||
)
|
||||
}
|
||||
|
||||
var endPollLambda: (
|
||||
pollStartId: EventId,
|
||||
text: String,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun endPoll(
|
||||
pollStartId: EventId,
|
||||
text: String,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
endPollLambda(
|
||||
pollStartId,
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendReadReceipt(
|
||||
eventId: EventId,
|
||||
receiptType: ReceiptType,
|
||||
): Result<Unit> = sendReadReceiptLambda(eventId, receiptType)
|
||||
|
||||
override suspend fun markAsRead(receiptType: ReceiptType): Result<Unit> {
|
||||
return markAsReadResult(receiptType)
|
||||
}
|
||||
|
||||
var paginateLambda: (direction: Timeline.PaginationDirection) -> Result<Boolean> = {
|
||||
Result.success(false)
|
||||
}
|
||||
|
||||
override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = paginateLambda(direction)
|
||||
|
||||
var loadReplyDetailsLambda: (eventId: EventId) -> InReplyTo = {
|
||||
InReplyTo.NotLoaded(it)
|
||||
}
|
||||
|
||||
override suspend fun loadReplyDetails(eventId: EventId) = loadReplyDetailsLambda(eventId)
|
||||
|
||||
var pinEventLambda: (eventId: EventId) -> Result<Boolean> = { lambdaError() }
|
||||
override suspend fun pinEvent(eventId: EventId): Result<Boolean> {
|
||||
return pinEventLambda(eventId)
|
||||
}
|
||||
|
||||
var unpinEventLambda: (eventId: EventId) -> Result<Boolean> = { lambdaError() }
|
||||
override suspend fun unpinEvent(eventId: EventId): Result<Boolean> {
|
||||
return unpinEventLambda(eventId)
|
||||
}
|
||||
|
||||
override suspend fun getLatestEventId(): Result<EventId?> {
|
||||
return getLatestEventIdResult()
|
||||
}
|
||||
|
||||
var closeCounter = 0
|
||||
private set
|
||||
|
||||
override fun close() {
|
||||
closeCounter++
|
||||
}
|
||||
|
||||
override fun toString() = "FakeTimeline: $name"
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.timeline
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class FakeTimelineProvider(
|
||||
initialTimeline: Timeline? = null,
|
||||
) : TimelineProvider {
|
||||
private val timelineFlow = MutableStateFlow(initialTimeline)
|
||||
|
||||
override fun activeTimelineFlow(): StateFlow<Timeline?> {
|
||||
return timelineFlow.asStateFlow()
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.timeline
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class LiveTimelineProvider(
|
||||
private val room: JoinedRoom,
|
||||
) : TimelineProvider {
|
||||
override fun activeTimelineFlow(): StateFlow<Timeline> = MutableStateFlow(room.liveTimeline)
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.timeline
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.poll.PollAnswer
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.timeline.item.EventThreadInfo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShieldProvider
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.Receipt
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.SendHandleProvider
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemDebugInfoProvider
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.core.FakeSendHandle
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
|
||||
fun anEventTimelineItem(
|
||||
eventId: EventId = AN_EVENT_ID,
|
||||
transactionId: TransactionId? = null,
|
||||
isEditable: Boolean = false,
|
||||
canBeRepliedTo: Boolean = false,
|
||||
isOwn: Boolean = false,
|
||||
isRemote: Boolean = false,
|
||||
localSendState: LocalEventSendState? = null,
|
||||
reactions: ImmutableList<EventReaction> = persistentListOf(),
|
||||
receipts: ImmutableList<Receipt> = persistentListOf(),
|
||||
sender: UserId = A_USER_ID,
|
||||
senderProfile: ProfileDetails = aProfileDetails(),
|
||||
timestamp: Long = 0L,
|
||||
content: EventContent = aProfileChangeMessageContent(),
|
||||
debugInfoProvider: TimelineItemDebugInfoProvider = TimelineItemDebugInfoProvider { aTimelineItemDebugInfo() },
|
||||
messageShieldProvider: MessageShieldProvider = MessageShieldProvider { null },
|
||||
sendHandleProvider: SendHandleProvider = SendHandleProvider { FakeSendHandle() }
|
||||
) = EventTimelineItem(
|
||||
eventId = eventId,
|
||||
transactionId = transactionId,
|
||||
isEditable = isEditable,
|
||||
canBeRepliedTo = canBeRepliedTo,
|
||||
isOwn = isOwn,
|
||||
isRemote = isRemote,
|
||||
localSendState = localSendState,
|
||||
reactions = reactions,
|
||||
receipts = receipts,
|
||||
sender = sender,
|
||||
senderProfile = senderProfile,
|
||||
timestamp = timestamp,
|
||||
content = content,
|
||||
origin = null,
|
||||
timelineItemDebugInfoProvider = debugInfoProvider,
|
||||
messageShieldProvider = messageShieldProvider,
|
||||
sendHandleProvider = sendHandleProvider,
|
||||
)
|
||||
|
||||
fun aProfileDetails(
|
||||
displayName: String? = A_USER_NAME,
|
||||
displayNameAmbiguous: Boolean = false,
|
||||
avatarUrl: String? = null
|
||||
): ProfileDetails = ProfileDetails.Ready(
|
||||
displayName = displayName,
|
||||
displayNameAmbiguous = displayNameAmbiguous,
|
||||
avatarUrl = avatarUrl,
|
||||
)
|
||||
|
||||
fun aProfileChangeMessageContent(
|
||||
displayName: String? = null,
|
||||
prevDisplayName: String? = null,
|
||||
avatarUrl: String? = null,
|
||||
prevAvatarUrl: String? = null,
|
||||
) = ProfileChangeContent(
|
||||
displayName = displayName,
|
||||
prevDisplayName = prevDisplayName,
|
||||
avatarUrl = avatarUrl,
|
||||
prevAvatarUrl = prevAvatarUrl,
|
||||
)
|
||||
|
||||
fun aMessageContent(
|
||||
body: String = "body",
|
||||
inReplyTo: InReplyTo? = null,
|
||||
isEdited: Boolean = false,
|
||||
threadInfo: EventThreadInfo? = null,
|
||||
messageType: MessageType = TextMessageType(
|
||||
body = body,
|
||||
formatted = null
|
||||
)
|
||||
) = MessageContent(
|
||||
body = body,
|
||||
inReplyTo = inReplyTo,
|
||||
isEdited = isEdited,
|
||||
threadInfo = threadInfo,
|
||||
type = messageType
|
||||
)
|
||||
|
||||
fun aStickerContent(
|
||||
filename: String = "filename",
|
||||
info: ImageInfo,
|
||||
mediaSource: MediaSource,
|
||||
body: String? = null,
|
||||
) = StickerContent(
|
||||
filename = filename,
|
||||
body = body,
|
||||
info = info,
|
||||
source = mediaSource,
|
||||
)
|
||||
|
||||
fun aTimelineItemDebugInfo(
|
||||
model: String = "Rust(Model())",
|
||||
originalJson: String? = null,
|
||||
latestEditedJson: String? = null,
|
||||
) = TimelineItemDebugInfo(
|
||||
model,
|
||||
originalJson,
|
||||
latestEditedJson
|
||||
)
|
||||
|
||||
fun aPollContent(
|
||||
question: String = "Do you like polls?",
|
||||
answers: ImmutableList<PollAnswer> = persistentListOf(PollAnswer("1", "Yes"), PollAnswer("2", "No")),
|
||||
kind: PollKind = PollKind.Disclosed,
|
||||
maxSelections: ULong = 1u,
|
||||
votes: ImmutableMap<String, ImmutableList<UserId>> = persistentMapOf(),
|
||||
endTime: ULong? = null,
|
||||
isEdited: Boolean = false,
|
||||
) = PollContent(
|
||||
question = question,
|
||||
kind = kind,
|
||||
maxSelections = maxSelections,
|
||||
answers = answers,
|
||||
votes = votes,
|
||||
endTime = endTime,
|
||||
isEdited = isEdited,
|
||||
)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.timeline.item.event
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
|
||||
fun aRoomMembershipContent(
|
||||
userId: UserId = A_USER_ID,
|
||||
userDisplayName: String? = null,
|
||||
change: MembershipChange? = null,
|
||||
reason: String? = null,
|
||||
) = RoomMembershipContent(
|
||||
userId = userId,
|
||||
userDisplayName = userDisplayName,
|
||||
change = change,
|
||||
reason = reason,
|
||||
)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.tracing
|
||||
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingService
|
||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import timber.log.Timber
|
||||
|
||||
class FakeTracingService(
|
||||
private val createTimberTreeResult: (String) -> Timber.Tree = { lambdaError() },
|
||||
private val updateWriteToFilesConfigurationResult: (WriteToFilesConfiguration) -> Unit = { lambdaError() }
|
||||
) : TracingService {
|
||||
override fun createTimberTree(target: String): Timber.Tree {
|
||||
return createTimberTreeResult(target)
|
||||
}
|
||||
|
||||
override fun updateWriteToFilesConfiguration(config: WriteToFilesConfiguration) {
|
||||
updateWriteToFilesConfigurationResult(config)
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.verification
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationRequest
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class FakeSessionVerificationService(
|
||||
initialSessionVerifiedStatus: SessionVerifiedStatus = SessionVerifiedStatus.Unknown,
|
||||
private val requestCurrentSessionVerificationLambda: () -> Unit = { lambdaError() },
|
||||
private val requestUserVerificationLambda: (UserId) -> Unit = { lambdaError() },
|
||||
private val cancelVerificationLambda: () -> Unit = { lambdaError() },
|
||||
private val approveVerificationLambda: () -> Unit = { lambdaError() },
|
||||
private val declineVerificationLambda: () -> Unit = { lambdaError() },
|
||||
private val startVerificationLambda: () -> Unit = { lambdaError() },
|
||||
private val resetLambda: (Boolean) -> Unit = { lambdaError() },
|
||||
private val acknowledgeVerificationRequestLambda: (VerificationRequest.Incoming) -> Unit = { lambdaError() },
|
||||
private val acceptVerificationRequestLambda: () -> Unit = { lambdaError() },
|
||||
) : SessionVerificationService {
|
||||
private val _sessionVerifiedStatus = MutableStateFlow(initialSessionVerifiedStatus)
|
||||
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
|
||||
private var _needsSessionVerification = MutableStateFlow(true)
|
||||
|
||||
override val verificationFlowState: StateFlow<VerificationFlowState> = _verificationFlowState
|
||||
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus
|
||||
override val needsSessionVerification: Flow<Boolean> = _needsSessionVerification
|
||||
|
||||
override suspend fun requestCurrentSessionVerification() {
|
||||
requestCurrentSessionVerificationLambda()
|
||||
}
|
||||
|
||||
override suspend fun requestUserVerification(userId: UserId) {
|
||||
requestUserVerificationLambda(userId)
|
||||
}
|
||||
|
||||
override suspend fun cancelVerification() {
|
||||
cancelVerificationLambda()
|
||||
}
|
||||
|
||||
override suspend fun approveVerification() {
|
||||
approveVerificationLambda()
|
||||
}
|
||||
|
||||
override suspend fun declineVerification() {
|
||||
declineVerificationLambda()
|
||||
}
|
||||
|
||||
override suspend fun startVerification() {
|
||||
startVerificationLambda()
|
||||
}
|
||||
|
||||
override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) {
|
||||
resetLambda(cancelAnyPendingVerificationAttempt)
|
||||
}
|
||||
|
||||
var listener: SessionVerificationServiceListener? = null
|
||||
private set
|
||||
|
||||
override fun setListener(listener: SessionVerificationServiceListener?) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
override suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) {
|
||||
acknowledgeVerificationRequestLambda(verificationRequest)
|
||||
}
|
||||
|
||||
override suspend fun acceptVerificationRequest() = simulateLongTask {
|
||||
acceptVerificationRequestLambda()
|
||||
}
|
||||
|
||||
suspend fun emitVerificationFlowState(state: VerificationFlowState) {
|
||||
_verificationFlowState.emit(state)
|
||||
}
|
||||
|
||||
suspend fun emitVerifiedStatus(status: SessionVerifiedStatus) {
|
||||
_sessionVerifiedStatus.emit(status)
|
||||
}
|
||||
|
||||
suspend fun emitNeedsSessionVerification(needsVerification: Boolean) {
|
||||
_needsSessionVerification.emit(needsVerification)
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.widget
|
||||
|
||||
import io.element.android.libraries.matrix.api.widget.CallWidgetSettingsProvider
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
|
||||
class FakeCallWidgetSettingsProvider(
|
||||
private val provideFn: (String, String, Boolean, Boolean, Boolean) -> MatrixWidgetSettings = { _, _, _, _, _ -> MatrixWidgetSettings("id", true, "url") }
|
||||
) : CallWidgetSettingsProvider {
|
||||
val providedBaseUrls = mutableListOf<String>()
|
||||
|
||||
override suspend fun provide(
|
||||
baseUrl: String,
|
||||
widgetId: String,
|
||||
encrypted: Boolean,
|
||||
direct: Boolean,
|
||||
hasActiveCall: Boolean
|
||||
): MatrixWidgetSettings {
|
||||
providedBaseUrls += baseUrl
|
||||
return provideFn(baseUrl, widgetId, encrypted, direct, hasActiveCall)
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2023-2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.test.widget
|
||||
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import java.util.UUID
|
||||
|
||||
class FakeMatrixWidgetDriver(
|
||||
override val id: String = UUID.randomUUID().toString(),
|
||||
) : MatrixWidgetDriver {
|
||||
private val _sentMessages = mutableListOf<String>()
|
||||
val sentMessages: List<String> = _sentMessages
|
||||
|
||||
var runCalledCount = 0
|
||||
private set
|
||||
var closeCalledCount = 0
|
||||
private set
|
||||
|
||||
override val incomingMessages = MutableSharedFlow<String>(extraBufferCapacity = 1)
|
||||
|
||||
override suspend fun run() {
|
||||
runCalledCount++
|
||||
}
|
||||
|
||||
override suspend fun send(message: String) {
|
||||
_sentMessages.add(message)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
closeCalledCount++
|
||||
}
|
||||
|
||||
fun givenIncomingMessage(message: String) {
|
||||
incomingMessages.tryEmit(message)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user