Merge pull request #3501 from element-hq/bma/testRustMatrixClient
Test RustMatrixClient and other classes in the matrix module
This commit is contained in:
commit
dfe9323455
70 changed files with 1597 additions and 137 deletions
|
|
@ -94,7 +94,7 @@ sealed interface NotificationContent {
|
|||
data object RoomHistoryVisibility : StateEvent
|
||||
data object RoomJoinRules : StateEvent
|
||||
data class RoomMemberContent(
|
||||
val userId: String,
|
||||
val userId: UserId,
|
||||
val membershipState: RoomMembershipState
|
||||
) : StateEvent
|
||||
|
||||
|
|
@ -108,6 +108,10 @@ sealed interface NotificationContent {
|
|||
data object SpaceChild : StateEvent
|
||||
data object SpaceParent : StateEvent
|
||||
}
|
||||
|
||||
data class Invite(
|
||||
val senderId: UserId,
|
||||
) : NotificationContent
|
||||
}
|
||||
|
||||
enum class CallNotifyType {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ package io.element.android.libraries.matrix.api.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.core.SessionId
|
||||
|
||||
interface NotificationService {
|
||||
suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result<NotificationData?>
|
||||
suspend fun getNotification(roomId: RoomId, eventId: EventId): Result<NotificationData?>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ dependencies {
|
|||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.preferences.test)
|
||||
testImplementation(projects.libraries.sessionStorage.implMemory)
|
||||
testImplementation(projects.libraries.sessionStorage.test)
|
||||
testImplementation(projects.services.analytics.test)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
import javax.inject.Inject
|
||||
|
||||
interface ClientBuilderProvider {
|
||||
fun provide(): ClientBuilder
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class RustClientBuilderProvider @Inject constructor() : ClientBuilderProvider {
|
||||
override fun provide(): ClientBuilder {
|
||||
return ClientBuilder()
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
|||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomFactory
|
||||
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
|
||||
|
|
@ -115,14 +116,15 @@ import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RustMatrixClient(
|
||||
private val client: Client,
|
||||
private val syncService: ClientSyncService,
|
||||
private val baseDirectory: File,
|
||||
private val sessionStore: SessionStore,
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val baseDirectory: File,
|
||||
baseCacheDirectory: File,
|
||||
private val clock: SystemClock,
|
||||
private val sessionDelegate: RustClientSessionDelegate,
|
||||
syncService: ClientSyncService,
|
||||
dispatchers: CoroutineDispatchers,
|
||||
baseCacheDirectory: File,
|
||||
clock: SystemClock,
|
||||
timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
||||
) : MatrixClient {
|
||||
override val sessionId: UserId = UserId(client.userId())
|
||||
override val deviceId: DeviceId = DeviceId(client.deviceId())
|
||||
|
|
@ -138,7 +140,7 @@ class RustMatrixClient(
|
|||
)
|
||||
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(syncService)
|
||||
private val notificationClient = runBlocking { client.notificationClient(notificationProcessSetup) }
|
||||
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
|
||||
private val notificationService = RustNotificationService(notificationClient, dispatchers, clock)
|
||||
private val notificationSettingsService = RustNotificationSettingsService(client, dispatchers)
|
||||
.apply { start() }
|
||||
private val encryptionService = RustEncryptionService(
|
||||
|
|
@ -185,6 +187,7 @@ class RustMatrixClient(
|
|||
systemClock = clock,
|
||||
roomContentForwarder = RoomContentForwarder(innerRoomListService),
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
|
||||
)
|
||||
|
||||
override val mediaLoader: MatrixMediaLoader = RustMediaLoader(
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.impl.certificates.UserCertificatesPro
|
|||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
||||
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
||||
import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.matrix.impl.util.anonymizedTokens
|
||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
|
|
@ -45,6 +46,8 @@ class RustMatrixClientFactory @Inject constructor(
|
|||
private val clock: SystemClock,
|
||||
private val utdTracker: UtdTracker,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
||||
private val clientBuilderProvider: ClientBuilderProvider,
|
||||
) {
|
||||
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
||||
val sessionDelegate = RustClientSessionDelegate(sessionStore, appCoroutineScope, coroutineDispatchers)
|
||||
|
|
@ -68,14 +71,15 @@ class RustMatrixClientFactory @Inject constructor(
|
|||
|
||||
RustMatrixClient(
|
||||
client = client,
|
||||
syncService = syncService,
|
||||
baseDirectory = baseDirectory,
|
||||
sessionStore = sessionStore,
|
||||
appCoroutineScope = appCoroutineScope,
|
||||
sessionDelegate = sessionDelegate,
|
||||
syncService = syncService,
|
||||
dispatchers = coroutineDispatchers,
|
||||
baseDirectory = baseDirectory,
|
||||
baseCacheDirectory = cacheDirectory,
|
||||
clock = clock,
|
||||
sessionDelegate = sessionDelegate,
|
||||
timelineEventTypeFilterFactory = timelineEventTypeFilterFactory,
|
||||
).also {
|
||||
Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'")
|
||||
}
|
||||
|
|
@ -86,7 +90,7 @@ class RustMatrixClientFactory @Inject constructor(
|
|||
passphrase: String?,
|
||||
slidingSyncType: ClientBuilderSlidingSync,
|
||||
): ClientBuilder {
|
||||
return ClientBuilder()
|
||||
return clientBuilderProvider.provide()
|
||||
.sessionPaths(
|
||||
dataPath = sessionPaths.fileDirectory.absolutePath,
|
||||
cachePath = sessionPaths.cacheDirectory.absolutePath,
|
||||
|
|
|
|||
|
|
@ -10,10 +10,9 @@ package io.element.android.libraries.matrix.impl.notification
|
|||
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.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import org.matrix.rustcomponents.sdk.NotificationEvent
|
||||
|
|
@ -21,10 +20,9 @@ import org.matrix.rustcomponents.sdk.NotificationItem
|
|||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class NotificationMapper(
|
||||
sessionId: SessionId,
|
||||
private val clock: SystemClock,
|
||||
) {
|
||||
private val notificationContentMapper = NotificationContentMapper(sessionId)
|
||||
private val notificationContentMapper = NotificationContentMapper()
|
||||
|
||||
fun map(
|
||||
eventId: EventId,
|
||||
|
|
@ -56,15 +54,14 @@ class NotificationMapper(
|
|||
}
|
||||
}
|
||||
|
||||
class NotificationContentMapper(private val sessionId: SessionId) {
|
||||
class NotificationContentMapper {
|
||||
private val timelineEventToNotificationContentMapper = TimelineEventToNotificationContentMapper()
|
||||
|
||||
fun map(notificationEvent: NotificationEvent): NotificationContent =
|
||||
when (notificationEvent) {
|
||||
is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(notificationEvent.event)
|
||||
is NotificationEvent.Invite -> NotificationContent.StateEvent.RoomMemberContent(
|
||||
userId = sessionId.value,
|
||||
membershipState = RoomMembershipState.INVITE,
|
||||
is NotificationEvent.Invite -> NotificationContent.Invite(
|
||||
senderId = UserId(notificationEvent.sender),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ package io.element.android.libraries.matrix.impl.notification
|
|||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
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.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
|
|
@ -19,15 +18,13 @@ import org.matrix.rustcomponents.sdk.NotificationClient
|
|||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class RustNotificationService(
|
||||
sessionId: SessionId,
|
||||
private val notificationClient: NotificationClient,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
clock: SystemClock,
|
||||
) : NotificationService {
|
||||
private val notificationMapper: NotificationMapper = NotificationMapper(sessionId, clock)
|
||||
private val notificationMapper: NotificationMapper = NotificationMapper(clock)
|
||||
|
||||
override suspend fun getNotification(
|
||||
userId: SessionId,
|
||||
roomId: RoomId,
|
||||
eventId: EventId,
|
||||
): Result<NotificationData?> = withContext(dispatchers.io) {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@ import org.matrix.rustcomponents.sdk.StateEventContent
|
|||
import org.matrix.rustcomponents.sdk.TimelineEvent
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineEventToNotificationContentMapper @Inject constructor() {
|
||||
class TimelineEventToNotificationContentMapper {
|
||||
fun map(timelineEvent: TimelineEvent): NotificationContent {
|
||||
return timelineEvent.use {
|
||||
timelineEvent.eventType().use { eventType ->
|
||||
|
|
@ -52,7 +51,10 @@ private fun StateEventContent.toContent(): NotificationContent.StateEvent {
|
|||
StateEventContent.RoomHistoryVisibility -> NotificationContent.StateEvent.RoomHistoryVisibility
|
||||
StateEventContent.RoomJoinRules -> NotificationContent.StateEvent.RoomJoinRules
|
||||
is StateEventContent.RoomMemberContent -> {
|
||||
NotificationContent.StateEvent.RoomMemberContent(userId, RoomMemberMapper.mapMembership(membershipState))
|
||||
NotificationContent.StateEvent.RoomMemberContent(
|
||||
userId = UserId(userId),
|
||||
membershipState = RoomMemberMapper.mapMembership(membershipState),
|
||||
)
|
||||
}
|
||||
StateEventContent.RoomName -> NotificationContent.StateEvent.RoomName
|
||||
StateEventContent.RoomPinnedEvents -> NotificationContent.StateEvent.RoomPinnedEvents
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ import kotlinx.coroutines.sync.Mutex
|
|||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.RequiredState
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||
import org.matrix.rustcomponents.sdk.RoomListService
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import timber.log.Timber
|
||||
|
||||
private const val DEFAULT_TIMELINE_LIMIT = 20u
|
||||
|
||||
class RoomSyncSubscriber(
|
||||
private val roomListService: RoomListServiceInterface,
|
||||
private val roomListService: RoomListService,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
private val subscribedRoomIds = mutableSetOf<RoomId>()
|
||||
|
|
|
|||
|
|
@ -27,12 +27,10 @@ import kotlinx.coroutines.NonCancellable
|
|||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListException
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
import timber.log.Timber
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||
|
||||
|
|
@ -49,6 +47,7 @@ class RustRoomFactory(
|
|||
private val roomListService: RoomListService,
|
||||
private val innerRoomListService: InnerRoomListService,
|
||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory,
|
||||
) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val dispatcher = dispatchers.io.limitedParallelism(1)
|
||||
|
|
@ -74,11 +73,7 @@ class RustRoomFactory(
|
|||
private val eventFilters = TimelineConfig.excludedEvents
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { listStateEventType ->
|
||||
TimelineEventTypeFilter.exclude(
|
||||
listStateEventType.map { stateEventType ->
|
||||
FilterTimelineEventType.State(stateEventType.map())
|
||||
}
|
||||
)
|
||||
timelineEventTypeFilterFactory.create(listStateEventType)
|
||||
}
|
||||
|
||||
suspend fun destroy() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
import javax.inject.Inject
|
||||
|
||||
interface TimelineEventTypeFilterFactory {
|
||||
fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class RustTimelineEventTypeFilterFactory @Inject constructor() : TimelineEventTypeFilterFactory {
|
||||
override fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter {
|
||||
return TimelineEventTypeFilter.exclude(
|
||||
listStateEventType.map { stateEventType ->
|
||||
FilterTimelineEventType.State(stateEventType.map())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,10 +11,12 @@ import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
|||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem
|
||||
|
||||
class RoomMessageFactory {
|
||||
class RoomMessageFactory(
|
||||
private val eventTimelineItemMapper: EventTimelineItemMapper = EventTimelineItemMapper(),
|
||||
) {
|
||||
fun create(eventTimelineItem: RustEventTimelineItem?): RoomMessage? {
|
||||
eventTimelineItem ?: return null
|
||||
val mappedTimelineItem = EventTimelineItemMapper().map(eventTimelineItem)
|
||||
val mappedTimelineItem = eventTimelineItemMapper.map(eventTimelineItem)
|
||||
return RoomMessage(
|
||||
eventId = mappedTimelineItem.eventId ?: return null,
|
||||
event = mappedTimelineItem,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
package io.element.android.libraries.matrix.impl.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
@ -17,10 +18,12 @@ import timber.log.Timber
|
|||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class RoomDirectorySearchProcessor(
|
||||
private val roomDescriptions: MutableSharedFlow<List<RoomDescription>>,
|
||||
private val coroutineContext: CoroutineContext,
|
||||
private val roomDescriptionMapper: RoomDescriptionMapper,
|
||||
) {
|
||||
private val roomDescriptions: MutableSharedFlow<List<RoomDescription>> = MutableSharedFlow(replay = 1)
|
||||
val roomDescriptionsFlow: Flow<List<RoomDescription>> = roomDescriptions
|
||||
|
||||
private val roomDescriptionMapper: RoomDescriptionMapper = RoomDescriptionMapper()
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend fun postUpdates(updates: List<RoomDirectorySearchEntryUpdate>) {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,10 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
|
@ -27,8 +25,7 @@ class RustRoomDirectoryList(
|
|||
private val coroutineContext: CoroutineContext,
|
||||
) : RoomDirectoryList {
|
||||
private val hasMoreToLoad = MutableStateFlow(true)
|
||||
private val items = MutableSharedFlow<List<RoomDescription>>(replay = 1)
|
||||
private val processor = RoomDirectorySearchProcessor(items, coroutineContext, RoomDescriptionMapper())
|
||||
private val processor = RoomDirectorySearchProcessor(coroutineContext)
|
||||
|
||||
init {
|
||||
launchIn(coroutineScope)
|
||||
|
|
@ -77,7 +74,7 @@ class RustRoomDirectoryList(
|
|||
}
|
||||
|
||||
override val state: Flow<RoomDirectoryList.State> =
|
||||
combine(hasMoreToLoad, items) { hasMoreToLoad, items ->
|
||||
combine(hasMoreToLoad, processor.roomDescriptionsFlow) { hasMoreToLoad, items ->
|
||||
RoomDirectoryList.State(
|
||||
hasMoreToLoad = hasMoreToLoad,
|
||||
items = items
|
||||
|
|
|
|||
|
|
@ -25,21 +25,21 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
|||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||
import org.matrix.rustcomponents.sdk.RoomListService
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
|
||||
|
||||
internal class RoomListFactory(
|
||||
private val innerRoomListService: RoomListService,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||
) {
|
||||
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory()
|
||||
|
||||
/**
|
||||
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
||||
*/
|
||||
fun createRoomList(
|
||||
pageSize: Int,
|
||||
coroutineContext: CoroutineContext,
|
||||
coroutineScope: CoroutineScope = sessionCoroutineScope,
|
||||
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
initialFilter: RoomListFilter = RoomListFilter.all(),
|
||||
innerProvider: suspend () -> InnerRoomList
|
||||
): DynamicRoomList {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory
|
|||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
|
||||
class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory()) {
|
||||
class RoomSummaryDetailsFactory(
|
||||
private val roomMessageFactory: RoomMessageFactory = RoomMessageFactory(),
|
||||
) {
|
||||
suspend fun create(roomListItem: RoomListItem): RoomSummary {
|
||||
val roomInfo = roomListItem.roomInfo()
|
||||
val latestRoomMessage = roomListItem.latestEvent().use { event ->
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ private const val DEFAULT_PAGE_SIZE = 20
|
|||
|
||||
internal class RustRoomListService(
|
||||
private val innerRoomListService: InnerRustRoomListService,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val sessionDispatcher: CoroutineDispatcher,
|
||||
private val roomListFactory: RoomListFactory,
|
||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
) : RoomListService {
|
||||
override fun createRoomList(
|
||||
pageSize: Int,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import org.matrix.rustcomponents.sdk.TimelineItem
|
|||
class MatrixTimelineItemMapper(
|
||||
private val fetchDetailsForEvent: suspend (EventId) -> Result<Unit>,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val virtualTimelineItemMapper: VirtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
||||
private val eventTimelineItemMapper: EventTimelineItemMapper = EventTimelineItemMapper(),
|
||||
private val virtualTimelineItemMapper: VirtualTimelineItemMapper,
|
||||
private val eventTimelineItemMapper: EventTimelineItemMapper,
|
||||
) {
|
||||
fun map(timelineItem: TimelineItem): MatrixTimelineItem = timelineItem.use {
|
||||
val uniqueId = UniqueId(timelineItem.uniqueId())
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails
|
|||
import org.matrix.rustcomponents.sdk.Receipt as RustReceipt
|
||||
import uniffi.matrix_sdk_ui.EventItemOrigin as RustEventItemOrigin
|
||||
|
||||
class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMapper = TimelineEventContentMapper()) {
|
||||
class EventTimelineItemMapper(
|
||||
private val contentMapper: TimelineEventContentMapper = TimelineEventContentMapper(),
|
||||
) {
|
||||
fun map(eventTimelineItem: RustEventTimelineItem): EventTimelineItem = eventTimelineItem.use {
|
||||
EventTimelineItem(
|
||||
eventId = it.eventId()?.let(::EventId),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ import org.matrix.rustcomponents.sdk.MembershipChange as RustMembershipChange
|
|||
import org.matrix.rustcomponents.sdk.OtherState as RustOtherState
|
||||
import uniffi.matrix_sdk_crypto.UtdCause as RustUtdCause
|
||||
|
||||
class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMapper = EventMessageMapper()) {
|
||||
class TimelineEventContentMapper(
|
||||
private val eventMessageMapper: EventMessageMapper = EventMessageMapper(),
|
||||
) {
|
||||
fun map(content: TimelineItemContent): EventContent {
|
||||
return content.use {
|
||||
content.kind().use { kind ->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClientBuilder
|
||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
|
||||
class FakeClientBuilderProvider : ClientBuilderProvider {
|
||||
override fun provide(): ClientBuilder {
|
||||
return FakeRustClientBuilder()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RustClientSessionDelegateTest {
|
||||
@Test
|
||||
fun `saveSessionInKeychain should update the store`() = runTest {
|
||||
val sessionStore = InMemorySessionStore()
|
||||
sessionStore.storeData(
|
||||
aSessionData(
|
||||
accessToken = "anAccessToken",
|
||||
refreshToken = "aRefreshToken",
|
||||
)
|
||||
)
|
||||
val sut = aRustClientSessionDelegate(sessionStore)
|
||||
sut.saveSessionInKeychain(
|
||||
aRustSession(
|
||||
accessToken = "at",
|
||||
refreshToken = "rt",
|
||||
)
|
||||
)
|
||||
runCurrent()
|
||||
val result = sessionStore.getLatestSession()
|
||||
assertThat(result!!.accessToken).isEqualTo("at")
|
||||
assertThat(result.refreshToken).isEqualTo("rt")
|
||||
}
|
||||
}
|
||||
|
||||
fun TestScope.aRustClientSessionDelegate(
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
) = RustClientSessionDelegate(
|
||||
sessionStore = sessionStore,
|
||||
appCoroutineScope = this,
|
||||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
)
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||
import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider
|
||||
import io.element.android.libraries.matrix.impl.auth.FakeUserCertificatesProvider
|
||||
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class RustMatrixClientFactoryTest {
|
||||
@Test
|
||||
fun test() = runTest {
|
||||
val sut = createRustMatrixClientFactory()
|
||||
val result = sut.create(aSessionData())
|
||||
assertThat(result.sessionId).isEqualTo(SessionId("@alice:server.org"))
|
||||
result.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun TestScope.createRustMatrixClientFactory(
|
||||
baseDirectory: File = File("/base"),
|
||||
cacheDirectory: File = File("/cache"),
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
) = RustMatrixClientFactory(
|
||||
baseDirectory = baseDirectory,
|
||||
cacheDirectory = cacheDirectory,
|
||||
appCoroutineScope = this,
|
||||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
sessionStore = sessionStore,
|
||||
userAgentProvider = SimpleUserAgentProvider(),
|
||||
userCertificatesProvider = FakeUserCertificatesProvider(),
|
||||
proxyProvider = FakeProxyProvider(),
|
||||
clock = FakeSystemClock(),
|
||||
utdTracker = UtdTracker(FakeAnalyticsService()),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
|
||||
clientBuilderProvider = FakeClientBuilderProvider(),
|
||||
)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustSyncService
|
||||
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class RustMatrixClientTest {
|
||||
@Test
|
||||
fun `ensure that sessionId and deviceId can be retrieved from the client`() = runTest {
|
||||
createRustMatrixClient().use { sut ->
|
||||
assertThat(sut.sessionId).isEqualTo(A_USER_ID)
|
||||
assertThat(sut.deviceId).isEqualTo(A_DEVICE_ID)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createRustMatrixClient(
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
) = RustMatrixClient(
|
||||
client = FakeRustClient(),
|
||||
baseDirectory = File(""),
|
||||
sessionStore = sessionStore,
|
||||
appCoroutineScope = this,
|
||||
sessionDelegate = aRustClientSessionDelegate(
|
||||
sessionStore = sessionStore,
|
||||
),
|
||||
syncService = FakeRustSyncService(),
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
baseCacheDirectory = File(""),
|
||||
clock = FakeSystemClock(),
|
||||
timelineEventTypeFilterFactory = FakeTimelineEventTypeFilterFactory(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
|
||||
import io.element.android.libraries.matrix.test.A_PASSPHRASE
|
||||
|
||||
class FakePassphraseGenerator(
|
||||
private val passphrase: () -> String? = { A_PASSPHRASE }
|
||||
) : PassphraseGenerator {
|
||||
override fun generatePassphrase(): String? = passphrase()
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
||||
|
||||
class FakeProxyProvider : ProxyProvider {
|
||||
override fun provides(): String? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
||||
|
||||
class FakeUserCertificatesProvider : UserCertificatesProvider {
|
||||
override fun provides(): List<ByteArray> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustHomeserverLoginDetails
|
||||
import org.junit.Test
|
||||
|
||||
class HomeserverDetailsKtTest {
|
||||
@Test
|
||||
fun `map should be correct`() {
|
||||
// Given
|
||||
val homeserverLoginDetails = FakeRustHomeserverLoginDetails(
|
||||
url = "https://example.org",
|
||||
supportsPasswordLogin = true,
|
||||
supportsOidcLogin = false
|
||||
)
|
||||
|
||||
// When
|
||||
val result = homeserverLoginDetails.map()
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo(
|
||||
MatrixHomeServerDetails(
|
||||
url = "https://example.org",
|
||||
supportsPasswordLogin = true,
|
||||
supportsOidcLogin = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.auth.OidcConfig
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class OidcConfigurationProviderTest {
|
||||
@Test
|
||||
fun get() {
|
||||
val result = OidcConfigurationProvider(File("/base")).get()
|
||||
assertThat(result.redirectUri).isEqualTo(OidcConfig.REDIRECT_URI)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.impl.createRustMatrixClientFactory
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class RustMatrixAuthenticationServiceTest {
|
||||
@Test
|
||||
fun `getLatestSessionId should return the value from the store`() = runTest {
|
||||
val sessionStore = InMemorySessionStore()
|
||||
val sut = createRustMatrixAuthenticationService(
|
||||
sessionStore = sessionStore,
|
||||
)
|
||||
assertThat(sut.getLatestSessionId()).isNull()
|
||||
sessionStore.storeData(aSessionData(sessionId = "@alice:server.org"))
|
||||
assertThat(sut.getLatestSessionId()).isEqualTo(SessionId("@alice:server.org"))
|
||||
}
|
||||
|
||||
private fun TestScope.createRustMatrixAuthenticationService(
|
||||
sessionStore: SessionStore = InMemorySessionStore(),
|
||||
): RustMatrixAuthenticationService {
|
||||
val baseDirectory = File("/base")
|
||||
val cacheDirectory = File("/cache")
|
||||
val rustMatrixClientFactory = createRustMatrixClientFactory(
|
||||
baseDirectory = baseDirectory,
|
||||
cacheDirectory = cacheDirectory,
|
||||
sessionStore = sessionStore,
|
||||
)
|
||||
return RustMatrixAuthenticationService(
|
||||
sessionPathsFactory = SessionPathsFactory(baseDirectory, cacheDirectory),
|
||||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
sessionStore = sessionStore,
|
||||
rustMatrixClientFactory = rustMatrixClientFactory,
|
||||
passphraseGenerator = FakePassphraseGenerator(),
|
||||
oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory),
|
||||
appPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.factories
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineEvent
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import org.matrix.rustcomponents.sdk.NotificationEvent
|
||||
import org.matrix.rustcomponents.sdk.NotificationItem
|
||||
import org.matrix.rustcomponents.sdk.NotificationRoomInfo
|
||||
import org.matrix.rustcomponents.sdk.NotificationSenderInfo
|
||||
import org.matrix.rustcomponents.sdk.TimelineEvent
|
||||
|
||||
fun aRustNotificationItem(
|
||||
event: NotificationEvent = aRustNotificationEventTimeline(),
|
||||
senderInfo: NotificationSenderInfo = aRustNotificationSenderInfo(),
|
||||
roomInfo: NotificationRoomInfo = aRustNotificationRoomInfo(),
|
||||
isNoisy: Boolean? = false,
|
||||
hasMention: Boolean? = false,
|
||||
) = NotificationItem(
|
||||
event = event,
|
||||
senderInfo = senderInfo,
|
||||
roomInfo = roomInfo,
|
||||
isNoisy = isNoisy,
|
||||
hasMention = hasMention,
|
||||
)
|
||||
|
||||
fun aRustNotificationSenderInfo(
|
||||
displayName: String? = A_USER_NAME,
|
||||
avatarUrl: String? = null,
|
||||
isNameAmbiguous: Boolean = false,
|
||||
) = NotificationSenderInfo(
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
isNameAmbiguous = isNameAmbiguous,
|
||||
)
|
||||
|
||||
fun aRustNotificationRoomInfo(
|
||||
displayName: String = A_ROOM_NAME,
|
||||
avatarUrl: String? = null,
|
||||
canonicalAlias: String? = null,
|
||||
joinedMembersCount: ULong = 2u,
|
||||
isEncrypted: Boolean? = true,
|
||||
isDirect: Boolean = false,
|
||||
) = NotificationRoomInfo(
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
canonicalAlias = canonicalAlias,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
isEncrypted = isEncrypted,
|
||||
isDirect = isDirect,
|
||||
)
|
||||
|
||||
fun aRustNotificationEventTimeline(
|
||||
event: TimelineEvent = FakeRustTimelineEvent(),
|
||||
) = NotificationEvent.Timeline(
|
||||
event = event,
|
||||
)
|
||||
|
|
@ -12,15 +12,24 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
|||
import org.matrix.rustcomponents.sdk.PublicRoomJoinRule
|
||||
import org.matrix.rustcomponents.sdk.RoomDescription
|
||||
|
||||
internal fun aRustRoomDescription(): RoomDescription {
|
||||
internal fun aRustRoomDescription(
|
||||
roomId: String = A_ROOM_ID.value,
|
||||
name: String? = "name",
|
||||
topic: String? = "topic",
|
||||
alias: String? = A_ROOM_ALIAS.value,
|
||||
avatarUrl: String? = "avatarUrl",
|
||||
joinRule: PublicRoomJoinRule = PublicRoomJoinRule.PUBLIC,
|
||||
isWorldReadable: Boolean = true,
|
||||
joinedMembers: ULong = 2u,
|
||||
): RoomDescription {
|
||||
return RoomDescription(
|
||||
roomId = A_ROOM_ID.value,
|
||||
name = "name",
|
||||
topic = "topic",
|
||||
alias = A_ROOM_ALIAS.value,
|
||||
avatarUrl = "avatarUrl",
|
||||
joinRule = PublicRoomJoinRule.PUBLIC,
|
||||
isWorldReadable = true,
|
||||
joinedMembers = 2u
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
topic = topic,
|
||||
alias = alias,
|
||||
avatarUrl = avatarUrl,
|
||||
joinRule = joinRule,
|
||||
isWorldReadable = isWorldReadable,
|
||||
joinedMembers = joinedMembers,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.factories
|
||||
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationSettings
|
||||
|
||||
fun aRustRoomNotificationSettings(
|
||||
mode: RoomNotificationMode = RoomNotificationMode.ALL_MESSAGES,
|
||||
isDefault: Boolean = true,
|
||||
) = RoomNotificationSettings(
|
||||
mode = mode,
|
||||
isDefault = isDefault
|
||||
)
|
||||
|
|
@ -14,11 +14,13 @@ import org.matrix.rustcomponents.sdk.Session
|
|||
import org.matrix.rustcomponents.sdk.SlidingSyncVersion
|
||||
|
||||
internal fun aRustSession(
|
||||
proxy: SlidingSyncVersion = SlidingSyncVersion.None
|
||||
proxy: SlidingSyncVersion = SlidingSyncVersion.None,
|
||||
accessToken: String = "accessToken",
|
||||
refreshToken: String = "refreshToken",
|
||||
): Session {
|
||||
return Session(
|
||||
accessToken = "accessToken",
|
||||
refreshToken = "refreshToken",
|
||||
accessToken = accessToken,
|
||||
refreshToken = refreshToken,
|
||||
userId = A_USER_ID.value,
|
||||
deviceId = A_DEVICE_ID.value,
|
||||
homeserverUrl = A_HOMESERVER_URL,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.factories
|
||||
|
||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||
import org.matrix.rustcomponents.sdk.FormattedBody
|
||||
import org.matrix.rustcomponents.sdk.MessageLikeEventContent
|
||||
import org.matrix.rustcomponents.sdk.MessageType
|
||||
import org.matrix.rustcomponents.sdk.TextMessageContent
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventType
|
||||
|
||||
fun aRustTimelineEventTypeMessageLike(
|
||||
content: MessageLikeEventContent = aRustMessageLikeEventContentRoomMessage(),
|
||||
): TimelineEventType.MessageLike {
|
||||
return TimelineEventType.MessageLike(
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
fun aRustMessageLikeEventContentRoomMessage(
|
||||
messageType: MessageType = aRustMessageTypeText(),
|
||||
inReplyToEventId: String? = null,
|
||||
) = MessageLikeEventContent.RoomMessage(
|
||||
messageType = messageType,
|
||||
inReplyToEventId = inReplyToEventId,
|
||||
)
|
||||
|
||||
fun aRustMessageTypeText(
|
||||
content: TextMessageContent = aRustTextMessageContent(),
|
||||
) = MessageType.Text(
|
||||
content = content,
|
||||
)
|
||||
|
||||
fun aRustTextMessageContent(
|
||||
body: String = A_MESSAGE,
|
||||
formatted: FormattedBody? = null,
|
||||
) = TextMessageContent(
|
||||
body = body,
|
||||
formatted = formatted,
|
||||
)
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearch
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntriesListener
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntryUpdate
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
|
||||
class FakeRoomDirectorySearch(
|
||||
var isAtLastPage: Boolean = false,
|
||||
) : RoomDirectorySearch(NoPointer) {
|
||||
override suspend fun isAtLastPage(): Boolean {
|
||||
return isAtLastPage
|
||||
}
|
||||
|
||||
override suspend fun search(filter: String?, batchSize: UInt) = simulateLongTask { }
|
||||
override suspend fun nextPage() = simulateLongTask { }
|
||||
|
||||
private var listener: RoomDirectorySearchEntriesListener? = null
|
||||
|
||||
override suspend fun results(listener: RoomDirectorySearchEntriesListener): TaskHandle {
|
||||
this.listener = listener
|
||||
return FakeRustTaskHandle()
|
||||
}
|
||||
|
||||
fun emitResult(roomEntriesUpdate: List<RoomDirectorySearchEntryUpdate>) {
|
||||
listener?.onUpdate(roomEntriesUpdate)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientDelegate
|
||||
import org.matrix.rustcomponents.sdk.Encryption
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.NotificationClient
|
||||
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
|
||||
import org.matrix.rustcomponents.sdk.NotificationSettings
|
||||
import org.matrix.rustcomponents.sdk.PusherIdentifiers
|
||||
import org.matrix.rustcomponents.sdk.PusherKind
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearch
|
||||
import org.matrix.rustcomponents.sdk.Session
|
||||
import org.matrix.rustcomponents.sdk.SyncServiceBuilder
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
|
||||
class FakeRustClient(
|
||||
private val userId: String = A_USER_ID.value,
|
||||
private val deviceId: String = A_DEVICE_ID.value,
|
||||
private val notificationClient: NotificationClient = FakeRustNotificationClient(),
|
||||
private val notificationSettings: NotificationSettings = FakeRustNotificationSettings(),
|
||||
private val encryption: Encryption = FakeRustEncryption(),
|
||||
private val session: Session = aRustSession(),
|
||||
) : Client(NoPointer) {
|
||||
override fun userId(): String = userId
|
||||
override fun deviceId(): String = deviceId
|
||||
override suspend fun notificationClient(processSetup: NotificationProcessSetup) = notificationClient
|
||||
override fun getNotificationSettings(): NotificationSettings = notificationSettings
|
||||
override fun encryption(): Encryption = encryption
|
||||
override fun session(): Session = session
|
||||
override fun setDelegate(delegate: ClientDelegate?): TaskHandle = FakeRustTaskHandle()
|
||||
override fun cachedAvatarUrl(): String? = null
|
||||
override suspend fun restoreSession(session: Session) = Unit
|
||||
override fun syncService(): SyncServiceBuilder = FakeRustSyncServiceBuilder()
|
||||
override fun roomDirectorySearch(): RoomDirectorySearch = FakeRoomDirectorySearch()
|
||||
override suspend fun setPusher(
|
||||
identifiers: PusherIdentifiers,
|
||||
kind: PusherKind,
|
||||
appDisplayName: String,
|
||||
deviceDisplayName: String,
|
||||
profileTag: String?,
|
||||
lang: String,
|
||||
) = Unit
|
||||
|
||||
override suspend fun deletePusher(identifiers: PusherIdentifiers) = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
import org.matrix.rustcomponents.sdk.ClientSessionDelegate
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.OidcConfiguration
|
||||
import org.matrix.rustcomponents.sdk.QrCodeData
|
||||
import org.matrix.rustcomponents.sdk.QrLoginProgressListener
|
||||
import org.matrix.rustcomponents.sdk.RequestConfig
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncVersionBuilder
|
||||
import uniffi.matrix_sdk.BackupDownloadStrategy
|
||||
import uniffi.matrix_sdk_crypto.CollectStrategy
|
||||
|
||||
class FakeRustClientBuilder : ClientBuilder(NoPointer) {
|
||||
override fun addRootCertificates(certificates: List<ByteArray>) = this
|
||||
override fun autoEnableBackups(autoEnableBackups: Boolean) = this
|
||||
override fun autoEnableCrossSigning(autoEnableCrossSigning: Boolean) = this
|
||||
override fun backupDownloadStrategy(backupDownloadStrategy: BackupDownloadStrategy) = this
|
||||
override fun disableAutomaticTokenRefresh() = this
|
||||
override fun disableBuiltInRootCertificates() = this
|
||||
override fun disableSslVerification() = this
|
||||
override fun enableCrossProcessRefreshLock(processId: String, sessionDelegate: ClientSessionDelegate) = this
|
||||
override fun homeserverUrl(url: String) = this
|
||||
override fun passphrase(passphrase: String?) = this
|
||||
override fun proxy(url: String) = this
|
||||
override fun requestConfig(config: RequestConfig) = this
|
||||
override fun roomKeyRecipientStrategy(strategy: CollectStrategy) = this
|
||||
override fun serverName(serverName: String) = this
|
||||
override fun serverNameOrHomeserverUrl(serverNameOrUrl: String) = this
|
||||
override fun sessionPaths(dataPath: String, cachePath: String) = this
|
||||
override fun setSessionDelegate(sessionDelegate: ClientSessionDelegate) = this
|
||||
override fun slidingSyncVersionBuilder(versionBuilder: SlidingSyncVersionBuilder) = this
|
||||
override fun userAgent(userAgent: String) = this
|
||||
override fun username(username: String) = this
|
||||
|
||||
override suspend fun buildWithQrCode(qrCodeData: QrCodeData, oidcConfiguration: OidcConfiguration, progressListener: QrLoginProgressListener): Client {
|
||||
return FakeRustClient()
|
||||
}
|
||||
|
||||
override suspend fun build(): Client {
|
||||
return FakeRustClient()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.Encryption
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RecoveryStateListener
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
import org.matrix.rustcomponents.sdk.VerificationStateListener
|
||||
|
||||
class FakeRustEncryption : Encryption(NoPointer) {
|
||||
override fun verificationStateListener(listener: VerificationStateListener): TaskHandle {
|
||||
return FakeRustTaskHandle()
|
||||
}
|
||||
|
||||
override fun recoveryStateListener(listener: RecoveryStateListener): TaskHandle {
|
||||
return FakeRustTaskHandle()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.HomeserverLoginDetails
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
|
||||
class FakeRustHomeserverLoginDetails(
|
||||
private val url: String = "https://example.org",
|
||||
private val supportsPasswordLogin: Boolean = true,
|
||||
private val supportsOidcLogin: Boolean = false
|
||||
) : HomeserverLoginDetails(NoPointer) {
|
||||
override fun url(): String = url
|
||||
override fun supportsOidcLogin(): Boolean = supportsOidcLogin
|
||||
override fun supportsPasswordLogin(): Boolean = supportsPasswordLogin
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.NotificationClient
|
||||
import org.matrix.rustcomponents.sdk.NotificationItem
|
||||
|
||||
class FakeRustNotificationClient(
|
||||
var notificationItemResult: NotificationItem? = null
|
||||
) : NotificationClient(NoPointer) {
|
||||
override suspend fun getNotification(roomId: String, eventId: String): NotificationItem? = simulateLongTask {
|
||||
notificationItemResult
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomNotificationSettings
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.NotificationSettings
|
||||
import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationSettings
|
||||
|
||||
class FakeRustNotificationSettings(
|
||||
private val roomNotificationSettings: RoomNotificationSettings = aRustRoomNotificationSettings(),
|
||||
) : NotificationSettings(NoPointer) {
|
||||
private var delegate: NotificationSettingsDelegate? = null
|
||||
|
||||
override fun setDelegate(delegate: NotificationSettingsDelegate?) {
|
||||
this.delegate = delegate
|
||||
}
|
||||
|
||||
override suspend fun getRoomNotificationSettings(
|
||||
roomId: String,
|
||||
isEncrypted: Boolean,
|
||||
isOneToOne: Boolean,
|
||||
): RoomNotificationSettings = roomNotificationSettings
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RoomList
|
||||
|
||||
class FakeRustRoomList : RoomList(NoPointer)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RoomList
|
||||
import org.matrix.rustcomponents.sdk.RoomListService
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
|
||||
class FakeRustRoomListService : RoomListService(NoPointer) {
|
||||
override suspend fun allRooms(): RoomList {
|
||||
return FakeRustRoomList()
|
||||
}
|
||||
|
||||
private var listener: RoomListServiceSyncIndicatorListener? = null
|
||||
override fun syncIndicator(
|
||||
delayBeforeShowingInMs: UInt,
|
||||
delayBeforeHidingInMs: UInt,
|
||||
listener: RoomListServiceSyncIndicatorListener,
|
||||
): TaskHandle {
|
||||
this.listener = listener
|
||||
return FakeRustTaskHandle()
|
||||
}
|
||||
|
||||
fun emitRoomListServiceSyncIndicator(syncIndicator: RoomListServiceSyncIndicator) {
|
||||
listener?.onUpdate(syncIndicator)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RoomListService
|
||||
import org.matrix.rustcomponents.sdk.SyncService
|
||||
|
||||
class FakeRustSyncService(
|
||||
private val roomListService: RoomListService = FakeRustRoomListService(),
|
||||
) : SyncService(NoPointer) {
|
||||
override fun roomListService(): RoomListService = roomListService
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.SyncService
|
||||
import org.matrix.rustcomponents.sdk.SyncServiceBuilder
|
||||
import org.matrix.rustcomponents.sdk.UnableToDecryptDelegate
|
||||
|
||||
class FakeRustSyncServiceBuilder : SyncServiceBuilder(NoPointer) {
|
||||
override suspend fun withUtdHook(delegate: UnableToDecryptDelegate): SyncServiceBuilder = this
|
||||
override suspend fun finish(): SyncService = FakeRustSyncService()
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
|
||||
class FakeRustTaskHandle : TaskHandle(NoPointer) {
|
||||
override fun cancel() = Unit
|
||||
override fun destroy() = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustTimelineEventTypeMessageLike
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.TimelineEvent
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventType
|
||||
|
||||
class FakeRustTimelineEvent(
|
||||
val timestamp: ULong = A_FAKE_TIMESTAMP.toULong(),
|
||||
val timelineEventType: TimelineEventType = aRustTimelineEventTypeMessageLike(),
|
||||
val senderId: String = A_USER_ID_2.value,
|
||||
) : TimelineEvent(NoPointer) {
|
||||
override fun timestamp(): ULong = timestamp
|
||||
override fun eventType(): TimelineEventType = timelineEventType
|
||||
override fun senderId(): String = senderId
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
|
||||
class FakeRustTimelineEventTypeFilter : TimelineEventTypeFilter(NoPointer)
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
package io.element.android.libraries.matrix.impl.mapper
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
|
|
@ -81,4 +82,54 @@ class SessionKtTest {
|
|||
)
|
||||
assertThat(result.slidingSyncProxy).isEqualTo("proxyUrl")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ExternalSession toSessionData compute the expected result`() {
|
||||
val result = anExternalSession().toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.PASSWORD,
|
||||
passphrase = A_SECRET,
|
||||
sessionPaths = SessionPaths(File("/a/file"), File("/a/cache")),
|
||||
)
|
||||
assertThat(result.userId).isEqualTo(A_USER_ID.value)
|
||||
assertThat(result.deviceId).isEqualTo(A_DEVICE_ID.value)
|
||||
assertThat(result.accessToken).isEqualTo("accessToken")
|
||||
assertThat(result.refreshToken).isNull()
|
||||
assertThat(result.homeserverUrl).isEqualTo(A_HOMESERVER_URL)
|
||||
assertThat(result.isTokenValid).isTrue()
|
||||
assertThat(result.oidcData).isNull()
|
||||
assertThat(result.slidingSyncProxy).isNull()
|
||||
assertThat(result.loginType).isEqualTo(LoginType.PASSWORD)
|
||||
assertThat(result.loginTimestamp).isNotNull()
|
||||
assertThat(result.passphrase).isEqualTo(A_SECRET)
|
||||
assertThat(result.sessionPath).isEqualTo("/a/file")
|
||||
assertThat(result.cachePath).isEqualTo("/a/cache")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ExternalSession toSessionData can change the validity of the token`() {
|
||||
val result = anExternalSession().toSessionData(
|
||||
isTokenValid = false,
|
||||
loginType = LoginType.PASSWORD,
|
||||
passphrase = A_SECRET,
|
||||
sessionPaths = SessionPaths(File("/a/file"), File("/a/cache")),
|
||||
)
|
||||
assertThat(result.isTokenValid).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
private fun anExternalSession(
|
||||
userId: String = A_USER_ID.value,
|
||||
deviceId: String = A_DEVICE_ID.value,
|
||||
accessToken: String = "accessToken",
|
||||
refreshToken: String? = null,
|
||||
homeserverUrl: String = A_HOMESERVER_URL,
|
||||
slidingSyncProxy: String? = null,
|
||||
) = ExternalSession(
|
||||
userId = userId,
|
||||
deviceId = deviceId,
|
||||
accessToken = accessToken,
|
||||
refreshToken = refreshToken,
|
||||
homeserverUrl = homeserverUrl,
|
||||
slidingSyncProxy = slidingSyncProxy,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.notification
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustNotificationItem
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustNotificationClient
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_MESSAGE
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.NotificationClient
|
||||
|
||||
class RustNotificationServiceTest {
|
||||
@Test
|
||||
fun test() = runTest {
|
||||
val notificationClient = FakeRustNotificationClient(
|
||||
notificationItemResult = aRustNotificationItem(),
|
||||
)
|
||||
val sut = createRustNotificationService(
|
||||
notificationClient = notificationClient,
|
||||
)
|
||||
val result = sut.getNotification(A_ROOM_ID, AN_EVENT_ID).getOrThrow()!!
|
||||
assertThat(result.isEncrypted).isTrue()
|
||||
assertThat(result.content).isEqualTo(
|
||||
NotificationContent.MessageLike.RoomMessage(
|
||||
senderId = A_USER_ID_2,
|
||||
messageType = TextMessageType(
|
||||
body = A_MESSAGE,
|
||||
formatted = null,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestScope.createRustNotificationService(
|
||||
notificationClient: NotificationClient = FakeRustNotificationClient(),
|
||||
clock: SystemClock = FakeSystemClock(),
|
||||
) =
|
||||
RustNotificationService(
|
||||
notificationClient = notificationClient,
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
clock = clock,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.notificationsettings
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustNotificationSettings
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.NotificationSettings
|
||||
|
||||
class RustNotificationSettingsServiceTest {
|
||||
@Test
|
||||
fun test() = runTest {
|
||||
val sut = createRustNotificationSettingsService()
|
||||
val result = sut.getRoomNotificationSettings(
|
||||
roomId = A_ROOM_ID,
|
||||
isEncrypted = true,
|
||||
isOneToOne = true,
|
||||
).getOrNull()!!
|
||||
assertThat(result.mode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
|
||||
assertThat(result.isDefault).isTrue()
|
||||
}
|
||||
|
||||
private fun TestScope.createRustNotificationSettingsService(
|
||||
notificationSettings: NotificationSettings = FakeRustNotificationSettings(),
|
||||
) = RustNotificationSettingsService(
|
||||
client = FakeRustClient(
|
||||
notificationSettings = notificationSettings,
|
||||
),
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.oidc
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.AccountManagementAction as RustAccountManagementAction
|
||||
|
||||
class AccountManagementActionKtTest {
|
||||
@Test
|
||||
fun `test AccountManagementAction to RustAccountManagementAction`() {
|
||||
assertThat(AccountManagementAction.Profile.toRustAction())
|
||||
.isEqualTo(RustAccountManagementAction.Profile)
|
||||
assertThat(AccountManagementAction.SessionEnd(A_DEVICE_ID).toRustAction())
|
||||
.isEqualTo(RustAccountManagementAction.SessionEnd(A_DEVICE_ID.value))
|
||||
assertThat(AccountManagementAction.SessionView(A_DEVICE_ID).toRustAction())
|
||||
.isEqualTo(RustAccountManagementAction.SessionView(A_DEVICE_ID.value))
|
||||
assertThat(AccountManagementAction.SessionsList.toRustAction())
|
||||
.isEqualTo(RustAccountManagementAction.SessionsList)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.pushers
|
||||
|
||||
import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
|
||||
import io.element.android.libraries.matrix.api.pusher.UnsetHttpPusherData
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class RustPushersServiceTest {
|
||||
@Test
|
||||
fun `setPusher should invoke the client method`() = runTest {
|
||||
val sut = RustPushersService(
|
||||
client = FakeRustClient(),
|
||||
dispatchers = testCoroutineDispatchers()
|
||||
)
|
||||
sut.setHttpPusher(
|
||||
setHttpPusherData = aSetHttpPusherData()
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unsetPusher should invoke the client method`() = runTest {
|
||||
val sut = RustPushersService(
|
||||
client = FakeRustClient(),
|
||||
dispatchers = testCoroutineDispatchers()
|
||||
)
|
||||
sut.unsetHttpPusher(
|
||||
unsetHttpPusherData = aUnsetHttpPusherData(),
|
||||
).getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun aSetHttpPusherData(
|
||||
pushKey: String = "pushKey",
|
||||
appId: String = "appId",
|
||||
url: String = "url",
|
||||
defaultPayload: String = "defaultPayload",
|
||||
appDisplayName: String = "appDisplayName",
|
||||
deviceDisplayName: String = "deviceDisplayName",
|
||||
profileTag: String = "profileTag",
|
||||
lang: String = "lang",
|
||||
) = SetHttpPusherData(
|
||||
pushKey = pushKey,
|
||||
appId = appId,
|
||||
url = url,
|
||||
defaultPayload = defaultPayload,
|
||||
appDisplayName = appDisplayName,
|
||||
deviceDisplayName = deviceDisplayName,
|
||||
profileTag = profileTag,
|
||||
lang = lang
|
||||
)
|
||||
|
||||
private fun aUnsetHttpPusherData(
|
||||
pushKey: String = "pushKey",
|
||||
appId: String = "appId",
|
||||
) = UnsetHttpPusherData(
|
||||
pushKey = pushKey,
|
||||
appId = appId,
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineEventTypeFilter
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
|
||||
class FakeTimelineEventTypeFilterFactory : TimelineEventTypeFilterFactory {
|
||||
override fun create(listStateEventType: List<StateEventType>): TimelineEventTypeFilter {
|
||||
return FakeRustTimelineEventTypeFilter()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomdirectory
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomDescription
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntryUpdate
|
||||
|
||||
class RoomDirectorySearchProcessorTest {
|
||||
private val rustRoom1 = aRustRoomDescription(roomId = A_ROOM_ID.value)
|
||||
private val rustRoom2 = aRustRoomDescription(roomId = A_ROOM_ID_2.value)
|
||||
private val rustRoom3 = aRustRoomDescription(roomId = A_ROOM_ID_3.value)
|
||||
private val mapper = RoomDescriptionMapper()
|
||||
private val room1 = mapper.map(rustRoom1)
|
||||
private val room2 = mapper.map(rustRoom2)
|
||||
private val room3 = mapper.map(rustRoom3)
|
||||
|
||||
@Test
|
||||
fun test() = runTest {
|
||||
val sut = RoomDirectorySearchProcessor(
|
||||
coroutineContext = StandardTestDispatcher(testScheduler),
|
||||
)
|
||||
sut.roomDescriptionsFlow.test {
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Reset(listOf(rustRoom1))))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Append(listOf(rustRoom2))))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.PushFront(rustRoom3)))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room3, room1, room2))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.PopFront))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.PushBack(rustRoom3)))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2, room3))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.PopBack))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Insert(1u, rustRoom3)))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room3, room2))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Remove(1u)))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2))
|
||||
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Reset(listOf(rustRoom1, rustRoom2))))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Set(1u, rustRoom3)))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room3))
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Truncate(1u)))
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1))
|
||||
|
||||
sut.postUpdates(listOf(RoomDirectorySearchEntryUpdate.Clear))
|
||||
assertThat(awaitItem()).isEmpty()
|
||||
|
||||
// Check that all the actions are performed
|
||||
sut.postUpdates(
|
||||
listOf(
|
||||
RoomDirectorySearchEntryUpdate.PushBack(rustRoom1),
|
||||
RoomDirectorySearchEntryUpdate.PushBack(rustRoom2),
|
||||
RoomDirectorySearchEntryUpdate.PushBack(rustRoom3),
|
||||
)
|
||||
)
|
||||
assertThat(awaitItem()).isEqualTo(listOf(room1, room2, room3))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomdirectory
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomDescription
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRoomDirectorySearch
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.tests.testutils.runCancellableScopeTestWithTestScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearch
|
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntryUpdate
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RustRoomDirectoryListTest {
|
||||
@Test
|
||||
fun `check that the state emits the expected values`() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||
val fakeRoomDirectorySearch = FakeRoomDirectorySearch()
|
||||
val mapper = RoomDescriptionMapper()
|
||||
val sut = testScope.createRustRoomDirectoryList(
|
||||
roomDirectorySearch = fakeRoomDirectorySearch,
|
||||
scope = cancellableScope,
|
||||
)
|
||||
// Let the mxCallback be ready
|
||||
testScope.runCurrent()
|
||||
sut.state.test {
|
||||
sut.filter("", 20)
|
||||
fakeRoomDirectorySearch.emitResult(
|
||||
listOf(
|
||||
RoomDirectorySearchEntryUpdate.Append(listOf(aRustRoomDescription()))
|
||||
)
|
||||
)
|
||||
val initialItem = awaitItem()
|
||||
assertThat(initialItem).isEqualTo(
|
||||
RoomDirectoryList.State(
|
||||
hasMoreToLoad = true,
|
||||
items = listOf(mapper.map(aRustRoomDescription()))
|
||||
)
|
||||
)
|
||||
assertThat(initialItem.hasMoreToLoad).isTrue()
|
||||
fakeRoomDirectorySearch.isAtLastPage = true
|
||||
sut.loadMore()
|
||||
fakeRoomDirectorySearch.emitResult(
|
||||
listOf(
|
||||
RoomDirectorySearchEntryUpdate.Append(listOf(aRustRoomDescription(A_ROOM_ID_2.value)))
|
||||
)
|
||||
)
|
||||
val nextItem = awaitItem()
|
||||
assertThat(nextItem).isEqualTo(
|
||||
RoomDirectoryList.State(
|
||||
hasMoreToLoad = false,
|
||||
items = listOf(
|
||||
mapper.map(aRustRoomDescription()),
|
||||
)
|
||||
)
|
||||
)
|
||||
val finalItem = awaitItem()
|
||||
assertThat(finalItem).isEqualTo(
|
||||
RoomDirectoryList.State(
|
||||
hasMoreToLoad = false,
|
||||
items = listOf(
|
||||
mapper.map(aRustRoomDescription()),
|
||||
mapper.map(aRustRoomDescription(A_ROOM_ID_2.value)),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createRustRoomDirectoryList(
|
||||
roomDirectorySearch: RoomDirectorySearch = FakeRoomDirectorySearch(),
|
||||
scope: CoroutineScope,
|
||||
) = RustRoomDirectoryList(
|
||||
inner = roomDirectorySearch,
|
||||
coroutineScope = scope,
|
||||
coroutineContext = StandardTestDispatcher(testScheduler),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustClient
|
||||
import io.element.android.tests.testutils.runCancellableScopeTestWithTestScope
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import org.junit.Test
|
||||
|
||||
class RustRoomDirectoryServiceTest {
|
||||
@Test
|
||||
fun test() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||
val client = FakeRustClient()
|
||||
val sut = RustRoomDirectoryService(
|
||||
client = client,
|
||||
sessionDispatcher = StandardTestDispatcher(testScope.testScheduler),
|
||||
)
|
||||
sut.createRoomDirectoryList(cancellableScope)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomList
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
|
||||
import io.element.android.tests.testutils.runCancellableScopeTest
|
||||
import org.junit.Test
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
class RoomListFactoryTest {
|
||||
@Test
|
||||
fun `createRoomList should work`() = runCancellableScopeTest {
|
||||
val sut = RoomListFactory(
|
||||
innerRoomListService = FakeRustRoomListService(),
|
||||
sessionCoroutineScope = it,
|
||||
)
|
||||
sut.createRoomList(
|
||||
pageSize = 10,
|
||||
coroutineContext = EmptyCoroutineContext,
|
||||
) {
|
||||
FakeRustRoomList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.sun.jna.Pointer
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListItem
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
|
||||
|
|
@ -21,17 +21,8 @@ import kotlinx.coroutines.test.StandardTestDispatcher
|
|||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.RoomList
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
|
||||
// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
|
||||
// Since we don't access the data in those objects, this is fine for our tests, but that's as far as we can test this class.
|
||||
class RoomSummaryListProcessorTest {
|
||||
private val summaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||
|
||||
|
|
@ -163,29 +154,8 @@ class RoomSummaryListProcessorTest {
|
|||
|
||||
private fun TestScope.createProcessor() = RoomSummaryListProcessor(
|
||||
summaries,
|
||||
fakeRoomListService,
|
||||
FakeRustRoomListService(),
|
||||
coroutineContext = StandardTestDispatcher(testScheduler),
|
||||
roomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||
)
|
||||
|
||||
// Fake room list service that returns Rust objects with null pointers. Luckily for us, they don't crash for our test cases
|
||||
private val fakeRoomListService = object : RoomListServiceInterface {
|
||||
override suspend fun allRooms(): RoomList {
|
||||
return RoomList(Pointer.NULL)
|
||||
}
|
||||
|
||||
override fun room(roomId: String): RoomListItem {
|
||||
return RoomListItem(Pointer.NULL)
|
||||
}
|
||||
|
||||
override fun state(listener: RoomListServiceStateListener): TaskHandle {
|
||||
return TaskHandle(Pointer.NULL)
|
||||
}
|
||||
|
||||
override fun syncIndicator(delayBeforeShowingInMs: UInt, delayBeforeHidingInMs: UInt, listener: RoomListServiceSyncIndicatorListener): TaskHandle {
|
||||
return TaskHandle(Pointer.NULL)
|
||||
}
|
||||
|
||||
override fun subscribeToRooms(roomIds: List<String>, settings: RoomSubscription?) = Unit
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
|
||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||
import io.element.android.tests.testutils.runCancellableScopeTestWithTestScope
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as RustRoomListService
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RustRoomListServiceTest {
|
||||
@Test
|
||||
fun `syncIndicator should emit the expected values`() = runCancellableScopeTestWithTestScope { testScope, cancellableScope ->
|
||||
val roomListService = FakeRustRoomListService()
|
||||
val sut = testScope.createRustRoomListService(
|
||||
sessionCoroutineScope = cancellableScope,
|
||||
roomListService = roomListService,
|
||||
)
|
||||
// Give time for mxCallback to setup
|
||||
testScope.runCurrent()
|
||||
sut.syncIndicator.test {
|
||||
assertThat(awaitItem()).isEqualTo(RoomListService.SyncIndicator.Hide)
|
||||
roomListService.emitRoomListServiceSyncIndicator(RoomListServiceSyncIndicator.SHOW)
|
||||
assertThat(awaitItem()).isEqualTo(RoomListService.SyncIndicator.Show)
|
||||
roomListService.emitRoomListServiceSyncIndicator(RoomListServiceSyncIndicator.HIDE)
|
||||
assertThat(awaitItem()).isEqualTo(RoomListService.SyncIndicator.Hide)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.createRustRoomListService(
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
roomListService: RustRoomListService = FakeRustRoomListService(),
|
||||
) = RustRoomListService(
|
||||
innerRoomListService = roomListService,
|
||||
sessionDispatcher = StandardTestDispatcher(testScheduler),
|
||||
roomListFactory = RoomListFactory(
|
||||
innerRoomListService = roomListService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
),
|
||||
roomSyncSubscriber = RoomSyncSubscriber(
|
||||
roomListService = roomListService,
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
),
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
)
|
||||
|
|
@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
|||
|
||||
const val A_USER_NAME = "alice"
|
||||
const val A_PASSWORD = "password"
|
||||
const val A_PASSPHRASE = "passphrase"
|
||||
const val A_SECRET = "secret"
|
||||
|
||||
val A_USER_ID = UserId("@alice:server.org")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ 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.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
|
||||
|
|
@ -21,7 +20,6 @@ class FakeNotificationService : NotificationService {
|
|||
}
|
||||
|
||||
override suspend fun getNotification(
|
||||
userId: SessionId,
|
||||
roomId: RoomId,
|
||||
eventId: EventId,
|
||||
): Result<NotificationData?> {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.core.UserId
|
|||
import io.element.android.libraries.matrix.api.notification.NotificationContent
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationData
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
|
|
@ -76,7 +75,6 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
|||
val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return null
|
||||
val notificationService = client.notificationService()
|
||||
val notificationData = notificationService.getNotification(
|
||||
userId = sessionId,
|
||||
roomId = roomId,
|
||||
eventId = eventId,
|
||||
).onFailure {
|
||||
|
|
@ -113,30 +111,26 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
|||
hasMentionOrReply = hasMention,
|
||||
)
|
||||
}
|
||||
is NotificationContent.StateEvent.RoomMemberContent -> {
|
||||
if (content.membershipState == RoomMembershipState.INVITE) {
|
||||
InviteNotifiableEvent(
|
||||
sessionId = userId,
|
||||
roomId = roomId,
|
||||
eventId = eventId,
|
||||
editedEventId = null,
|
||||
canBeReplaced = true,
|
||||
roomName = roomDisplayName,
|
||||
noisy = isNoisy,
|
||||
timestamp = this.timestamp,
|
||||
soundName = null,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
description = descriptionFromRoomMembershipInvite(isDirect),
|
||||
// TODO check if type is needed anymore
|
||||
type = null,
|
||||
// TODO check if title is needed anymore
|
||||
title = null,
|
||||
)
|
||||
} else {
|
||||
Timber.tag(loggerTag.value).d("Ignoring notification state event for membership ${content.membershipState}")
|
||||
null
|
||||
}
|
||||
is NotificationContent.Invite -> {
|
||||
InviteNotifiableEvent(
|
||||
sessionId = userId,
|
||||
roomId = roomId,
|
||||
eventId = eventId,
|
||||
editedEventId = null,
|
||||
canBeReplaced = true,
|
||||
roomName = roomDisplayName,
|
||||
noisy = isNoisy,
|
||||
timestamp = this.timestamp,
|
||||
soundName = null,
|
||||
isRedacted = false,
|
||||
isUpdated = false,
|
||||
// TODO We could use the senderId here
|
||||
description = descriptionFromRoomMembershipInvite(isDirect),
|
||||
// TODO check if type is needed anymore
|
||||
type = null,
|
||||
// TODO check if title is needed anymore
|
||||
title = null,
|
||||
)
|
||||
}
|
||||
NotificationContent.MessageLike.CallAnswer,
|
||||
NotificationContent.MessageLike.CallCandidates,
|
||||
|
|
@ -203,6 +197,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
|
|||
NotificationContent.MessageLike.Sticker -> null.also {
|
||||
Timber.tag(loggerTag.value).d("Ignoring notification for sticker")
|
||||
}
|
||||
is NotificationContent.StateEvent.RoomMemberContent,
|
||||
NotificationContent.StateEvent.PolicyRuleRoom,
|
||||
NotificationContent.StateEvent.PolicyRuleServer,
|
||||
NotificationContent.StateEvent.PolicyRuleUser,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class DefaultOnMissedCallNotificationHandler @Inject constructor(
|
|||
// Resolve the event and add a notification for it, at this point it should no longer be a ringing one
|
||||
val notificationData = matrixClientProvider.getOrRestore(sessionId).getOrNull()
|
||||
?.notificationService()
|
||||
?.getNotification(sessionId, roomId, eventId)
|
||||
?.getNotification(roomId, eventId)
|
||||
?.getOrNull()
|
||||
?: return
|
||||
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ class DefaultNotifiableEventResolverTest {
|
|||
notificationResult = Result.success(
|
||||
createNotificationData(
|
||||
content = NotificationContent.StateEvent.RoomMemberContent(
|
||||
userId = A_USER_ID_2.value,
|
||||
userId = A_USER_ID_2,
|
||||
membershipState = RoomMembershipState.INVITE
|
||||
),
|
||||
isDirect = false,
|
||||
|
|
@ -378,6 +378,22 @@ class DefaultNotifiableEventResolverTest {
|
|||
)
|
||||
)
|
||||
val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID)
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve invite room`() = runTest {
|
||||
val sut = createDefaultNotifiableEventResolver(
|
||||
notificationResult = Result.success(
|
||||
createNotificationData(
|
||||
content = NotificationContent.Invite(
|
||||
senderId = A_USER_ID_2,
|
||||
),
|
||||
isDirect = false,
|
||||
)
|
||||
)
|
||||
)
|
||||
val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID)
|
||||
val expectedResult = ResolvedPushEvent.Event(
|
||||
InviteNotifiableEvent(
|
||||
sessionId = A_SESSION_ID,
|
||||
|
|
@ -400,13 +416,12 @@ class DefaultNotifiableEventResolverTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `resolve RoomMemberContent invite direct`() = runTest {
|
||||
fun `resolve invite invite direct`() = runTest {
|
||||
val sut = createDefaultNotifiableEventResolver(
|
||||
notificationResult = Result.success(
|
||||
createNotificationData(
|
||||
content = NotificationContent.StateEvent.RoomMemberContent(
|
||||
userId = A_USER_ID_2.value,
|
||||
membershipState = RoomMembershipState.INVITE
|
||||
content = NotificationContent.Invite(
|
||||
senderId = A_USER_ID_2,
|
||||
),
|
||||
isDirect = true,
|
||||
)
|
||||
|
|
@ -440,7 +455,7 @@ class DefaultNotifiableEventResolverTest {
|
|||
notificationResult = Result.success(
|
||||
createNotificationData(
|
||||
content = NotificationContent.StateEvent.RoomMemberContent(
|
||||
userId = A_USER_ID_2.value,
|
||||
userId = A_USER_ID_2,
|
||||
membershipState = RoomMembershipState.JOIN
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,12 +16,14 @@ fun aSessionData(
|
|||
isTokenValid: Boolean = false,
|
||||
sessionPath: String = "/a/path/to/a/session",
|
||||
cachePath: String = "/a/path/to/a/cache",
|
||||
): SessionData {
|
||||
accessToken: String = "anAccessToken",
|
||||
refreshToken: String? = "aRefreshToken",
|
||||
): SessionData {
|
||||
return SessionData(
|
||||
userId = sessionId,
|
||||
deviceId = deviceId,
|
||||
accessToken = "anAccessToken",
|
||||
refreshToken = "aRefreshToken",
|
||||
accessToken = accessToken,
|
||||
refreshToken = refreshToken,
|
||||
homeserverUrl = "aHomeserverUrl",
|
||||
oidcData = null,
|
||||
slidingSyncProxy = null,
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.core.view.WindowCompat
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.impl.RustClientBuilderProvider
|
||||
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
|
||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||
import io.element.android.libraries.matrix.impl.auth.OidcConfigurationProvider
|
||||
import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||
import io.element.android.libraries.matrix.impl.room.RustTimelineEventTypeFilterFactory
|
||||
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
||||
|
|
@ -56,6 +58,8 @@ class MainActivity : ComponentActivity() {
|
|||
clock = DefaultSystemClock(),
|
||||
utdTracker = UtdTracker(NoopAnalyticsService()),
|
||||
featureFlagService = AlwaysEnabledFeatureFlagService(),
|
||||
timelineEventTypeFilterFactory = RustTimelineEventTypeFilterFactory(),
|
||||
clientBuilderProvider = RustClientBuilderProvider(),
|
||||
),
|
||||
passphraseGenerator = NullPassphraseGenerator(),
|
||||
oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.tests.testutils
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
/**
|
||||
|
|
@ -20,3 +21,12 @@ fun runCancellableScopeTest(block: suspend (CoroutineScope) -> Unit) = runTest {
|
|||
block(scope)
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a test with a [CoroutineScope] that will be cancelled automatically and avoiding failing the test.
|
||||
*/
|
||||
fun runCancellableScopeTestWithTestScope(block: suspend (testScope: TestScope, cancellableScope: CoroutineScope) -> Unit) = runTest {
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
block(this, scope)
|
||||
scope.cancel()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue