Merge branch 'release/0.6.3' into main
This commit is contained in:
commit
cec945cd07
96 changed files with 2597 additions and 369 deletions
|
|
@ -1,3 +1,9 @@
|
|||
Changes in Element X v0.6.2 (2024-09-17)
|
||||
========================================
|
||||
|
||||
### ✨ Features
|
||||
* Account deactivation. by @bmarty in https://github.com/element-hq/element-x-android/pull/3479
|
||||
|
||||
Changes in Element X v0.6.1 (2024-09-17)
|
||||
========================================
|
||||
|
||||
|
|
|
|||
2
fastlane/metadata/android/en-US/changelogs/40006030.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40006030.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Element X is the new generation of Element for professional and personal use on mobile. It’s the fastest Matrix client with a seamless & intuitive user interface.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
|
|
@ -33,6 +33,7 @@ dependencies {
|
|||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.testtags)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
api(projects.features.deactivation.api)
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
|||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.designsystem.theme.components.autofill
|
||||
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -277,6 +279,7 @@ private fun Content(
|
|||
.padding(top = 8.dp)
|
||||
.fillMaxWidth()
|
||||
.onTabOrEnterKeyFocusNext(focusManager)
|
||||
.testTag(TestTags.loginPassword)
|
||||
.autofill(
|
||||
autofillTypes = listOf(AutofillType.Password),
|
||||
onFill = {
|
||||
|
|
|
|||
|
|
@ -10,15 +10,26 @@ package io.element.android.features.logout.impl
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.deactivation.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_PASSWORD
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import io.element.android.tests.testutils.pressTag
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AccountDeactivationViewTest {
|
||||
|
|
@ -36,7 +47,96 @@ class AccountDeactivationViewTest {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Add more tests
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on Deactivate emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
rule.setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
password = A_PASSWORD,
|
||||
),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_deactivate)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
rule.setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
password = A_PASSWORD,
|
||||
),
|
||||
accountDeactivationAction = AsyncAction.Confirming,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.pressTag(TestTags.dialogPositive.value)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on retry on the confirmation dialog emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
rule.setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
password = A_PASSWORD,
|
||||
),
|
||||
accountDeactivationAction = AsyncAction.Failure(AN_EXCEPTION),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_retry)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `switching on the erase all switch emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
rule.setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_deactivate_account_delete_all_messages)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `switching off the erase all switch emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
rule.setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
eraseData = true,
|
||||
),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_deactivate_account_delete_all_messages)
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(false))
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `typing text in the password field emits the expected Event`() {
|
||||
val eventsRecorder = EventsRecorder<AccountDeactivationEvents>()
|
||||
rule.setAccountDeactivationView(
|
||||
state = anAccountDeactivationState(
|
||||
deactivateFormState = aDeactivateFormState(
|
||||
password = A_PASSWORD,
|
||||
),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.onNodeWithTag(TestTags.loginPassword.value).performTextInput("A")
|
||||
eventsRecorder.assertSingle(AccountDeactivationEvents.SetPassword("A$A_PASSWORD"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAccountDeactivationView(
|
||||
|
|
|
|||
|
|
@ -374,7 +374,8 @@ private fun VerifiedUserSendFailureView(
|
|||
fun VerifiedUserSendFailure.headline(): String {
|
||||
return when (this) {
|
||||
is None -> ""
|
||||
is UnsignedDevice -> stringResource(CommonStrings.screen_timeline_item_menu_send_failure_unsigned_device, userDisplayName)
|
||||
is UnsignedDevice.FromOther -> stringResource(CommonStrings.screen_timeline_item_menu_send_failure_unsigned_device, userDisplayName)
|
||||
is UnsignedDevice.FromYou -> stringResource(CommonStrings.screen_timeline_item_menu_send_failure_you_unsigned_device)
|
||||
is ChangedIdentity -> stringResource(CommonStrings.screen_timeline_item_menu_send_failure_changed_identity, userDisplayName)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ import androidx.compose.runtime.Immutable
|
|||
sealed interface VerifiedUserSendFailure {
|
||||
data object None : VerifiedUserSendFailure
|
||||
|
||||
data class UnsignedDevice(
|
||||
val userDisplayName: String,
|
||||
) : VerifiedUserSendFailure
|
||||
sealed interface UnsignedDevice : VerifiedUserSendFailure {
|
||||
data object FromYou : UnsignedDevice
|
||||
data class FromOther(val userDisplayName: String) : UnsignedDevice
|
||||
}
|
||||
|
||||
data class ChangedIdentity(
|
||||
val userDisplayName: String,
|
||||
|
|
|
|||
|
|
@ -23,8 +23,12 @@ class VerifiedUserSendFailureFactory @Inject constructor(
|
|||
if (userId == null) {
|
||||
VerifiedUserSendFailure.None
|
||||
} else {
|
||||
val displayName = room.userDisplayName(userId).getOrNull() ?: userId.value
|
||||
VerifiedUserSendFailure.UnsignedDevice(displayName)
|
||||
if (userId == room.sessionId) {
|
||||
VerifiedUserSendFailure.UnsignedDevice.FromYou
|
||||
} else {
|
||||
val displayName = room.userDisplayName(userId).getOrNull() ?: userId.value
|
||||
VerifiedUserSendFailure.UnsignedDevice.FromOther(displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
is LocalEventSendState.Failed.VerifiedUserChangedIdentity -> {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ fun aResolveVerifiedUserSendFailureState(
|
|||
eventSink = eventSink
|
||||
)
|
||||
|
||||
fun anUnsignedDeviceSendFailure(userDisplayName: String = "Alice") = VerifiedUserSendFailure.UnsignedDevice(
|
||||
fun anUnsignedDeviceSendFailure(userDisplayName: String = "Alice") = VerifiedUserSendFailure.UnsignedDevice.FromOther(
|
||||
userDisplayName = userDisplayName,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -113,28 +113,33 @@ fun ResolveVerifiedUserSendFailureView(
|
|||
@Composable
|
||||
private fun VerifiedUserSendFailure.title(): String {
|
||||
return when (this) {
|
||||
is VerifiedUserSendFailure.UnsignedDevice -> stringResource(id = CommonStrings.screen_resolve_send_failure_unsigned_device_title, userDisplayName)
|
||||
is VerifiedUserSendFailure.UnsignedDevice.FromOther -> stringResource(
|
||||
id = CommonStrings.screen_resolve_send_failure_unsigned_device_title,
|
||||
userDisplayName
|
||||
)
|
||||
VerifiedUserSendFailure.UnsignedDevice.FromYou -> stringResource(id = CommonStrings.screen_resolve_send_failure_you_unsigned_device_title)
|
||||
is VerifiedUserSendFailure.ChangedIdentity -> stringResource(
|
||||
id = CommonStrings.screen_resolve_send_failure_changed_identity_title,
|
||||
userDisplayName
|
||||
)
|
||||
VerifiedUserSendFailure.None -> error("This method should never be called for this state")
|
||||
VerifiedUserSendFailure.None -> ""
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VerifiedUserSendFailure.subtitle(): String {
|
||||
return when (this) {
|
||||
is VerifiedUserSendFailure.UnsignedDevice -> stringResource(
|
||||
is VerifiedUserSendFailure.UnsignedDevice.FromOther -> stringResource(
|
||||
id = CommonStrings.screen_resolve_send_failure_unsigned_device_subtitle,
|
||||
userDisplayName,
|
||||
userDisplayName,
|
||||
)
|
||||
VerifiedUserSendFailure.UnsignedDevice.FromYou -> stringResource(id = CommonStrings.screen_resolve_send_failure_you_unsigned_device_subtitle)
|
||||
is VerifiedUserSendFailure.ChangedIdentity -> stringResource(
|
||||
id = CommonStrings.screen_resolve_send_failure_changed_identity_subtitle,
|
||||
userDisplayName
|
||||
)
|
||||
VerifiedUserSendFailure.None -> error("This method should never be called for this state")
|
||||
VerifiedUserSendFailure.None -> ""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +148,7 @@ private fun VerifiedUserSendFailure.resolveAction(): String {
|
|||
return when (this) {
|
||||
is VerifiedUserSendFailure.UnsignedDevice -> stringResource(id = CommonStrings.screen_resolve_send_failure_unsigned_device_primary_button_title)
|
||||
is VerifiedUserSendFailure.ChangedIdentity -> stringResource(id = CommonStrings.screen_resolve_send_failure_changed_identity_primary_button_title)
|
||||
VerifiedUserSendFailure.None -> error("This method should never be called for this state")
|
||||
VerifiedUserSendFailure.None -> ""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,14 +25,12 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class PinnedMessagesBannerPresenter @Inject constructor(
|
||||
private val room: MatrixRoom,
|
||||
|
|
@ -123,7 +121,6 @@ class PinnedMessagesBannerPresenter @Inject constructor(
|
|||
is AsyncData.Loading -> flowOf(AsyncData.Loading())
|
||||
is AsyncData.Success -> {
|
||||
asyncTimeline.data.timelineItems
|
||||
.debounce(300.milliseconds)
|
||||
.map { timelineItems ->
|
||||
val pinnedItems = timelineItems.mapNotNull { timelineItem ->
|
||||
itemFactory.create(timelineItem)
|
||||
|
|
|
|||
|
|
@ -43,14 +43,12 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class PinnedMessagesListPresenter @AssistedInject constructor(
|
||||
@Assisted private val navigator: PinnedMessagesListNavigator,
|
||||
|
|
@ -174,7 +172,7 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
|
|||
is AsyncData.Failure -> flowOf(AsyncData.Failure(asyncTimeline.error))
|
||||
is AsyncData.Loading -> flowOf(AsyncData.Loading())
|
||||
is AsyncData.Success -> {
|
||||
val timelineItemsFlow = asyncTimeline.data.timelineItems.debounce(300.milliseconds)
|
||||
val timelineItemsFlow = asyncTimeline.data.timelineItems
|
||||
combine(timelineItemsFlow, room.membersStateFlow) { items, membersState ->
|
||||
timelineItemsFactory.replaceWith(
|
||||
timelineItems = items,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -81,6 +82,7 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
computeReactions = true,
|
||||
)
|
||||
)
|
||||
private var timelineItems by mutableStateOf<ImmutableList<TimelineItem>>(persistentListOf())
|
||||
|
||||
@Composable
|
||||
override fun present(): TimelineState {
|
||||
|
|
@ -89,9 +91,12 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
mutableStateOf(FocusRequestState.None)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
timelineItemsFactory.timelineItems.collect { timelineItems = it }
|
||||
}
|
||||
|
||||
val lastReadReceiptId = rememberSaveable { mutableStateOf<EventId?>(null) }
|
||||
|
||||
val timelineItems by timelineItemsFactory.timelineItems.collectAsState(initial = persistentListOf())
|
||||
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
|
||||
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
|||
initialState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(failedMessage))
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value))
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Dismiss)
|
||||
}
|
||||
skipItems(1)
|
||||
|
|
@ -124,7 +124,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
|||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value))
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.Retry)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
|
|
@ -158,7 +158,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
|||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value))
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
|
|
@ -167,7 +167,7 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
|||
// This should move to the next user
|
||||
skipItems(2)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID_2.value))
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromOther(A_USER_ID_2.value))
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
}
|
||||
|
|
@ -199,14 +199,14 @@ class ResolveVerifiedUserSendFailurePresenterTest {
|
|||
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value))
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
state.eventSink(ResolveVerifiedUserSendFailureEvents.ResolveAndResend)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.resolveAction).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice(A_USER_ID.value))
|
||||
assertThat(state.verifiedUserSendFailure).isEqualTo(VerifiedUserSendFailure.UnsignedDevice.FromYou)
|
||||
assertThat(state.resolveAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
}
|
||||
ensureAllEventsConsumed()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class AppMigration05Test {
|
|||
val sessionStore = InMemorySessionStore().apply {
|
||||
updateData(
|
||||
aSessionData(
|
||||
sessionId = A_SESSION_ID,
|
||||
sessionId = A_SESSION_ID.value,
|
||||
sessionPath = "",
|
||||
)
|
||||
)
|
||||
|
|
@ -37,7 +37,7 @@ class AppMigration05Test {
|
|||
val sessionStore = InMemorySessionStore().apply {
|
||||
updateData(
|
||||
aSessionData(
|
||||
sessionId = A_SESSION_ID,
|
||||
sessionId = A_SESSION_ID.value,
|
||||
sessionPath = "/a/path/existing",
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class AppMigration06Test {
|
|||
val sessionStore = InMemorySessionStore().apply {
|
||||
updateData(
|
||||
aSessionData(
|
||||
sessionId = A_SESSION_ID,
|
||||
sessionId = A_SESSION_ID.value,
|
||||
sessionPath = "/a/path/to/a/session/AN_ID",
|
||||
cachePath = "",
|
||||
)
|
||||
|
|
@ -38,7 +38,7 @@ class AppMigration06Test {
|
|||
val sessionStore = InMemorySessionStore().apply {
|
||||
updateData(
|
||||
aSessionData(
|
||||
sessionId = A_SESSION_ID,
|
||||
sessionId = A_SESSION_ID.value,
|
||||
cachePath = "/a/path/existing",
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ dependencies {
|
|||
testImplementation(libs.test.mockk)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.sessionStorage.implMemory)
|
||||
testImplementation(projects.libraries.sessionStorage.test)
|
||||
testImplementation(projects.features.rageshake.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@ import io.element.android.libraries.matrix.test.FakeSdkMetadata
|
|||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.network.useragent.DefaultUserAgentProvider
|
||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
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
|
||||
|
|
@ -94,7 +93,7 @@ class DefaultBugReporterTest {
|
|||
server.start()
|
||||
|
||||
val mockSessionStore = InMemorySessionStore().apply {
|
||||
storeData(mockSessionData("@foo:eample.com", "ABCDEFGH"))
|
||||
storeData(aSessionData(sessionId = "@foo:example.com", deviceId = "ABCDEFGH"))
|
||||
}
|
||||
|
||||
val buildMeta = aBuildMeta()
|
||||
|
|
@ -143,7 +142,7 @@ class DefaultBugReporterTest {
|
|||
assertThat(foundValues["can_contact"]).isEqualTo("true")
|
||||
assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH")
|
||||
assertThat(foundValues["sdk_sha"]).isEqualTo("123456789")
|
||||
assertThat(foundValues["user_id"]).isEqualTo("@foo:eample.com")
|
||||
assertThat(foundValues["user_id"]).isEqualTo("@foo:example.com")
|
||||
assertThat(foundValues["text"]).isEqualTo("a bug occurred")
|
||||
assertThat(foundValues["device_keys"]).isEqualTo("curve25519:CURVECURVECURVE, ed25519:EDKEYEDKEYEDKY")
|
||||
|
||||
|
|
@ -163,7 +162,7 @@ class DefaultBugReporterTest {
|
|||
server.start()
|
||||
|
||||
val mockSessionStore = InMemorySessionStore().apply {
|
||||
storeData(mockSessionData("@foo:eample.com", "ABCDEFGH"))
|
||||
storeData(aSessionData("@foo:example.com", "ABCDEFGH"))
|
||||
}
|
||||
|
||||
val buildMeta = aBuildMeta()
|
||||
|
|
@ -267,21 +266,6 @@ class DefaultBugReporterTest {
|
|||
return foundValues
|
||||
}
|
||||
|
||||
private fun mockSessionData(userId: String, deviceId: String) = SessionData(
|
||||
userId = userId,
|
||||
deviceId = deviceId,
|
||||
homeserverUrl = "example.com",
|
||||
accessToken = "AA",
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.DIRECT,
|
||||
loginTimestamp = null,
|
||||
oidcData = null,
|
||||
refreshToken = null,
|
||||
slidingSyncProxy = null,
|
||||
passphrase = null,
|
||||
sessionPath = "session",
|
||||
cachePath = "cache",
|
||||
)
|
||||
@Test
|
||||
fun `test sendBugReport error`() = runTest {
|
||||
val server = MockWebServer()
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
|
@ -73,6 +73,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
private const val EXTENDED_RANGE_SIZE = 40
|
||||
private const val SUBSCRIBE_TO_VISIBLE_ROOMS_DEBOUNCE_IN_MILLIS = 300L
|
||||
|
||||
class RoomListPresenter @Inject constructor(
|
||||
private val client: MatrixClient,
|
||||
|
|
@ -301,7 +302,10 @@ class RoomListPresenter @Inject constructor(
|
|||
private var currentUpdateVisibleRangeJob: Job? = null
|
||||
private fun CoroutineScope.updateVisibleRange(range: IntRange) {
|
||||
currentUpdateVisibleRangeJob?.cancel()
|
||||
currentUpdateVisibleRangeJob = launch(SupervisorJob()) {
|
||||
currentUpdateVisibleRangeJob = launch {
|
||||
// Debounce the subscription to avoid subscribing to too many rooms
|
||||
delay(SUBSCRIBE_TO_VISIBLE_ROOMS_DEBOUNCE_IN_MILLIS)
|
||||
|
||||
if (range.isEmpty()) return@launch
|
||||
val currentRoomList = roomListDataSource.allRooms.first()
|
||||
// Use extended range to 'prefetch' the next rooms info
|
||||
|
|
|
|||
|
|
@ -85,13 +85,16 @@ import io.element.android.tests.testutils.lambda.value
|
|||
import io.element.android.tests.testutils.test
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class RoomListPresenterTest {
|
||||
@get:Rule
|
||||
|
|
@ -599,6 +602,38 @@ class RoomListPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `present - UpdateVisibleRange will cancel the previous subscription if called too soon`() = runTest {
|
||||
val subscribeToVisibleRoomsLambda = lambdaRecorder { _: List<RoomId> -> }
|
||||
val roomListService = FakeRoomListService(subscribeToVisibleRoomsLambda = subscribeToVisibleRoomsLambda)
|
||||
val scope = CoroutineScope(coroutineContext + SupervisorJob())
|
||||
val matrixClient = FakeMatrixClient(
|
||||
roomListService = roomListService,
|
||||
)
|
||||
val roomSummary = aRoomSummary(
|
||||
currentUserMembership = CurrentUserMembership.INVITED
|
||||
)
|
||||
roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1))
|
||||
roomListService.postAllRooms(listOf(roomSummary))
|
||||
val presenter = createRoomListPresenter(
|
||||
coroutineScope = scope,
|
||||
client = matrixClient,
|
||||
)
|
||||
presenter.test {
|
||||
val state = consumeItemsUntilPredicate {
|
||||
it.contentState is RoomListContentState.Rooms
|
||||
}.last()
|
||||
|
||||
state.eventSink(RoomListEvents.UpdateVisibleRange(IntRange(0, 10)))
|
||||
// If called again, it will cancel the current one, which should not result in a test failure
|
||||
state.eventSink(RoomListEvents.UpdateVisibleRange(IntRange(0, 11)))
|
||||
advanceTimeBy(1.seconds)
|
||||
subscribeToVisibleRoomsLambda.assertions().isCalledOnce()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `present - UpdateVisibleRange subscribes to rooms in visible range`() = runTest {
|
||||
val subscribeToVisibleRoomsLambda = lambdaRecorder { _: List<RoomId> -> }
|
||||
|
|
@ -622,10 +657,12 @@ class RoomListPresenterTest {
|
|||
}.last()
|
||||
|
||||
state.eventSink(RoomListEvents.UpdateVisibleRange(IntRange(0, 10)))
|
||||
advanceTimeBy(1.seconds)
|
||||
subscribeToVisibleRoomsLambda.assertions().isCalledOnce()
|
||||
|
||||
// If called again, it will cancel the current one, which should not result in a test failure
|
||||
// If called again, it will subscribe to the next items
|
||||
state.eventSink(RoomListEvents.UpdateVisibleRange(IntRange(0, 11)))
|
||||
advanceTimeBy(1.seconds)
|
||||
subscribeToVisibleRoomsLambda.assertions().isCalledExactly(2)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,5 +37,6 @@ dependencies {
|
|||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.sessionStorage.implMemory)
|
||||
testImplementation(projects.libraries.sessionStorage.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
package io.element.android.features.signedout.impl
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
|
||||
|
|
@ -20,18 +19,18 @@ open class SignedOutStateProvider : PreviewParameterProvider<SignedOutState> {
|
|||
)
|
||||
}
|
||||
|
||||
fun aSignedOutState() = SignedOutState(
|
||||
private fun aSignedOutState() = SignedOutState(
|
||||
appName = "AppName",
|
||||
signedOutSession = aSessionData(),
|
||||
eventSink = {},
|
||||
)
|
||||
|
||||
fun aSessionData(
|
||||
sessionId: SessionId = SessionId("@alice:server.org"),
|
||||
private fun aSessionData(
|
||||
sessionId: String = "@alice:server.org",
|
||||
isTokenValid: Boolean = false,
|
||||
): SessionData {
|
||||
return SessionData(
|
||||
userId = sessionId.value,
|
||||
userId = sessionId,
|
||||
deviceId = "aDeviceId",
|
||||
accessToken = "anAccessToken",
|
||||
refreshToken = "aRefreshToken",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
|||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
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.WarmUpRule
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ jsoup = "org.jsoup:jsoup:1.18.1"
|
|||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.46"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.47"
|
||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||
|
|
|
|||
|
|
@ -149,7 +149,12 @@ object MatrixPatterns {
|
|||
add(MatrixPatternResult(MatrixPatternType.USER_ID, permalink.userId.toString(), match.range.first, match.range.last + 1))
|
||||
}
|
||||
is PermalinkData.RoomLink -> {
|
||||
add(MatrixPatternResult(MatrixPatternType.ROOM_ALIAS, permalink.roomIdOrAlias.identifier, match.range.first, match.range.last + 1))
|
||||
when (permalink.roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> MatrixPatternType.ROOM_ALIAS
|
||||
is RoomIdOrAlias.Id -> if (permalink.eventId == null) MatrixPatternType.ROOM_ID else null
|
||||
}?.let { type ->
|
||||
add(MatrixPatternResult(type, permalink.roomIdOrAlias.identifier, match.range.first, match.range.last + 1))
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ data class MatrixRoomInfo(
|
|||
val isTombstoned: Boolean,
|
||||
val isFavorite: Boolean,
|
||||
val canonicalAlias: RoomAlias?,
|
||||
val alternativeAliases: ImmutableList<String>,
|
||||
val alternativeAliases: ImmutableList<RoomAlias>,
|
||||
val currentUserMembership: CurrentUserMembership,
|
||||
val inviter: RoomMember?,
|
||||
val activeMembersCount: Long,
|
||||
|
|
@ -42,7 +42,7 @@ data class MatrixRoomInfo(
|
|||
val notificationCount: Long,
|
||||
val userDefinedNotificationMode: RoomNotificationMode?,
|
||||
val hasRoomCall: Boolean,
|
||||
val activeRoomCallParticipants: ImmutableList<String>,
|
||||
val activeRoomCallParticipants: ImmutableList<UserId>,
|
||||
val heroes: ImmutableList<MatrixUser>,
|
||||
val pinnedEventIds: ImmutableList<EventId>,
|
||||
val creator: UserId?,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ dependencies {
|
|||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.sessionStorage.implMemory)
|
||||
testImplementation(projects.libraries.sessionStorage.test)
|
||||
testImplementation(projects.services.analytics.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.turbine)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ class RustClientSessionDelegate(
|
|||
*/
|
||||
fun bindClient(client: RustMatrixClient) {
|
||||
this.client = client
|
||||
client.setDelegate(this)
|
||||
}
|
||||
|
||||
override fun saveSessionInKeychain(session: Session) {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class RustMatrixClient(
|
|||
private val baseDirectory: File,
|
||||
baseCacheDirectory: File,
|
||||
private val clock: SystemClock,
|
||||
sessionDelegate: RustClientSessionDelegate,
|
||||
private val sessionDelegate: RustClientSessionDelegate,
|
||||
) : MatrixClient {
|
||||
override val sessionId: UserId = UserId(client.userId())
|
||||
override val deviceId: DeviceId = DeviceId(client.deviceId())
|
||||
|
|
@ -195,7 +195,7 @@ class RustMatrixClient(
|
|||
|
||||
private val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
||||
private val clientDelegateTaskHandle: TaskHandle? = client.setDelegate(sessionDelegate)
|
||||
private var clientDelegateTaskHandle: TaskHandle? = client.setDelegate(sessionDelegate)
|
||||
|
||||
private val _userProfile: MutableStateFlow<MatrixUser> = MutableStateFlow(
|
||||
MatrixUser(
|
||||
|
|
@ -449,12 +449,12 @@ class RustMatrixClient(
|
|||
override fun close() {
|
||||
appCoroutineScope.launch {
|
||||
roomFactory.destroy()
|
||||
rustSyncService.destroy()
|
||||
}
|
||||
sessionCoroutineScope.cancel()
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
notificationSettingsService.destroy()
|
||||
verificationService.destroy()
|
||||
syncService.destroy()
|
||||
innerRoomListService.destroy()
|
||||
notificationClient.destroy()
|
||||
notificationProcessSetup.destroy()
|
||||
|
|
@ -473,7 +473,9 @@ class RustMatrixClient(
|
|||
|
||||
override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean): String? {
|
||||
var result: String? = null
|
||||
syncService.stop()
|
||||
// Remove current delegate so we don't receive an auth error
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
clientDelegateTaskHandle = null
|
||||
withContext(sessionDispatcher) {
|
||||
if (userInitiated) {
|
||||
try {
|
||||
|
|
@ -482,12 +484,15 @@ class RustMatrixClient(
|
|||
if (ignoreSdkError) {
|
||||
Timber.e(failure, "Fail to call logout on HS. Still delete local files.")
|
||||
} else {
|
||||
// If the logout failed we need to restore the delegate
|
||||
clientDelegateTaskHandle = client.setDelegate(sessionDelegate)
|
||||
Timber.e(failure, "Fail to call logout on HS.")
|
||||
throw failure
|
||||
}
|
||||
}
|
||||
}
|
||||
close()
|
||||
|
||||
deleteSessionDirectory(deleteCryptoDb = true)
|
||||
if (userInitiated) {
|
||||
sessionStore.removeSession(sessionId.value)
|
||||
|
|
@ -506,7 +511,9 @@ class RustMatrixClient(
|
|||
|
||||
override suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit> = withContext(sessionDispatcher) {
|
||||
Timber.w("Deactivating account")
|
||||
syncService.stop()
|
||||
// Remove current delegate so we don't receive an auth error
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
clientDelegateTaskHandle = null
|
||||
runCatching {
|
||||
// First call without AuthData, should fail
|
||||
val firstAttempt = runCatching {
|
||||
|
|
@ -518,15 +525,22 @@ class RustMatrixClient(
|
|||
if (firstAttempt.isFailure) {
|
||||
Timber.w(firstAttempt.exceptionOrNull(), "Expected failure, try again")
|
||||
// This is expected, try again with the password
|
||||
client.deactivateAccount(
|
||||
authData = AuthData.Password(
|
||||
passwordDetails = AuthDataPasswordDetails(
|
||||
identifier = sessionId.value,
|
||||
password = password,
|
||||
runCatching {
|
||||
client.deactivateAccount(
|
||||
authData = AuthData.Password(
|
||||
passwordDetails = AuthDataPasswordDetails(
|
||||
identifier = sessionId.value,
|
||||
password = password,
|
||||
),
|
||||
),
|
||||
),
|
||||
eraseData = eraseData,
|
||||
)
|
||||
eraseData = eraseData,
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Failed to deactivate account")
|
||||
// If the deactivation failed we need to restore the delegate
|
||||
clientDelegateTaskHandle = client.setDelegate(sessionDelegate)
|
||||
throw it
|
||||
}
|
||||
}
|
||||
close()
|
||||
deleteSessionDirectory(deleteCryptoDb = true)
|
||||
|
|
@ -592,10 +606,6 @@ class RustMatrixClient(
|
|||
return client.session().slidingSyncVersion == SlidingSyncVersion.Native
|
||||
}
|
||||
|
||||
internal fun setDelegate(delegate: RustClientSessionDelegate) {
|
||||
client.setDelegate(delegate)
|
||||
}
|
||||
|
||||
private suspend fun File.getCacheSize(
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(sessionDispatcher) {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ package io.element.android.libraries.matrix.impl.analytics
|
|||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
|
||||
private fun Long?.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
|
||||
private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize {
|
||||
return when (this) {
|
||||
null,
|
||||
0L,
|
||||
1L -> JoinedRoom.RoomSize.One
|
||||
2L -> JoinedRoom.RoomSize.Two
|
||||
in 3..10 -> JoinedRoom.RoomSize.ThreeToTen
|
||||
in 11..100 -> JoinedRoom.RoomSize.ElevenToOneHundred
|
||||
|
|
|
|||
|
|
@ -8,14 +8,21 @@
|
|||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||
import org.matrix.rustcomponents.sdk.ClientBuildException as RustAuthenticationException
|
||||
import org.matrix.rustcomponents.sdk.ClientBuildException
|
||||
|
||||
fun Throwable.mapAuthenticationException(): AuthenticationException {
|
||||
val message = this.message ?: "Unknown error"
|
||||
return when (this) {
|
||||
is RustAuthenticationException.Generic -> AuthenticationException.Generic(message)
|
||||
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
|
||||
is RustAuthenticationException.SlidingSyncVersion -> AuthenticationException.SlidingSyncVersion(message)
|
||||
is ClientBuildException -> when (this) {
|
||||
is ClientBuildException.Generic -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
|
||||
is ClientBuildException.SlidingSyncVersion -> AuthenticationException.SlidingSyncVersion(message)
|
||||
is ClientBuildException.Sdk -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.ServerUnreachable -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.SlidingSync -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.WellKnownDeserializationException -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message)
|
||||
}
|
||||
else -> AuthenticationException.Generic(message)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientBuildException
|
||||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
import org.matrix.rustcomponents.sdk.HumanQrLoginException
|
||||
import org.matrix.rustcomponents.sdk.OidcConfiguration
|
||||
|
|
@ -251,18 +252,17 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
oidcConfiguration = oidcConfiguration,
|
||||
progressListener = progressListener,
|
||||
)
|
||||
|
||||
client.use { rustClient ->
|
||||
val sessionData = rustClient.session()
|
||||
val sessionData = client.use { rustClient ->
|
||||
rustClient.session()
|
||||
.toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.QR,
|
||||
passphrase = pendingPassphrase,
|
||||
sessionPaths = emptySessionPaths,
|
||||
)
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}.mapFailure {
|
||||
when (it) {
|
||||
is QrCodeDecodeException -> QrErrorMapper.map(it)
|
||||
|
|
@ -285,15 +285,15 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
if (slidingSyncType is ClientBuilderSlidingSync.Simplified) {
|
||||
Timber.d("Creating client with simplified sliding sync")
|
||||
try {
|
||||
return rustMatrixClientFactory
|
||||
.getBaseClientBuilder(
|
||||
sessionPaths = sessionPaths,
|
||||
passphrase = pendingPassphrase,
|
||||
slidingSyncType = slidingSyncType,
|
||||
)
|
||||
.run { config() }
|
||||
.build()
|
||||
} catch (e: HumanQrLoginException.SlidingSyncNotAvailable) {
|
||||
return rustMatrixClientFactory
|
||||
.getBaseClientBuilder(
|
||||
sessionPaths = sessionPaths,
|
||||
passphrase = pendingPassphrase,
|
||||
slidingSyncType = slidingSyncType,
|
||||
)
|
||||
.config()
|
||||
.build()
|
||||
} catch (e: ClientBuildException.SlidingSyncVersion) {
|
||||
Timber.e(e, "Failed to create client with simplified sliding sync, trying with Proxy now")
|
||||
}
|
||||
}
|
||||
|
|
@ -304,7 +304,7 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
passphrase = pendingPassphrase,
|
||||
slidingSyncType = getSlidingSyncProxy(),
|
||||
)
|
||||
.run { config() }
|
||||
.config()
|
||||
.build()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,17 @@ import org.matrix.rustcomponents.sdk.RecoveryException as RustRecoveryException
|
|||
|
||||
fun Throwable.mapRecoveryException(): RecoveryException {
|
||||
return when (this) {
|
||||
is RustRecoveryException.SecretStorage -> RecoveryException.SecretStorage(
|
||||
message = errorMessage
|
||||
)
|
||||
is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer
|
||||
is RustRecoveryException.Client -> RecoveryException.Client(
|
||||
source.mapClientException()
|
||||
)
|
||||
is RustRecoveryException -> {
|
||||
when (this) {
|
||||
is RustRecoveryException.SecretStorage -> RecoveryException.SecretStorage(
|
||||
message = errorMessage
|
||||
)
|
||||
is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer
|
||||
is RustRecoveryException.Client -> RecoveryException.Client(
|
||||
source.mapClientException()
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> RecoveryException.Client(
|
||||
ClientException.Other("Unknown error")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ import org.matrix.rustcomponents.sdk.ClientException as RustClientException
|
|||
|
||||
fun Throwable.mapClientException(): ClientException {
|
||||
return when (this) {
|
||||
is RustClientException.Generic -> ClientException.Generic(msg)
|
||||
is RustClientException -> {
|
||||
when (this) {
|
||||
is RustClientException.Generic -> ClientException.Generic(msg)
|
||||
}
|
||||
}
|
||||
else -> ClientException.Other(message ?: "Unknown error")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class MatrixRoomInfoMapper {
|
|||
isTombstoned = it.isTombstoned,
|
||||
isFavorite = it.isFavourite,
|
||||
canonicalAlias = it.canonicalAlias?.let(::RoomAlias),
|
||||
alternativeAliases = it.alternativeAliases.toImmutableList(),
|
||||
alternativeAliases = it.alternativeAliases.map(::RoomAlias).toImmutableList(),
|
||||
currentUserMembership = it.membership.map(),
|
||||
inviter = it.inviter?.let(RoomMemberMapper::map),
|
||||
activeMembersCount = it.activeMembersCount.toLong(),
|
||||
|
|
@ -50,7 +50,7 @@ class MatrixRoomInfoMapper {
|
|||
notificationCount = it.notificationCount.toLong(),
|
||||
userDefinedNotificationMode = it.cachedUserDefinedNotificationMode?.map(),
|
||||
hasRoomCall = it.hasRoomCall,
|
||||
activeRoomCallParticipants = it.activeRoomCallParticipants.toImmutableList(),
|
||||
activeRoomCallParticipants = it.activeRoomCallParticipants.map(::UserId).toImmutableList(),
|
||||
heroes = it.elementHeroes().toImmutableList(),
|
||||
pinnedEventIds = it.pinnedEventIds.map(::EventId).toImmutableList(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,13 +21,17 @@ class RoomDescriptionMapper {
|
|||
topic = roomDescription.topic,
|
||||
avatarUrl = roomDescription.avatarUrl,
|
||||
alias = roomDescription.alias?.let(::RoomAlias),
|
||||
joinRule = when (roomDescription.joinRule) {
|
||||
PublicRoomJoinRule.PUBLIC -> RoomDescription.JoinRule.PUBLIC
|
||||
PublicRoomJoinRule.KNOCK -> RoomDescription.JoinRule.KNOCK
|
||||
null -> RoomDescription.JoinRule.UNKNOWN
|
||||
},
|
||||
joinRule = roomDescription.joinRule.map(),
|
||||
isWorldReadable = roomDescription.isWorldReadable,
|
||||
numberOfMembers = roomDescription.joinedMembers.toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun PublicRoomJoinRule?.map(): RoomDescription.JoinRule {
|
||||
return when (this) {
|
||||
PublicRoomJoinRule.PUBLIC -> RoomDescription.JoinRule.PUBLIC
|
||||
PublicRoomJoinRule.KNOCK -> RoomDescription.JoinRule.KNOCK
|
||||
null -> RoomDescription.JoinRule.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||
|
||||
@Suppress("unused")
|
||||
@ExcludeFromCoverage
|
||||
internal fun RoomListEntriesUpdate.describe(): String {
|
||||
return when (this) {
|
||||
is RoomListEntriesUpdate.Set -> {
|
||||
|
|
|
|||
|
|
@ -53,11 +53,7 @@ internal class RustRoomListService(
|
|||
}
|
||||
|
||||
override suspend fun subscribeToVisibleRooms(roomIds: List<RoomId>) {
|
||||
val toSubscribe = roomIds.filterNot { roomSyncSubscriber.isSubscribedTo(it) }
|
||||
if (toSubscribe.isNotEmpty()) {
|
||||
Timber.d("Subscribe to ${toSubscribe.size} rooms: $toSubscribe")
|
||||
roomSyncSubscriber.batchSubscribe(toSubscribe)
|
||||
}
|
||||
roomSyncSubscriber.batchSubscribe(roomIds)
|
||||
}
|
||||
|
||||
override val allRooms: DynamicRoomList = roomListFactory.createRoomList(
|
||||
|
|
|
|||
|
|
@ -16,15 +16,22 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.matrix.rustcomponents.sdk.SyncServiceInterface
|
||||
import org.matrix.rustcomponents.sdk.SyncServiceState
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.matrix.rustcomponents.sdk.SyncService as InnerSyncService
|
||||
|
||||
class RustSyncService(
|
||||
private val innerSyncService: SyncServiceInterface,
|
||||
private val innerSyncService: InnerSyncService,
|
||||
sessionCoroutineScope: CoroutineScope
|
||||
) : SyncService {
|
||||
private val isServiceReady = AtomicBoolean(true)
|
||||
|
||||
override suspend fun startSync() = runCatching {
|
||||
if (!isServiceReady.get()) {
|
||||
Timber.d("Can't start sync: service is not ready")
|
||||
return@runCatching
|
||||
}
|
||||
Timber.i("Start sync")
|
||||
innerSyncService.start()
|
||||
}.onFailure {
|
||||
|
|
@ -32,12 +39,24 @@ class RustSyncService(
|
|||
}
|
||||
|
||||
override suspend fun stopSync() = runCatching {
|
||||
if (!isServiceReady.get()) {
|
||||
Timber.d("Can't stop sync: service is not ready")
|
||||
return@runCatching
|
||||
}
|
||||
Timber.i("Stop sync")
|
||||
innerSyncService.stop()
|
||||
}.onFailure {
|
||||
Timber.d("Stop sync failed: $it")
|
||||
}
|
||||
|
||||
suspend fun destroy() {
|
||||
// If the service was still running, stop it
|
||||
stopSync()
|
||||
Timber.d("Destroying sync service")
|
||||
isServiceReady.set(false)
|
||||
innerSyncService.destroy()
|
||||
}
|
||||
|
||||
override val syncState: StateFlow<SyncState> =
|
||||
innerSyncService.stateFlow()
|
||||
.map(SyncServiceState::toSyncState)
|
||||
|
|
|
|||
|
|
@ -100,7 +100,8 @@ internal class MatrixTimelineDiffProcessor(
|
|||
clear()
|
||||
}
|
||||
TimelineChange.TRUNCATE -> {
|
||||
// Not supported
|
||||
val index = diff.truncate() ?: return
|
||||
subList(index.toInt(), size).clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ class RustSessionVerificationService(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateVerificationStatus() {
|
||||
if (verificationFlowState.value == VerificationFlowState.Finished) {
|
||||
// Calling `encryptionService.verificationState()` performs a network call and it will deadlock if there is no network
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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.analytics
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import org.junit.Test
|
||||
|
||||
class JoinedRoomExtKtTest {
|
||||
@Test
|
||||
fun `test room size mapping`() {
|
||||
mapOf(
|
||||
listOf(0L, 1L) to JoinedRoom.RoomSize.One,
|
||||
listOf(2L, 2L) to JoinedRoom.RoomSize.Two,
|
||||
listOf(3L, 10L) to JoinedRoom.RoomSize.ThreeToTen,
|
||||
listOf(11L, 100L) to JoinedRoom.RoomSize.ElevenToOneHundred,
|
||||
listOf(101L, 1000L) to JoinedRoom.RoomSize.OneHundredAndOneToAThousand,
|
||||
listOf(1001L, 2000L) to JoinedRoom.RoomSize.MoreThanAThousand
|
||||
).forEach { (joinedMemberCounts, expectedRoomSize) ->
|
||||
joinedMemberCounts.forEach { joinedMemberCount ->
|
||||
assertThat(aMatrixRoom(joinedMemberCount = joinedMemberCount).toAnalyticsJoinedRoom(null))
|
||||
.isEqualTo(
|
||||
JoinedRoom(
|
||||
isDM = false,
|
||||
isSpace = false,
|
||||
roomSize = expectedRoomSize,
|
||||
trigger = null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test isDirect parameter mapping`() {
|
||||
assertThat(aMatrixRoom(isDirect = true).toAnalyticsJoinedRoom(null))
|
||||
.isEqualTo(
|
||||
JoinedRoom(
|
||||
isDM = true,
|
||||
isSpace = false,
|
||||
roomSize = JoinedRoom.RoomSize.One,
|
||||
trigger = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test isSpace parameter mapping`() {
|
||||
assertThat(aMatrixRoom(isSpace = true).toAnalyticsJoinedRoom(null))
|
||||
.isEqualTo(
|
||||
JoinedRoom(
|
||||
isDM = false,
|
||||
isSpace = true,
|
||||
roomSize = JoinedRoom.RoomSize.One,
|
||||
trigger = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test trigger parameter mapping`() {
|
||||
assertThat(aMatrixRoom().toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite))
|
||||
.isEqualTo(
|
||||
JoinedRoom(
|
||||
isDM = false,
|
||||
isSpace = false,
|
||||
roomSize = JoinedRoom.RoomSize.One,
|
||||
trigger = JoinedRoom.Trigger.Invite
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun aMatrixRoom(
|
||||
isDirect: Boolean = false,
|
||||
isSpace: Boolean = false,
|
||||
joinedMemberCount: Long = 0
|
||||
): MatrixRoom {
|
||||
return FakeMatrixRoom(
|
||||
isDirect = isDirect,
|
||||
isSpace = isSpace,
|
||||
joinedMemberCount = joinedMemberCount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -36,13 +36,24 @@ class AuthenticationExceptionMappingTest {
|
|||
assertThat(ClientBuildException.InvalidServerName("Invalid server name").mapAuthenticationException())
|
||||
.isException<AuthenticationException.InvalidServerName>("Invalid server name")
|
||||
|
||||
assertThat(ClientBuildException.Sdk("SDK issue").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("SDK issue")
|
||||
|
||||
assertThat(ClientBuildException.SlidingSyncVersion("Sliding sync not available").mapAuthenticationException())
|
||||
.isException<AuthenticationException.SlidingSyncVersion>("Sliding sync not available")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping other exceptions map to the Generic Kotlin`() {
|
||||
assertThat(ClientBuildException.Sdk("SDK issue").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("SDK issue")
|
||||
assertThat(ClientBuildException.ServerUnreachable("Server unreachable").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("Server unreachable")
|
||||
assertThat(ClientBuildException.SlidingSync("Sliding Sync").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("Sliding Sync")
|
||||
assertThat(ClientBuildException.WellKnownDeserializationException("WellKnown Deserialization").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("WellKnown Deserialization")
|
||||
assertThat(ClientBuildException.WellKnownLookupFailed("WellKnown Lookup Failed").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("WellKnown Lookup Failed")
|
||||
}
|
||||
|
||||
private inline fun <reified T> ThrowableSubject.isException(message: String) {
|
||||
isInstanceOf(T::class.java)
|
||||
hasMessageThat().isEqualTo(message)
|
||||
|
|
|
|||
|
|
@ -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.qrlogin
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeDecodeException
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.QrLoginException
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.HumanQrLoginException as RustHumanQrLoginException
|
||||
import org.matrix.rustcomponents.sdk.QrCodeDecodeException as RustQrCodeDecodeException
|
||||
|
||||
class QrErrorMapperTest {
|
||||
@Test
|
||||
fun `test map QrCodeDecodeException`() {
|
||||
val result = QrErrorMapper.map(RustQrCodeDecodeException.Crypto("test"))
|
||||
assertThat(result).isInstanceOf(QrCodeDecodeException.Crypto::class.java)
|
||||
assertThat(result.message).isEqualTo("test")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test map HumanQrLoginException`() {
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.Cancelled())).isEqualTo(QrLoginException.Cancelled)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.ConnectionInsecure())).isEqualTo(QrLoginException.ConnectionInsecure)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.Declined())).isEqualTo(QrLoginException.Declined)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.Expired())).isEqualTo(QrLoginException.Expired)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.OtherDeviceNotSignedIn())).isEqualTo(QrLoginException.OtherDeviceNotSignedIn)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.LinkingNotSupported())).isEqualTo(QrLoginException.LinkingNotSupported)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.Unknown())).isEqualTo(QrLoginException.Unknown)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.OidcMetadataInvalid())).isEqualTo(QrLoginException.OidcMetadataInvalid)
|
||||
assertThat(QrErrorMapper.map(RustHumanQrLoginException.SlidingSyncNotAvailable())).isEqualTo(QrLoginException.SlidingSyncNotAvailable)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.qrlogin
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.QrLoginProgress
|
||||
|
||||
class QrLoginProgressExtensionsKtTest {
|
||||
@Test
|
||||
fun `mapping QrLoginProgress should return expected result`() {
|
||||
assertThat(QrLoginProgress.Starting.toStep())
|
||||
.isEqualTo(QrCodeLoginStep.Starting)
|
||||
assertThat(QrLoginProgress.EstablishingSecureChannel(1u, "01").toStep())
|
||||
.isEqualTo(QrCodeLoginStep.EstablishingSecureChannel("01"))
|
||||
assertThat(QrLoginProgress.WaitingForToken("userCode").toStep())
|
||||
.isEqualTo(QrCodeLoginStep.WaitingForToken("userCode"))
|
||||
assertThat(QrLoginProgress.Done.toStep())
|
||||
.isEqualTo(QrCodeLoginStep.Finished)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.core
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.TransmissionProgress
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class ProgressWatcherWrapperKtTest {
|
||||
@Test
|
||||
fun testToProgressWatcher() = runTest {
|
||||
suspendCoroutine { continuation ->
|
||||
val callback = object : ProgressCallback {
|
||||
override fun onProgress(current: Long, total: Long) {
|
||||
assertThat(current).isEqualTo(1)
|
||||
assertThat(total).isEqualTo(2)
|
||||
continuation.resume(Unit)
|
||||
}
|
||||
}
|
||||
val result = callback.toProgressWatcher()
|
||||
result.transmissionProgress(TransmissionProgress(1.toULong(), 2.toULong()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.di
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import org.junit.Test
|
||||
|
||||
class TracingMatrixModuleTest {
|
||||
@Test
|
||||
fun `providesTracingFilterConfiguration returns debug config for debug build`() {
|
||||
assertThat(TracingMatrixModule.providesTracingFilterConfiguration(aBuildMeta(BuildType.DEBUG)))
|
||||
.isEqualTo(TracingFilterConfigurations.debug)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `providesTracingFilterConfiguration returns nightly config for nightly build`() {
|
||||
assertThat(TracingMatrixModule.providesTracingFilterConfiguration(aBuildMeta(BuildType.NIGHTLY)))
|
||||
.isEqualTo(TracingFilterConfigurations.nightly)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `providesTracingFilterConfiguration returns release config for release build`() {
|
||||
assertThat(TracingMatrixModule.providesTracingFilterConfiguration(aBuildMeta(BuildType.RELEASE)))
|
||||
.isEqualTo(TracingFilterConfigurations.release)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.encryption
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.BackupState as RustBackupState
|
||||
|
||||
class BackupStateMapperTest {
|
||||
@Test
|
||||
fun `Ensure that mapping is correct`() {
|
||||
val sut = BackupStateMapper()
|
||||
assertThat(sut.map(RustBackupState.UNKNOWN)).isEqualTo(BackupState.UNKNOWN)
|
||||
assertThat(sut.map(RustBackupState.CREATING)).isEqualTo(BackupState.CREATING)
|
||||
assertThat(sut.map(RustBackupState.ENABLING)).isEqualTo(BackupState.ENABLING)
|
||||
assertThat(sut.map(RustBackupState.RESUMING)).isEqualTo(BackupState.RESUMING)
|
||||
assertThat(sut.map(RustBackupState.ENABLED)).isEqualTo(BackupState.ENABLED)
|
||||
assertThat(sut.map(RustBackupState.DOWNLOADING)).isEqualTo(BackupState.DOWNLOADING)
|
||||
assertThat(sut.map(RustBackupState.DISABLING)).isEqualTo(BackupState.DISABLING)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.encryption
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState
|
||||
|
||||
class BackupUploadStateMapperTest {
|
||||
@Test
|
||||
fun `Ensure that mapping is correct`() {
|
||||
val sut = BackupUploadStateMapper()
|
||||
assertThat(sut.map(RustBackupUploadState.Waiting))
|
||||
.isEqualTo(BackupUploadState.Waiting)
|
||||
assertThat(sut.map(RustBackupUploadState.Error))
|
||||
.isEqualTo(BackupUploadState.Error)
|
||||
assertThat(sut.map(RustBackupUploadState.Done))
|
||||
.isEqualTo(BackupUploadState.Done)
|
||||
assertThat(sut.map(RustBackupUploadState.Uploading(1.toUInt(), 2.toUInt())))
|
||||
.isEqualTo(BackupUploadState.Uploading(1, 2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Ensure that full uploading is mapper to Done`() {
|
||||
val sut = BackupUploadStateMapper()
|
||||
assertThat(sut.map(RustBackupUploadState.Uploading(2.toUInt(), 2.toUInt())))
|
||||
.isEqualTo(BackupUploadState.Done)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.encryption
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress
|
||||
|
||||
class EnableRecoveryProgressMapperTest {
|
||||
@Test
|
||||
fun `Ensure that mapping is correct`() {
|
||||
val sut = EnableRecoveryProgressMapper()
|
||||
assertThat(sut.map(RustEnableRecoveryProgress.CreatingRecoveryKey))
|
||||
.isEqualTo(EnableRecoveryProgress.CreatingRecoveryKey)
|
||||
assertThat(sut.map(RustEnableRecoveryProgress.CreatingBackup))
|
||||
.isEqualTo(EnableRecoveryProgress.CreatingBackup)
|
||||
assertThat(sut.map(RustEnableRecoveryProgress.Starting))
|
||||
.isEqualTo(EnableRecoveryProgress.Starting)
|
||||
assertThat(sut.map(RustEnableRecoveryProgress.BackingUp(1.toUInt(), 2.toUInt())))
|
||||
.isEqualTo(EnableRecoveryProgress.BackingUp(1, 2))
|
||||
assertThat(sut.map(RustEnableRecoveryProgress.RoomKeyUploadError))
|
||||
.isEqualTo(EnableRecoveryProgress.RoomKeyUploadError)
|
||||
assertThat(sut.map(RustEnableRecoveryProgress.Done("recoveryKey")))
|
||||
.isEqualTo(EnableRecoveryProgress.Done("recoveryKey"))
|
||||
}
|
||||
}
|
||||
|
|
@ -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.encryption
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState
|
||||
|
||||
class RecoveryStateMapperTest {
|
||||
@Test
|
||||
fun `Ensure that mapping is correct`() {
|
||||
val sut = RecoveryStateMapper()
|
||||
assertThat(sut.map(RustRecoveryState.UNKNOWN)).isEqualTo(RecoveryState.UNKNOWN)
|
||||
assertThat(sut.map(RustRecoveryState.ENABLED)).isEqualTo(RecoveryState.ENABLED)
|
||||
assertThat(sut.map(RustRecoveryState.DISABLED)).isEqualTo(RecoveryState.DISABLED)
|
||||
assertThat(sut.map(RustRecoveryState.INCOMPLETE)).isEqualTo(RecoveryState.INCOMPLETE)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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_ROOM_ALIAS
|
||||
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 {
|
||||
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
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.api.core.UserId
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import org.matrix.rustcomponents.sdk.RoomHero
|
||||
|
||||
internal fun aRustRoomHero(
|
||||
userId: UserId = A_USER_ID,
|
||||
): RoomHero {
|
||||
return RoomHero(
|
||||
userId = userId.value,
|
||||
displayName = "displayName",
|
||||
avatarUrl = "avatarUrl",
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.api.core.UserId
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.RoomHero
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
||||
|
||||
fun aRustRoomInfo(
|
||||
id: String = A_ROOM_ID.value,
|
||||
displayName: String? = A_ROOM_NAME,
|
||||
rawName: String? = A_ROOM_NAME,
|
||||
topic: String? = null,
|
||||
avatarUrl: String? = null,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = false,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavourite: Boolean = false,
|
||||
canonicalAlias: String? = null,
|
||||
alternativeAliases: List<String> = listOf(),
|
||||
membership: Membership = Membership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
heroes: List<RoomHero> = listOf(),
|
||||
activeMembersCount: ULong = 0uL,
|
||||
invitedMembersCount: ULong = 0uL,
|
||||
joinedMembersCount: ULong = 0uL,
|
||||
userPowerLevels: Map<String, Long> = mapOf(),
|
||||
highlightCount: ULong = 0uL,
|
||||
notificationCount: ULong = 0uL,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
activeRoomCallParticipants: List<String> = listOf(),
|
||||
isMarkedUnread: Boolean = false,
|
||||
numUnreadMessages: ULong = 0uL,
|
||||
numUnreadNotifications: ULong = 0uL,
|
||||
numUnreadMentions: ULong = 0uL,
|
||||
pinnedEventIds: List<String> = listOf(),
|
||||
roomCreator: UserId? = null,
|
||||
) = RoomInfo(
|
||||
id = id,
|
||||
displayName = displayName,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
isFavourite = isFavourite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases,
|
||||
membership = membership,
|
||||
inviter = inviter,
|
||||
heroes = heroes,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
userPowerLevels = userPowerLevels,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
cachedUserDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
pinnedEventIds = pinnedEventIds,
|
||||
creator = roomCreator?.value,
|
||||
)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.api.core.UserId
|
||||
import org.matrix.rustcomponents.sdk.MembershipState
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import uniffi.matrix_sdk.RoomMemberRole
|
||||
|
||||
fun aRustRoomMember(
|
||||
userId: UserId,
|
||||
displayName: String? = null,
|
||||
avatarUrl: String? = null,
|
||||
membership: MembershipState = MembershipState.JOIN,
|
||||
isNameAmbiguous: Boolean = false,
|
||||
powerLevel: Long = 0L,
|
||||
isIgnored: Boolean = false,
|
||||
role: RoomMemberRole = RoomMemberRole.USER,
|
||||
) = RoomMember(
|
||||
userId = userId.value,
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
membership = membership,
|
||||
isNameAmbiguous = isNameAmbiguous,
|
||||
powerLevel = powerLevel,
|
||||
normalizedPowerLevel = powerLevel,
|
||||
isIgnored = isIgnored,
|
||||
suggestedRoleForPowerLevel = role,
|
||||
)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.RoomPowerLevels
|
||||
|
||||
internal fun aRustRoomPowerLevels(
|
||||
ban: Long,
|
||||
invite: Long,
|
||||
kick: Long,
|
||||
redact: Long,
|
||||
eventsDefault: Long,
|
||||
stateDefault: Long,
|
||||
usersDefault: Long,
|
||||
roomName: Long,
|
||||
roomAvatar: Long,
|
||||
roomTopic: Long,
|
||||
) = RoomPowerLevels(
|
||||
ban = ban,
|
||||
invite = invite,
|
||||
kick = kick,
|
||||
redact = redact,
|
||||
eventsDefault = eventsDefault,
|
||||
stateDefault = stateDefault,
|
||||
usersDefault = usersDefault,
|
||||
roomName = roomName,
|
||||
roomAvatar = roomAvatar,
|
||||
roomTopic = roomTopic,
|
||||
)
|
||||
|
|
@ -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.factories
|
||||
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import org.matrix.rustcomponents.sdk.RoomPreview
|
||||
|
||||
internal fun aRustRoomPreview(
|
||||
canonicalAlias: String? = A_ROOM_ALIAS.value,
|
||||
isJoined: Boolean = true,
|
||||
isInvited: Boolean = true,
|
||||
isPublic: Boolean = true,
|
||||
canKnock: Boolean = true,
|
||||
): RoomPreview {
|
||||
return RoomPreview(
|
||||
roomId = A_ROOM_ID.value,
|
||||
canonicalAlias = canonicalAlias,
|
||||
name = "name",
|
||||
topic = "topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numJoinedMembers = 1u,
|
||||
roomType = null,
|
||||
isHistoryWorldReadable = true,
|
||||
isJoined = isJoined,
|
||||
isInvited = isInvited,
|
||||
isPublic = isPublic,
|
||||
canKnock = canKnock,
|
||||
)
|
||||
}
|
||||
|
|
@ -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.SearchUsersResults
|
||||
import org.matrix.rustcomponents.sdk.UserProfile
|
||||
|
||||
internal fun aRustSearchUsersResults(
|
||||
results: List<UserProfile>,
|
||||
limited: Boolean,
|
||||
) = SearchUsersResults(
|
||||
results = results,
|
||||
limited = limited,
|
||||
)
|
||||
|
|
@ -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.fixtures.factories
|
||||
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import org.matrix.rustcomponents.sdk.Session
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncVersion
|
||||
|
||||
internal fun aRustSession(
|
||||
proxy: SlidingSyncVersion = SlidingSyncVersion.None
|
||||
): Session {
|
||||
return Session(
|
||||
accessToken = "accessToken",
|
||||
refreshToken = "refreshToken",
|
||||
userId = A_USER_ID.value,
|
||||
deviceId = A_DEVICE_ID.value,
|
||||
homeserverUrl = A_HOMESERVER_URL,
|
||||
oidcData = null,
|
||||
slidingSyncVersion = proxy,
|
||||
)
|
||||
}
|
||||
|
|
@ -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.factories
|
||||
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import org.matrix.rustcomponents.sdk.UserProfile
|
||||
|
||||
fun aRustUserProfile(
|
||||
userId: String = A_USER_ID.value,
|
||||
displayName: String = "displayName",
|
||||
avatarUrl: String = "avatarUrl",
|
||||
) = UserProfile(
|
||||
userId = userId,
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
)
|
||||
|
|
@ -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.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomMembersIterator
|
||||
|
||||
class FakeRustRoom(
|
||||
private val roomId: RoomId = A_ROOM_ID,
|
||||
private val getMembers: () -> RoomMembersIterator = { lambdaError() },
|
||||
private val getMembersNoSync: () -> RoomMembersIterator = { lambdaError() },
|
||||
) : Room(NoPointer) {
|
||||
override fun id(): String {
|
||||
return roomId.value
|
||||
}
|
||||
|
||||
override suspend fun members(): RoomMembersIterator {
|
||||
return getMembers()
|
||||
}
|
||||
|
||||
override suspend fun membersNoSync(): RoomMembersIterator {
|
||||
return getMembersNoSync()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomInfo
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
|
||||
class FakeRustRoomListItem(
|
||||
private val roomId: RoomId,
|
||||
private val roomInfo: RoomInfo = aRustRoomInfo(id = roomId.value),
|
||||
private val latestEvent: EventTimelineItem? = null,
|
||||
) : RoomListItem(NoPointer) {
|
||||
override fun id(): String {
|
||||
return roomId.value
|
||||
}
|
||||
|
||||
override suspend fun roomInfo(): RoomInfo {
|
||||
return roomInfo
|
||||
}
|
||||
|
||||
override suspend fun latestEvent(): EventTimelineItem? {
|
||||
return latestEvent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomMembersIterator
|
||||
|
||||
class FakeRustRoomMembersIterator(
|
||||
private var members: List<RoomMember>? = null
|
||||
) : RoomMembersIterator(NoPointer) {
|
||||
override fun len(): UInt {
|
||||
return members?.size?.toUInt() ?: 0u
|
||||
}
|
||||
|
||||
override fun nextChunk(chunkSize: UInt): List<RoomMember>? {
|
||||
if (members?.isEmpty() == true) {
|
||||
return null
|
||||
}
|
||||
return members?.let {
|
||||
val result = it.take(chunkSize.toInt())
|
||||
members = it.subList(result.size, it.size)
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 org.matrix.rustcomponents.sdk.InsertData
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.SetData
|
||||
import org.matrix.rustcomponents.sdk.TimelineChange
|
||||
import org.matrix.rustcomponents.sdk.TimelineDiff
|
||||
import org.matrix.rustcomponents.sdk.TimelineItem
|
||||
|
||||
class FakeRustTimelineDiff(
|
||||
private val change: TimelineChange,
|
||||
private val item: TimelineItem? = FakeRustTimelineItem()
|
||||
) : TimelineDiff(NoPointer) {
|
||||
override fun change() = change
|
||||
override fun append(): List<TimelineItem>? = item?.let { listOf(it) }
|
||||
override fun insert(): InsertData? = item?.let { InsertData(1u, it) }
|
||||
override fun pushBack(): TimelineItem? = item
|
||||
override fun pushFront(): TimelineItem? = item
|
||||
override fun remove(): UInt? = 1u
|
||||
override fun reset(): List<TimelineItem>? = item?.let { listOf(it) }
|
||||
override fun set(): SetData? = item?.let { SetData(1u, it) }
|
||||
override fun truncate(): UInt? = 1u
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.EventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.TimelineItem
|
||||
import org.matrix.rustcomponents.sdk.VirtualTimelineItem
|
||||
|
||||
class FakeRustTimelineItem : TimelineItem(NoPointer) {
|
||||
override fun asEvent(): EventTimelineItem? = null
|
||||
override fun asVirtual(): VirtualTimelineItem? = null
|
||||
override fun fmtDebug(): String = "fmtDebug"
|
||||
override fun uniqueId(): String = "uniqueId"
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.keys
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class DefaultPassphraseGeneratorTest {
|
||||
@Test
|
||||
fun `check that generated passphrase has the expected length`() {
|
||||
val passphraseGenerator = DefaultPassphraseGenerator()
|
||||
val passphrase = passphraseGenerator.generatePassphrase()
|
||||
assertThat(passphrase!!.length).isEqualTo(342)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.mapper
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
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
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2
|
||||
import io.element.android.libraries.matrix.test.A_SECRET
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncVersion
|
||||
import java.io.File
|
||||
|
||||
class SessionKtTest {
|
||||
@Test
|
||||
fun `toSessionData compute the expected result`() {
|
||||
val result = aRustSession().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).isEqualTo("refreshToken")
|
||||
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 `toSessionData can change the validity of the token`() {
|
||||
val result = aRustSession().toSessionData(
|
||||
isTokenValid = false,
|
||||
loginType = LoginType.PASSWORD,
|
||||
passphrase = A_SECRET,
|
||||
sessionPaths = SessionPaths(File("/a/file"), File("/a/cache")),
|
||||
homeserverUrl = null,
|
||||
)
|
||||
assertThat(result.isTokenValid).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSessionData can override the value of the homeserver url`() {
|
||||
val result = aRustSession().toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.PASSWORD,
|
||||
passphrase = A_SECRET,
|
||||
sessionPaths = SessionPaths(File("/a/file"), File("/a/cache")),
|
||||
homeserverUrl = A_HOMESERVER_URL_2,
|
||||
)
|
||||
assertThat(result.homeserverUrl).isEqualTo(A_HOMESERVER_URL_2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toSessionData copy the sliding sync url if present`() {
|
||||
val result = aRustSession(
|
||||
proxy = SlidingSyncVersion.Proxy("proxyUrl")
|
||||
).toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.PASSWORD,
|
||||
passphrase = A_SECRET,
|
||||
sessionPaths = SessionPaths(File("/a/file"), File("/a/cache")),
|
||||
homeserverUrl = A_HOMESERVER_URL_2,
|
||||
)
|
||||
assertThat(result.slidingSyncProxy).isEqualTo("proxyUrl")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2023, 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.poll
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.PollKind as RustPollKind
|
||||
|
||||
class PollKindKtTest {
|
||||
@Test
|
||||
fun `map should return Disclosed when RustPollKind is Disclosed`() {
|
||||
val pollKind = RustPollKind.DISCLOSED.map()
|
||||
assertThat(pollKind).isEqualTo(PollKind.Disclosed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map should return Undisclosed when RustPollKind is Undisclosed`() {
|
||||
val pollKind = RustPollKind.UNDISCLOSED.map()
|
||||
assertThat(pollKind).isEqualTo(PollKind.Undisclosed)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toInner should return DISCLOSED when PollKind is Disclosed`() {
|
||||
val rustPollKind = PollKind.Disclosed.toInner()
|
||||
assertThat(rustPollKind).isEqualTo(RustPollKind.DISCLOSED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toInner should return UNDISCLOSED when PollKind is Undisclosed`() {
|
||||
val rustPollKind = PollKind.Undisclosed.toInner()
|
||||
assertThat(rustPollKind).isEqualTo(RustPollKind.UNDISCLOSED)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomHero
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomInfo
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomMember
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_6
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
||||
|
||||
class MatrixRoomInfoMapperTest {
|
||||
@Test
|
||||
fun `mapping of RustRoomInfo should map all the fields`() {
|
||||
assertThat(
|
||||
MatrixRoomInfoMapper().map(
|
||||
aRustRoomInfo(
|
||||
id = A_ROOM_ID.value,
|
||||
displayName = "displayName",
|
||||
rawName = "rawName",
|
||||
topic = "topic",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
isDirect = true,
|
||||
isPublic = false,
|
||||
isSpace = false,
|
||||
isTombstoned = false,
|
||||
isFavourite = false,
|
||||
canonicalAlias = A_ROOM_ALIAS.value,
|
||||
alternativeAliases = listOf(A_ROOM_ALIAS.value),
|
||||
membership = Membership.JOINED,
|
||||
inviter = aRustRoomMember(A_USER_ID),
|
||||
heroes = listOf(aRustRoomHero()),
|
||||
activeMembersCount = 2uL,
|
||||
invitedMembersCount = 3uL,
|
||||
joinedMembersCount = 4uL,
|
||||
userPowerLevels = mapOf(A_USER_ID_6.value to 50L),
|
||||
highlightCount = 10uL,
|
||||
notificationCount = 11uL,
|
||||
userDefinedNotificationMode = RustRoomNotificationMode.MUTE,
|
||||
hasRoomCall = true,
|
||||
activeRoomCallParticipants = listOf(A_USER_ID_3.value),
|
||||
isMarkedUnread = false,
|
||||
numUnreadMessages = 12uL,
|
||||
numUnreadNotifications = 13uL,
|
||||
numUnreadMentions = 14uL,
|
||||
pinnedEventIds = listOf(AN_EVENT_ID.value),
|
||||
roomCreator = A_USER_ID,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
MatrixRoomInfo(
|
||||
id = A_ROOM_ID,
|
||||
name = "displayName",
|
||||
rawName = "rawName",
|
||||
topic = "topic",
|
||||
avatarUrl = AN_AVATAR_URL,
|
||||
isDirect = true,
|
||||
isPublic = false,
|
||||
isSpace = false,
|
||||
isTombstoned = false,
|
||||
isFavorite = false,
|
||||
canonicalAlias = A_ROOM_ALIAS,
|
||||
alternativeAliases = listOf(A_ROOM_ALIAS).toImmutableList(),
|
||||
currentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter = aRoomMember(A_USER_ID),
|
||||
activeMembersCount = 2L,
|
||||
invitedMembersCount = 3L,
|
||||
joinedMembersCount = 4L,
|
||||
userPowerLevels = mapOf(A_USER_ID_6 to 50L).toImmutableMap(),
|
||||
highlightCount = 10L,
|
||||
notificationCount = 11L,
|
||||
userDefinedNotificationMode = RoomNotificationMode.MUTE,
|
||||
hasRoomCall = true,
|
||||
activeRoomCallParticipants = listOf(A_USER_ID_3).toImmutableList(),
|
||||
heroes = listOf(
|
||||
MatrixUser(
|
||||
userId = A_USER_ID,
|
||||
displayName = "displayName",
|
||||
avatarUrl = "avatarUrl",
|
||||
)
|
||||
).toImmutableList(),
|
||||
pinnedEventIds = listOf(AN_EVENT_ID).toPersistentList(),
|
||||
creator = A_USER_ID,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping of RustRoomInfo with null members should map all the fields`() {
|
||||
assertThat(
|
||||
MatrixRoomInfoMapper().map(
|
||||
aRustRoomInfo(
|
||||
id = A_ROOM_ID.value,
|
||||
displayName = null,
|
||||
rawName = null,
|
||||
topic = null,
|
||||
avatarUrl = null,
|
||||
isDirect = false,
|
||||
isPublic = true,
|
||||
isSpace = false,
|
||||
isTombstoned = false,
|
||||
isFavourite = true,
|
||||
canonicalAlias = null,
|
||||
alternativeAliases = emptyList(),
|
||||
membership = Membership.INVITED,
|
||||
inviter = null,
|
||||
heroes = listOf(aRustRoomHero()),
|
||||
activeMembersCount = 2uL,
|
||||
invitedMembersCount = 3uL,
|
||||
joinedMembersCount = 4uL,
|
||||
userPowerLevels = emptyMap(),
|
||||
highlightCount = 10uL,
|
||||
notificationCount = 11uL,
|
||||
userDefinedNotificationMode = null,
|
||||
hasRoomCall = false,
|
||||
activeRoomCallParticipants = emptyList(),
|
||||
isMarkedUnread = true,
|
||||
numUnreadMessages = 12uL,
|
||||
numUnreadNotifications = 13uL,
|
||||
numUnreadMentions = 14uL,
|
||||
pinnedEventIds = emptyList(),
|
||||
roomCreator = null,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
MatrixRoomInfo(
|
||||
id = A_ROOM_ID,
|
||||
name = null,
|
||||
rawName = null,
|
||||
topic = null,
|
||||
avatarUrl = null,
|
||||
isDirect = false,
|
||||
isPublic = true,
|
||||
isSpace = false,
|
||||
isTombstoned = false,
|
||||
isFavorite = true,
|
||||
canonicalAlias = null,
|
||||
alternativeAliases = emptyList<RoomAlias>().toPersistentList(),
|
||||
currentUserMembership = CurrentUserMembership.INVITED,
|
||||
inviter = null,
|
||||
activeMembersCount = 2L,
|
||||
invitedMembersCount = 3L,
|
||||
joinedMembersCount = 4L,
|
||||
userPowerLevels = emptyMap<UserId, Long>().toImmutableMap(),
|
||||
highlightCount = 10L,
|
||||
notificationCount = 11L,
|
||||
userDefinedNotificationMode = null,
|
||||
hasRoomCall = false,
|
||||
activeRoomCallParticipants = emptyList<UserId>().toImmutableList(),
|
||||
heroes = emptyList<MatrixUser>().toImmutableList(),
|
||||
pinnedEventIds = emptyList<EventId>().toPersistentList(),
|
||||
creator = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping Membership`() {
|
||||
assertThat(Membership.INVITED.map()).isEqualTo(CurrentUserMembership.INVITED)
|
||||
assertThat(Membership.JOINED.map()).isEqualTo(CurrentUserMembership.JOINED)
|
||||
assertThat(Membership.LEFT.map()).isEqualTo(CurrentUserMembership.LEFT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping RoomNotificationMode`() {
|
||||
assertThat(RustRoomNotificationMode.ALL_MESSAGES.map()).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
|
||||
assertThat(RustRoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY.map()).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||
assertThat(RustRoomNotificationMode.MUTE.map()).isEqualTo(RoomNotificationMode.MUTE)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.MessageLikeEventType
|
||||
|
||||
class MessageEventTypeKtTest {
|
||||
@Test
|
||||
fun `map Rust type should result to correct Kotlin type`() {
|
||||
assertThat(MessageLikeEventType.CALL_ANSWER.map()).isEqualTo(MessageEventType.CALL_ANSWER)
|
||||
assertThat(MessageLikeEventType.CALL_INVITE.map()).isEqualTo(MessageEventType.CALL_INVITE)
|
||||
assertThat(MessageLikeEventType.CALL_HANGUP.map()).isEqualTo(MessageEventType.CALL_HANGUP)
|
||||
assertThat(MessageLikeEventType.CALL_CANDIDATES.map()).isEqualTo(MessageEventType.CALL_CANDIDATES)
|
||||
assertThat(MessageLikeEventType.CALL_NOTIFY.map()).isEqualTo(MessageEventType.CALL_NOTIFY)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_READY.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_READY)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_START.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_START)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_CANCEL.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_CANCEL)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_ACCEPT.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_ACCEPT)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_KEY.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_KEY)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_MAC.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_MAC)
|
||||
assertThat(MessageLikeEventType.KEY_VERIFICATION_DONE.map()).isEqualTo(MessageEventType.KEY_VERIFICATION_DONE)
|
||||
assertThat(MessageLikeEventType.REACTION.map()).isEqualTo(MessageEventType.REACTION)
|
||||
assertThat(MessageLikeEventType.ROOM_ENCRYPTED.map()).isEqualTo(MessageEventType.ROOM_ENCRYPTED)
|
||||
assertThat(MessageLikeEventType.ROOM_MESSAGE.map()).isEqualTo(MessageEventType.ROOM_MESSAGE)
|
||||
assertThat(MessageLikeEventType.ROOM_REDACTION.map()).isEqualTo(MessageEventType.ROOM_REDACTION)
|
||||
assertThat(MessageLikeEventType.STICKER.map()).isEqualTo(MessageEventType.STICKER)
|
||||
assertThat(MessageLikeEventType.POLL_END.map()).isEqualTo(MessageEventType.POLL_END)
|
||||
assertThat(MessageLikeEventType.POLL_RESPONSE.map()).isEqualTo(MessageEventType.POLL_RESPONSE)
|
||||
assertThat(MessageLikeEventType.POLL_START.map()).isEqualTo(MessageEventType.POLL_START)
|
||||
assertThat(MessageLikeEventType.UNSTABLE_POLL_END.map()).isEqualTo(MessageEventType.UNSTABLE_POLL_END)
|
||||
assertThat(MessageLikeEventType.UNSTABLE_POLL_RESPONSE.map()).isEqualTo(MessageEventType.UNSTABLE_POLL_RESPONSE)
|
||||
assertThat(MessageLikeEventType.UNSTABLE_POLL_START.map()).isEqualTo(MessageEventType.UNSTABLE_POLL_START)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map Kotlin type should result to correct Rust type`() {
|
||||
assertThat(MessageEventType.CALL_ANSWER.map()).isEqualTo(MessageLikeEventType.CALL_ANSWER)
|
||||
assertThat(MessageEventType.CALL_INVITE.map()).isEqualTo(MessageLikeEventType.CALL_INVITE)
|
||||
assertThat(MessageEventType.CALL_HANGUP.map()).isEqualTo(MessageLikeEventType.CALL_HANGUP)
|
||||
assertThat(MessageEventType.CALL_CANDIDATES.map()).isEqualTo(MessageLikeEventType.CALL_CANDIDATES)
|
||||
assertThat(MessageEventType.CALL_NOTIFY.map()).isEqualTo(MessageLikeEventType.CALL_NOTIFY)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_READY.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_READY)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_START.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_START)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_CANCEL.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_CANCEL)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_ACCEPT.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_ACCEPT)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_KEY.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_KEY)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_MAC.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_MAC)
|
||||
assertThat(MessageEventType.KEY_VERIFICATION_DONE.map()).isEqualTo(MessageLikeEventType.KEY_VERIFICATION_DONE)
|
||||
assertThat(MessageEventType.REACTION.map()).isEqualTo(MessageLikeEventType.REACTION)
|
||||
assertThat(MessageEventType.ROOM_ENCRYPTED.map()).isEqualTo(MessageLikeEventType.ROOM_ENCRYPTED)
|
||||
assertThat(MessageEventType.ROOM_MESSAGE.map()).isEqualTo(MessageLikeEventType.ROOM_MESSAGE)
|
||||
assertThat(MessageEventType.ROOM_REDACTION.map()).isEqualTo(MessageLikeEventType.ROOM_REDACTION)
|
||||
assertThat(MessageEventType.STICKER.map()).isEqualTo(MessageLikeEventType.STICKER)
|
||||
assertThat(MessageEventType.POLL_END.map()).isEqualTo(MessageLikeEventType.POLL_END)
|
||||
assertThat(MessageEventType.POLL_RESPONSE.map()).isEqualTo(MessageLikeEventType.POLL_RESPONSE)
|
||||
assertThat(MessageEventType.POLL_START.map()).isEqualTo(MessageLikeEventType.POLL_START)
|
||||
assertThat(MessageEventType.UNSTABLE_POLL_END.map()).isEqualTo(MessageLikeEventType.UNSTABLE_POLL_END)
|
||||
assertThat(MessageEventType.UNSTABLE_POLL_RESPONSE.map()).isEqualTo(MessageLikeEventType.UNSTABLE_POLL_RESPONSE)
|
||||
assertThat(MessageEventType.UNSTABLE_POLL_START.map()).isEqualTo(MessageLikeEventType.UNSTABLE_POLL_START)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomHero
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomInfo
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import org.junit.Test
|
||||
|
||||
class RoomInfoExtTest {
|
||||
@Test
|
||||
fun `get non empty element Heroes`() {
|
||||
val result = aRustRoomInfo(
|
||||
isDirect = true,
|
||||
activeMembersCount = 2uL,
|
||||
heroes = listOf(aRustRoomHero())
|
||||
).elementHeroes()
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
MatrixUser(
|
||||
userId = UserId(A_USER_ID.value),
|
||||
displayName = "displayName",
|
||||
avatarUrl = "avatarUrl",
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `too many heroes and element Heroes is empty`() {
|
||||
val result = aRustRoomInfo(
|
||||
isDirect = true,
|
||||
activeMembersCount = 2uL,
|
||||
heroes = listOf(aRustRoomHero(), aRustRoomHero())
|
||||
).elementHeroes()
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `not direct and element Heroes is empty`() {
|
||||
val result = aRustRoomInfo(
|
||||
isDirect = false,
|
||||
activeMembersCount = 2uL,
|
||||
heroes = listOf(aRustRoomHero())
|
||||
).elementHeroes()
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `too many members and element Heroes is empty`() {
|
||||
val result = aRustRoomInfo(
|
||||
isDirect = true,
|
||||
activeMembersCount = 3uL,
|
||||
heroes = listOf(aRustRoomHero())
|
||||
).elementHeroes()
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
}
|
||||
|
|
@ -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.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import org.junit.Test
|
||||
|
||||
class RoomTypeKtTest {
|
||||
@Test
|
||||
fun toRoomType() {
|
||||
assert(null.toRoomType() == RoomType.Room)
|
||||
assert("m.space".toRoomType() == RoomType.Space)
|
||||
assert("m.other".toRoomType() == RoomType.Other("m.other"))
|
||||
}
|
||||
}
|
||||
|
|
@ -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.room
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.StateEventType as RustStateEventType
|
||||
|
||||
class StateEventTypeTest {
|
||||
@Test
|
||||
fun `mapping Rust type should work`() {
|
||||
assertThat(RustStateEventType.CALL_MEMBER.map()).isEqualTo(StateEventType.CALL_MEMBER)
|
||||
assertThat(RustStateEventType.POLICY_RULE_ROOM.map()).isEqualTo(StateEventType.POLICY_RULE_ROOM)
|
||||
assertThat(RustStateEventType.POLICY_RULE_SERVER.map()).isEqualTo(StateEventType.POLICY_RULE_SERVER)
|
||||
assertThat(RustStateEventType.POLICY_RULE_USER.map()).isEqualTo(StateEventType.POLICY_RULE_USER)
|
||||
assertThat(RustStateEventType.ROOM_ALIASES.map()).isEqualTo(StateEventType.ROOM_ALIASES)
|
||||
assertThat(RustStateEventType.ROOM_AVATAR.map()).isEqualTo(StateEventType.ROOM_AVATAR)
|
||||
assertThat(RustStateEventType.ROOM_CANONICAL_ALIAS.map()).isEqualTo(StateEventType.ROOM_CANONICAL_ALIAS)
|
||||
assertThat(RustStateEventType.ROOM_CREATE.map()).isEqualTo(StateEventType.ROOM_CREATE)
|
||||
assertThat(RustStateEventType.ROOM_ENCRYPTION.map()).isEqualTo(StateEventType.ROOM_ENCRYPTION)
|
||||
assertThat(RustStateEventType.ROOM_GUEST_ACCESS.map()).isEqualTo(StateEventType.ROOM_GUEST_ACCESS)
|
||||
assertThat(RustStateEventType.ROOM_HISTORY_VISIBILITY.map()).isEqualTo(StateEventType.ROOM_HISTORY_VISIBILITY)
|
||||
assertThat(RustStateEventType.ROOM_JOIN_RULES.map()).isEqualTo(StateEventType.ROOM_JOIN_RULES)
|
||||
assertThat(RustStateEventType.ROOM_MEMBER_EVENT.map()).isEqualTo(StateEventType.ROOM_MEMBER_EVENT)
|
||||
assertThat(RustStateEventType.ROOM_NAME.map()).isEqualTo(StateEventType.ROOM_NAME)
|
||||
assertThat(RustStateEventType.ROOM_PINNED_EVENTS.map()).isEqualTo(StateEventType.ROOM_PINNED_EVENTS)
|
||||
assertThat(RustStateEventType.ROOM_POWER_LEVELS.map()).isEqualTo(StateEventType.ROOM_POWER_LEVELS)
|
||||
assertThat(RustStateEventType.ROOM_SERVER_ACL.map()).isEqualTo(StateEventType.ROOM_SERVER_ACL)
|
||||
assertThat(RustStateEventType.ROOM_THIRD_PARTY_INVITE.map()).isEqualTo(StateEventType.ROOM_THIRD_PARTY_INVITE)
|
||||
assertThat(RustStateEventType.ROOM_TOMBSTONE.map()).isEqualTo(StateEventType.ROOM_TOMBSTONE)
|
||||
assertThat(RustStateEventType.ROOM_TOPIC.map()).isEqualTo(StateEventType.ROOM_TOPIC)
|
||||
assertThat(RustStateEventType.SPACE_CHILD.map()).isEqualTo(StateEventType.SPACE_CHILD)
|
||||
assertThat(RustStateEventType.SPACE_PARENT.map()).isEqualTo(StateEventType.SPACE_PARENT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping Kotlin type should work`() {
|
||||
assertThat(StateEventType.CALL_MEMBER.map()).isEqualTo(RustStateEventType.CALL_MEMBER)
|
||||
assertThat(StateEventType.POLICY_RULE_ROOM.map()).isEqualTo(RustStateEventType.POLICY_RULE_ROOM)
|
||||
assertThat(StateEventType.POLICY_RULE_SERVER.map()).isEqualTo(RustStateEventType.POLICY_RULE_SERVER)
|
||||
assertThat(StateEventType.POLICY_RULE_USER.map()).isEqualTo(RustStateEventType.POLICY_RULE_USER)
|
||||
assertThat(StateEventType.ROOM_ALIASES.map()).isEqualTo(RustStateEventType.ROOM_ALIASES)
|
||||
assertThat(StateEventType.ROOM_AVATAR.map()).isEqualTo(RustStateEventType.ROOM_AVATAR)
|
||||
assertThat(StateEventType.ROOM_CANONICAL_ALIAS.map()).isEqualTo(RustStateEventType.ROOM_CANONICAL_ALIAS)
|
||||
assertThat(StateEventType.ROOM_CREATE.map()).isEqualTo(RustStateEventType.ROOM_CREATE)
|
||||
assertThat(StateEventType.ROOM_ENCRYPTION.map()).isEqualTo(RustStateEventType.ROOM_ENCRYPTION)
|
||||
assertThat(StateEventType.ROOM_GUEST_ACCESS.map()).isEqualTo(RustStateEventType.ROOM_GUEST_ACCESS)
|
||||
assertThat(StateEventType.ROOM_HISTORY_VISIBILITY.map()).isEqualTo(RustStateEventType.ROOM_HISTORY_VISIBILITY)
|
||||
assertThat(StateEventType.ROOM_JOIN_RULES.map()).isEqualTo(RustStateEventType.ROOM_JOIN_RULES)
|
||||
assertThat(StateEventType.ROOM_MEMBER_EVENT.map()).isEqualTo(RustStateEventType.ROOM_MEMBER_EVENT)
|
||||
assertThat(StateEventType.ROOM_NAME.map()).isEqualTo(RustStateEventType.ROOM_NAME)
|
||||
assertThat(StateEventType.ROOM_PINNED_EVENTS.map()).isEqualTo(RustStateEventType.ROOM_PINNED_EVENTS)
|
||||
assertThat(StateEventType.ROOM_POWER_LEVELS.map()).isEqualTo(RustStateEventType.ROOM_POWER_LEVELS)
|
||||
assertThat(StateEventType.ROOM_SERVER_ACL.map()).isEqualTo(RustStateEventType.ROOM_SERVER_ACL)
|
||||
assertThat(StateEventType.ROOM_THIRD_PARTY_INVITE.map()).isEqualTo(RustStateEventType.ROOM_THIRD_PARTY_INVITE)
|
||||
assertThat(StateEventType.ROOM_TOMBSTONE.map()).isEqualTo(RustStateEventType.ROOM_TOMBSTONE)
|
||||
assertThat(StateEventType.ROOM_TOPIC.map()).isEqualTo(RustStateEventType.ROOM_TOPIC)
|
||||
assertThat(StateEventType.SPACE_CHILD.map()).isEqualTo(RustStateEventType.SPACE_CHILD)
|
||||
assertThat(StateEventType.SPACE_PARENT.map()).isEqualTo(RustStateEventType.SPACE_PARENT)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.location
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import org.junit.Test
|
||||
|
||||
class AssetTypeKtTest {
|
||||
@Test
|
||||
fun toInner() {
|
||||
assertThat(AssetType.SENDER.toInner()).isEqualTo(org.matrix.rustcomponents.sdk.AssetType.SENDER)
|
||||
assertThat(AssetType.PIN.toInner()).isEqualTo(org.matrix.rustcomponents.sdk.AssetType.PIN)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,13 +9,14 @@ package io.element.android.libraries.matrix.impl.room.member
|
|||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.roomMembers
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomMember
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoom
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomMembersIterator
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher.Source.CACHE
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher.Source.CACHE_AND_SERVER
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher.Source.SERVER
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_3
|
||||
|
|
@ -23,22 +24,16 @@ import io.element.android.libraries.matrix.test.A_USER_ID_4
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.MembershipState
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomMembersIterator
|
||||
import uniffi.matrix_sdk.RoomMemberRole
|
||||
|
||||
class RoomMemberListFetcherTest {
|
||||
@Test
|
||||
fun `fetchRoomMembers with CACHE source - emits cached members, if any`() = runTest {
|
||||
val room = FakeRustRoom(getMembersNoSync = {
|
||||
FakeRoomMembersIterator(
|
||||
FakeRustRoomMembersIterator(
|
||||
listOf(
|
||||
fakeRustRoomMember(A_USER_ID),
|
||||
fakeRustRoomMember(A_USER_ID_2),
|
||||
fakeRustRoomMember(A_USER_ID_3),
|
||||
aRustRoomMember(A_USER_ID),
|
||||
aRustRoomMember(A_USER_ID_2),
|
||||
aRustRoomMember(A_USER_ID_3),
|
||||
)
|
||||
)
|
||||
})
|
||||
|
|
@ -55,17 +50,13 @@ class RoomMemberListFetcherTest {
|
|||
val cachedItemsState = awaitItem()
|
||||
assertThat(cachedItemsState).isInstanceOf(MatrixRoomMembersState.Ready::class.java)
|
||||
assertThat((cachedItemsState as? MatrixRoomMembersState.Ready)?.roomMembers).hasSize(3)
|
||||
|
||||
// Assert only the 'no sync' method was called, so no new member sync happened
|
||||
assertThat(room.membersNoSyncCallCount).isEqualTo(1)
|
||||
assertThat(room.membersCallCount).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetchRoomMembers with CACHE source - emits empty list, if no members exist`() = runTest {
|
||||
val room = FakeRustRoom(getMembersNoSync = {
|
||||
FakeRoomMembersIterator(emptyList())
|
||||
FakeRustRoomMembersIterator(emptyList())
|
||||
})
|
||||
|
||||
val fetcher = RoomMemberListFetcher(room, Dispatchers.Default)
|
||||
|
|
@ -95,11 +86,11 @@ class RoomMemberListFetcherTest {
|
|||
@Test
|
||||
fun `fetchRoomMembers with CACHE source - emits all items at once`() = runTest {
|
||||
val room = FakeRustRoom(getMembersNoSync = {
|
||||
FakeRoomMembersIterator(
|
||||
FakeRustRoomMembersIterator(
|
||||
listOf(
|
||||
fakeRustRoomMember(A_USER_ID),
|
||||
fakeRustRoomMember(A_USER_ID_2),
|
||||
fakeRustRoomMember(A_USER_ID_3),
|
||||
aRustRoomMember(A_USER_ID),
|
||||
aRustRoomMember(A_USER_ID_2),
|
||||
aRustRoomMember(A_USER_ID_3),
|
||||
)
|
||||
)
|
||||
})
|
||||
|
|
@ -122,11 +113,11 @@ class RoomMemberListFetcherTest {
|
|||
@Test
|
||||
fun `fetchRoomMembers with SERVER source - emits only new members, if any`() = runTest {
|
||||
val room = FakeRustRoom(getMembers = {
|
||||
FakeRoomMembersIterator(
|
||||
FakeRustRoomMembersIterator(
|
||||
listOf(
|
||||
fakeRustRoomMember(A_USER_ID),
|
||||
fakeRustRoomMember(A_USER_ID_2),
|
||||
fakeRustRoomMember(A_USER_ID_3),
|
||||
aRustRoomMember(A_USER_ID),
|
||||
aRustRoomMember(A_USER_ID_2),
|
||||
aRustRoomMember(A_USER_ID_3),
|
||||
)
|
||||
)
|
||||
})
|
||||
|
|
@ -138,10 +129,6 @@ class RoomMemberListFetcherTest {
|
|||
assertThat(awaitItem()).isInstanceOf(MatrixRoomMembersState.Unknown::class.java)
|
||||
assertThat(awaitItem()).isInstanceOf(MatrixRoomMembersState.Pending::class.java)
|
||||
assertThat((awaitItem() as? MatrixRoomMembersState.Ready)?.roomMembers?.size).isEqualTo(3)
|
||||
|
||||
// Assert only the 'sync' method was called, so a new member sync happened
|
||||
assertThat(room.membersNoSyncCallCount).isEqualTo(0)
|
||||
assertThat(room.membersCallCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,14 +150,14 @@ class RoomMemberListFetcherTest {
|
|||
fun `fetchRoomMembers with CACHE_AND_SERVER source - returns cached items first, then new ones`() = runTest {
|
||||
val room = FakeRustRoom(
|
||||
getMembersNoSync = {
|
||||
FakeRoomMembersIterator(listOf(fakeRustRoomMember(A_USER_ID_4)))
|
||||
FakeRustRoomMembersIterator(listOf(aRustRoomMember(A_USER_ID_4)))
|
||||
},
|
||||
getMembers = {
|
||||
FakeRoomMembersIterator(
|
||||
FakeRustRoomMembersIterator(
|
||||
listOf(
|
||||
fakeRustRoomMember(A_USER_ID),
|
||||
fakeRustRoomMember(A_USER_ID_2),
|
||||
fakeRustRoomMember(A_USER_ID_3),
|
||||
aRustRoomMember(A_USER_ID),
|
||||
aRustRoomMember(A_USER_ID_2),
|
||||
aRustRoomMember(A_USER_ID_3),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -196,76 +183,6 @@ class RoomMemberListFetcherTest {
|
|||
assertThat(ready).isInstanceOf(MatrixRoomMembersState.Ready::class.java)
|
||||
assertThat(ready.roomMembers()).hasSize(3)
|
||||
}
|
||||
|
||||
// Assert both member methods were called, so both the cache was hit and a new member sync happened
|
||||
assertThat(room.membersNoSyncCallCount).isEqualTo(1)
|
||||
assertThat(room.membersCallCount).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FakeRustRoom(
|
||||
private val getMembers: () -> RoomMembersIterator = { FakeRoomMembersIterator() },
|
||||
private val getMembersNoSync: () -> RoomMembersIterator = { FakeRoomMembersIterator() },
|
||||
) : Room(NoPointer) {
|
||||
var membersCallCount = 0
|
||||
var membersNoSyncCallCount = 0
|
||||
|
||||
override fun id(): String {
|
||||
return A_ROOM_ID.value
|
||||
}
|
||||
|
||||
override suspend fun members(): RoomMembersIterator {
|
||||
membersCallCount++
|
||||
return getMembers()
|
||||
}
|
||||
|
||||
override suspend fun membersNoSync(): RoomMembersIterator {
|
||||
membersNoSyncCallCount++
|
||||
return getMembersNoSync()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
class FakeRoomMembersIterator(
|
||||
private var members: List<RoomMember>? = null
|
||||
) : RoomMembersIterator(NoPointer) {
|
||||
override fun len(): UInt {
|
||||
return members?.size?.toUInt() ?: 0u
|
||||
}
|
||||
|
||||
override fun nextChunk(chunkSize: UInt): List<RoomMember>? {
|
||||
if (members?.isEmpty() == true) {
|
||||
return null
|
||||
}
|
||||
return members?.let {
|
||||
val result = it.take(chunkSize.toInt())
|
||||
members = it.subList(result.size, it.size)
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fakeRustRoomMember(
|
||||
userId: UserId,
|
||||
displayName: String? = null,
|
||||
avatarUrl: String? = null,
|
||||
membership: MembershipState = MembershipState.JOIN,
|
||||
isNameAmbiguous: Boolean = false,
|
||||
powerLevel: Long = 0L,
|
||||
isIgnored: Boolean = false,
|
||||
role: RoomMemberRole = RoomMemberRole.USER,
|
||||
) = RoomMember(
|
||||
userId = userId.value,
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
membership = membership,
|
||||
isNameAmbiguous = isNameAmbiguous,
|
||||
powerLevel = powerLevel,
|
||||
normalizedPowerLevel = powerLevel,
|
||||
isIgnored = isIgnored,
|
||||
suggestedRoleForPowerLevel = role,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.member
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import org.junit.Test
|
||||
import uniffi.matrix_sdk.RoomMemberRole
|
||||
import org.matrix.rustcomponents.sdk.MembershipState as RustMembershipState
|
||||
|
||||
class RoomMemberMapperTest {
|
||||
@Test
|
||||
fun mapRole() {
|
||||
assertThat(RoomMemberMapper.mapRole(RoomMemberRole.USER)).isEqualTo(RoomMember.Role.USER)
|
||||
assertThat(RoomMemberMapper.mapRole(RoomMemberRole.MODERATOR)).isEqualTo(RoomMember.Role.MODERATOR)
|
||||
assertThat(RoomMemberMapper.mapRole(RoomMemberRole.ADMINISTRATOR)).isEqualTo(RoomMember.Role.ADMIN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mapMembership() {
|
||||
assertThat(RoomMemberMapper.mapMembership(RustMembershipState.BAN)).isEqualTo(RoomMembershipState.BAN)
|
||||
assertThat(RoomMemberMapper.mapMembership(RustMembershipState.INVITE)).isEqualTo(RoomMembershipState.INVITE)
|
||||
assertThat(RoomMemberMapper.mapMembership(RustMembershipState.JOIN)).isEqualTo(RoomMembershipState.JOIN)
|
||||
assertThat(RoomMemberMapper.mapMembership(RustMembershipState.KNOCK)).isEqualTo(RoomMembershipState.KNOCK)
|
||||
assertThat(RoomMemberMapper.mapMembership(RustMembershipState.LEAVE)).isEqualTo(RoomMembershipState.LEAVE)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.powerlevels
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomPowerLevels
|
||||
import org.junit.Test
|
||||
|
||||
class RoomPowerLevelsMapperTest {
|
||||
@Test
|
||||
fun `test that mapping of RoomPowerLevels is correct`() {
|
||||
assertThat(
|
||||
RoomPowerLevelsMapper.map(
|
||||
aRustRoomPowerLevels(
|
||||
ban = 1,
|
||||
invite = 2,
|
||||
kick = 3,
|
||||
redact = 4,
|
||||
eventsDefault = 5,
|
||||
stateDefault = 6,
|
||||
usersDefault = 7,
|
||||
roomName = 8,
|
||||
roomAvatar = 9,
|
||||
roomTopic = 10,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
MatrixRoomPowerLevels(
|
||||
ban = 1,
|
||||
invite = 2,
|
||||
kick = 3,
|
||||
sendEvents = 5,
|
||||
redactEvents = 4,
|
||||
roomName = 8,
|
||||
roomAvatar = 9,
|
||||
roomTopic = 10,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.preview
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreview
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomPreview
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import org.junit.Test
|
||||
|
||||
class RoomPreviewMapperTest {
|
||||
@Test
|
||||
fun `map should map values 1`() {
|
||||
assertThat(
|
||||
RoomPreviewMapper.map(
|
||||
aRustRoomPreview(
|
||||
isJoined = false,
|
||||
isInvited = false,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
RoomPreview(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = A_ROOM_ALIAS,
|
||||
name = "name",
|
||||
topic = "topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 1L,
|
||||
roomType = RoomType.Room,
|
||||
isHistoryWorldReadable = true,
|
||||
isJoined = false,
|
||||
isInvited = false,
|
||||
isPublic = true,
|
||||
canKnock = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map should map values 2`() {
|
||||
assertThat(
|
||||
RoomPreviewMapper.map(
|
||||
aRustRoomPreview(
|
||||
canonicalAlias = null,
|
||||
isPublic = false,
|
||||
canKnock = false,
|
||||
)
|
||||
)
|
||||
).isEqualTo(
|
||||
RoomPreview(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = null,
|
||||
name = "name",
|
||||
topic = "topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 1L,
|
||||
roomType = RoomType.Room,
|
||||
isHistoryWorldReadable = true,
|
||||
isJoined = true,
|
||||
isInvited = true,
|
||||
isPublic = false,
|
||||
canKnock = false,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustRoomDescription
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.roomdirectory.aRoomDescription
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.PublicRoomJoinRule
|
||||
|
||||
class RoomDescriptionMapperTest {
|
||||
@Test
|
||||
fun map() {
|
||||
assertThat(RoomDescriptionMapper().map(aRustRoomDescription())).isEqualTo(
|
||||
aRoomDescription(
|
||||
roomId = A_ROOM_ID,
|
||||
name = "name",
|
||||
topic = "topic",
|
||||
alias = A_ROOM_ALIAS,
|
||||
avatarUrl = "avatarUrl",
|
||||
joinRule = RoomDescription.JoinRule.PUBLIC,
|
||||
isWorldReadable = true,
|
||||
joinedMembers = 2L
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mapWithNullAlias() {
|
||||
assertThat(RoomDescriptionMapper().map(aRustRoomDescription().copy(alias = null)).alias).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map join rule`() {
|
||||
assertThat(PublicRoomJoinRule.PUBLIC.map()).isEqualTo(RoomDescription.JoinRule.PUBLIC)
|
||||
assertThat(PublicRoomJoinRule.KNOCK.map()).isEqualTo(RoomDescription.JoinRule.KNOCK)
|
||||
assertThat(null.map()).isEqualTo(RoomDescription.JoinRule.UNKNOWN)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,11 @@ 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.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
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.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -22,19 +21,12 @@ import kotlinx.coroutines.test.StandardTestDispatcher
|
|||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.RoomHero
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||
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.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
|
||||
|
|
@ -48,7 +40,7 @@ class RoomSummaryListProcessorTest {
|
|||
summaries.value = listOf(aRoomSummary())
|
||||
val processor = createProcessor()
|
||||
|
||||
val newEntry = FakeRoomListItem(A_ROOM_ID_2)
|
||||
val newEntry = FakeRustRoomListItem(A_ROOM_ID_2)
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(newEntry, newEntry, newEntry))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(4)
|
||||
|
|
@ -59,7 +51,7 @@ class RoomSummaryListProcessorTest {
|
|||
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled())
|
||||
val processor = createProcessor()
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(FakeRoomListItem(A_ROOM_ID_2))))
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(FakeRustRoomListItem(A_ROOM_ID_2))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(2)
|
||||
assertThat(summaries.value.last().roomId).isEqualTo(A_ROOM_ID_2)
|
||||
|
|
@ -69,7 +61,7 @@ class RoomSummaryListProcessorTest {
|
|||
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled())
|
||||
val processor = createProcessor()
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(FakeRoomListItem(A_ROOM_ID_2))))
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(FakeRustRoomListItem(A_ROOM_ID_2))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(2)
|
||||
assertThat(summaries.value.first().roomId).isEqualTo(A_ROOM_ID_2)
|
||||
|
|
@ -81,7 +73,7 @@ class RoomSummaryListProcessorTest {
|
|||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), FakeRoomListItem(A_ROOM_ID_2))))
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), FakeRustRoomListItem(A_ROOM_ID_2))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(1)
|
||||
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_2)
|
||||
|
|
@ -93,7 +85,7 @@ class RoomSummaryListProcessorTest {
|
|||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), FakeRoomListItem(A_ROOM_ID_2))))
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), FakeRustRoomListItem(A_ROOM_ID_2))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(2)
|
||||
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_2)
|
||||
|
|
@ -157,6 +149,18 @@ class RoomSummaryListProcessorTest {
|
|||
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Reset removes all entries and add the provided ones`() = runTest {
|
||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||
val processor = createProcessor()
|
||||
val index = 0
|
||||
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Reset(listOf(FakeRustRoomListItem(A_ROOM_ID_3)))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(1)
|
||||
assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_3)
|
||||
}
|
||||
|
||||
private fun TestScope.createProcessor() = RoomSummaryListProcessor(
|
||||
summaries,
|
||||
fakeRoomListService,
|
||||
|
|
@ -185,85 +189,3 @@ class RoomSummaryListProcessorTest {
|
|||
override fun subscribeToRooms(roomIds: List<String>, settings: RoomSubscription?) = Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun aRustRoomInfo(
|
||||
id: String = A_ROOM_ID.value,
|
||||
displayName: String = A_ROOM_NAME,
|
||||
rawName: String = A_ROOM_NAME,
|
||||
topic: String? = null,
|
||||
avatarUrl: String? = null,
|
||||
isDirect: Boolean = false,
|
||||
isPublic: Boolean = false,
|
||||
isSpace: Boolean = false,
|
||||
isTombstoned: Boolean = false,
|
||||
isFavourite: Boolean = false,
|
||||
canonicalAlias: String? = null,
|
||||
alternativeAliases: List<String> = listOf(),
|
||||
membership: Membership = Membership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
heroes: List<RoomHero> = listOf(),
|
||||
activeMembersCount: ULong = 0uL,
|
||||
invitedMembersCount: ULong = 0uL,
|
||||
joinedMembersCount: ULong = 0uL,
|
||||
userPowerLevels: Map<String, Long> = mapOf(),
|
||||
highlightCount: ULong = 0uL,
|
||||
notificationCount: ULong = 0uL,
|
||||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
activeRoomCallParticipants: List<String> = listOf(),
|
||||
isMarkedUnread: Boolean = false,
|
||||
numUnreadMessages: ULong = 0uL,
|
||||
numUnreadNotifications: ULong = 0uL,
|
||||
numUnreadMentions: ULong = 0uL,
|
||||
pinnedEventIds: List<String> = listOf(),
|
||||
roomCreator: UserId? = null,
|
||||
) = RoomInfo(
|
||||
id = id,
|
||||
displayName = displayName,
|
||||
rawName = rawName,
|
||||
topic = topic,
|
||||
avatarUrl = avatarUrl,
|
||||
isDirect = isDirect,
|
||||
isPublic = isPublic,
|
||||
isSpace = isSpace,
|
||||
isTombstoned = isTombstoned,
|
||||
isFavourite = isFavourite,
|
||||
canonicalAlias = canonicalAlias,
|
||||
alternativeAliases = alternativeAliases,
|
||||
membership = membership,
|
||||
inviter = inviter,
|
||||
heroes = heroes,
|
||||
activeMembersCount = activeMembersCount,
|
||||
invitedMembersCount = invitedMembersCount,
|
||||
joinedMembersCount = joinedMembersCount,
|
||||
userPowerLevels = userPowerLevels,
|
||||
highlightCount = highlightCount,
|
||||
notificationCount = notificationCount,
|
||||
cachedUserDefinedNotificationMode = userDefinedNotificationMode,
|
||||
hasRoomCall = hasRoomCall,
|
||||
activeRoomCallParticipants = activeRoomCallParticipants,
|
||||
isMarkedUnread = isMarkedUnread,
|
||||
numUnreadMessages = numUnreadMessages,
|
||||
numUnreadNotifications = numUnreadNotifications,
|
||||
numUnreadMentions = numUnreadMentions,
|
||||
pinnedEventIds = pinnedEventIds,
|
||||
creator = roomCreator?.value,
|
||||
)
|
||||
|
||||
class FakeRoomListItem(
|
||||
private val roomId: RoomId,
|
||||
private val roomInfo: RoomInfo = aRustRoomInfo(id = roomId.value),
|
||||
private val latestEvent: EventTimelineItem? = null,
|
||||
) : RoomListItem(NoPointer) {
|
||||
override fun id(): String {
|
||||
return roomId.value
|
||||
}
|
||||
|
||||
override suspend fun roomInfo(): RoomInfo {
|
||||
return roomInfo
|
||||
}
|
||||
|
||||
override suspend fun latestEvent(): EventTimelineItem? {
|
||||
return latestEvent
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.server
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultUserServerResolverTest {
|
||||
@Test
|
||||
fun resolve() {
|
||||
// Given
|
||||
val userServerResolver = DefaultUserServerResolver(FakeMatrixClient(
|
||||
userIdServerNameLambda = { "dummy.org" }
|
||||
))
|
||||
|
||||
// When
|
||||
val result = userServerResolver.resolve()
|
||||
|
||||
// Then
|
||||
assertThat(result).isEqualTo("dummy.org")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.sync
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.SyncServiceState
|
||||
|
||||
class AppStateMapperKtTest {
|
||||
@Test
|
||||
fun toSyncState() {
|
||||
assertThat(SyncServiceState.IDLE.toSyncState()).isEqualTo(SyncState.Idle)
|
||||
assertThat(SyncServiceState.RUNNING.toSyncState()).isEqualTo(SyncState.Running)
|
||||
assertThat(SyncServiceState.TERMINATED.toSyncState()).isEqualTo(SyncState.Terminated)
|
||||
assertThat(SyncServiceState.ERROR.toSyncState()).isEqualTo(SyncState.Error)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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.timeline
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustTimelineDiff
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
|
||||
import io.element.android.libraries.matrix.test.A_UNIQUE_ID_2
|
||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.TimelineChange
|
||||
|
||||
class MatrixTimelineDiffProcessorTest {
|
||||
private val timelineItems = MutableStateFlow<List<MatrixTimelineItem>>(emptyList())
|
||||
|
||||
private val anEvent = MatrixTimelineItem.Event(A_UNIQUE_ID, anEventTimelineItem())
|
||||
private val anEvent2 = MatrixTimelineItem.Event(A_UNIQUE_ID_2, anEventTimelineItem())
|
||||
|
||||
@Test
|
||||
fun `Append adds new entries at the end of the list`() = runTest {
|
||||
timelineItems.value = listOf(anEvent)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.APPEND)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(2)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
MatrixTimelineItem.Other,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
||||
timelineItems.value = listOf(anEvent)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.PUSH_BACK)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(2)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
MatrixTimelineItem.Other,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
||||
timelineItems.value = listOf(anEvent)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.PUSH_FRONT)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(2)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
MatrixTimelineItem.Other,
|
||||
anEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Set replaces an entry at some index`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.SET)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(2)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
MatrixTimelineItem.Other
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Insert inserts a new entry at the provided index`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.INSERT)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(3)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
MatrixTimelineItem.Other,
|
||||
anEvent2,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Remove removes an entry at some index`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, MatrixTimelineItem.Other, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.REMOVE)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(2)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
anEvent2,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PopBack removes an entry at the end of the list`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.POP_BACK)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(1)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PopFront removes an entry at the start of the list`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.POP_FRONT)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(1)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent2,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Clear removes all the entries`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.CLEAR)))
|
||||
assertThat(timelineItems.value).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Truncate removes all entries after the provided length`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, MatrixTimelineItem.Other, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.TRUNCATE)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(1)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
anEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Reset removes all entries and add the provided ones`() = runTest {
|
||||
timelineItems.value = listOf(anEvent, MatrixTimelineItem.Other, anEvent2)
|
||||
val processor = createProcessor()
|
||||
processor.postDiffs(listOf(FakeRustTimelineDiff(change = TimelineChange.RESET)))
|
||||
assertThat(timelineItems.value.count()).isEqualTo(1)
|
||||
assertThat(timelineItems.value).containsExactly(
|
||||
MatrixTimelineItem.Other,
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestScope.createProcessor(): MatrixTimelineDiffProcessor {
|
||||
val timelineEventContentMapper = TimelineEventContentMapper()
|
||||
val timelineItemMapper = MatrixTimelineItemMapper(
|
||||
fetchDetailsForEvent = { _ -> Result.success(Unit) },
|
||||
coroutineScope = this,
|
||||
virtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
||||
eventTimelineItemMapper = EventTimelineItemMapper(
|
||||
contentMapper = timelineEventContentMapper
|
||||
)
|
||||
)
|
||||
return MatrixTimelineDiffProcessor(
|
||||
timelineItems,
|
||||
timelineItemFactory = timelineItemMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.timeline
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.ReceiptType as RustReceiptType
|
||||
|
||||
class ReceiptTypeMapperKtTest {
|
||||
@Test
|
||||
fun toRustReceiptType() {
|
||||
assertThat(ReceiptType.READ.toRustReceiptType()).isEqualTo(RustReceiptType.READ)
|
||||
assertThat(ReceiptType.READ_PRIVATE.toRustReceiptType()).isEqualTo(RustReceiptType.READ_PRIVATE)
|
||||
assertThat(ReceiptType.FULLY_READ.toRustReceiptType()).isEqualTo(RustReceiptType.FULLY_READ)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.timeline.postprocessor
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.timeline.aMessageContent
|
||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||
|
||||
internal val roomCreateEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.create"),
|
||||
event = anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))
|
||||
)
|
||||
internal val roomCreatorJoinEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.member"),
|
||||
event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))
|
||||
)
|
||||
internal val otherMemberJoinEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.member_other"),
|
||||
event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))
|
||||
)
|
||||
internal val messageEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.message"),
|
||||
event = anEventTimelineItem(content = aMessageContent("hi"))
|
||||
)
|
||||
internal val messageEvent2 = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.message2"),
|
||||
event = anEventTimelineItem(content = aMessageContent("hello"))
|
||||
)
|
||||
internal val dayEvent = MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("day"),
|
||||
virtual = VirtualTimelineItem.DayDivider(0),
|
||||
)
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.timeline.postprocessor
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||
import org.junit.Test
|
||||
|
||||
class LastForwardIndicatorsPostProcessorTest {
|
||||
@Test
|
||||
fun `LastForwardIndicatorsPostProcessor does not alter the items with mode not FOCUSED_ON_EVENT`() {
|
||||
val sut = LastForwardIndicatorsPostProcessor(Timeline.Mode.LIVE)
|
||||
val result = sut.process(listOf(messageEvent), true)
|
||||
assertThat(result).containsExactly(messageEvent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LastForwardIndicatorsPostProcessor does not alter the items with mode FOCUSED_ON_EVENT but timeline not initialized`() {
|
||||
val sut = LastForwardIndicatorsPostProcessor(Timeline.Mode.FOCUSED_ON_EVENT)
|
||||
val result = sut.process(listOf(messageEvent), false)
|
||||
assertThat(result).containsExactly(messageEvent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LastForwardIndicatorsPostProcessor add virtual items`() {
|
||||
val sut = LastForwardIndicatorsPostProcessor(Timeline.Mode.FOCUSED_ON_EVENT)
|
||||
val result = sut.process(listOf(messageEvent), true)
|
||||
assertThat(result).containsExactly(
|
||||
messageEvent,
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("last_forward_indicator_${messageEvent.uniqueId}"),
|
||||
virtual = VirtualTimelineItem.LastForwardIndicator
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LastForwardIndicatorsPostProcessor add virtual items on empty list`() {
|
||||
val sut = LastForwardIndicatorsPostProcessor(Timeline.Mode.FOCUSED_ON_EVENT)
|
||||
val result = sut.process(listOf(), true)
|
||||
assertThat(result).containsExactly(
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("last_forward_indicator_fake_id"),
|
||||
virtual = VirtualTimelineItem.LastForwardIndicator
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LastForwardIndicatorsPostProcessor add virtual items but does not alter the list if called a second time`() {
|
||||
val sut = LastForwardIndicatorsPostProcessor(Timeline.Mode.FOCUSED_ON_EVENT)
|
||||
// Process a first time
|
||||
sut.process(listOf(messageEvent), true)
|
||||
// Process a second time with the same Event
|
||||
val result = sut.process(listOf(messageEvent), true)
|
||||
assertThat(result).containsExactly(
|
||||
messageEvent,
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("last_forward_indicator_${messageEvent.uniqueId}"),
|
||||
virtual = VirtualTimelineItem.LastForwardIndicator
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LastForwardIndicatorsPostProcessor add virtual items each time it is called with new Events`() {
|
||||
val sut = LastForwardIndicatorsPostProcessor(Timeline.Mode.FOCUSED_ON_EVENT)
|
||||
// Process a first time
|
||||
sut.process(listOf(dayEvent, messageEvent), true)
|
||||
// Process a second time with the same Event
|
||||
val result = sut.process(listOf(dayEvent, messageEvent, messageEvent2), true)
|
||||
assertThat(result).containsExactly(
|
||||
dayEvent,
|
||||
messageEvent,
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("last_forward_indicator_${messageEvent.uniqueId}"),
|
||||
virtual = VirtualTimelineItem.LastForwardIndicator
|
||||
),
|
||||
messageEvent2,
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("last_forward_indicator_${messageEvent2.uniqueId}"),
|
||||
virtual = VirtualTimelineItem.LastForwardIndicator
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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.timeline.postprocessor
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import org.junit.Test
|
||||
|
||||
class LoadingIndicatorsPostProcessorTest {
|
||||
@Test
|
||||
fun `LoadingIndicatorsPostProcessor does not alter the items is the timeline is not initialized`() {
|
||||
val sut = LoadingIndicatorsPostProcessor(FakeSystemClock())
|
||||
val result = sut.process(
|
||||
items = listOf(messageEvent, messageEvent2),
|
||||
isTimelineInitialized = false,
|
||||
hasMoreToLoadBackward = true,
|
||||
hasMoreToLoadForward = true,
|
||||
)
|
||||
assertThat(result).containsExactly(messageEvent, messageEvent2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LoadingIndicatorsPostProcessor adds Loading indicator at the top of the list if hasMoreToLoadBackward is true`() {
|
||||
val clock = FakeSystemClock()
|
||||
val sut = LoadingIndicatorsPostProcessor(clock)
|
||||
val result = sut.process(
|
||||
items = listOf(messageEvent, messageEvent2),
|
||||
isTimelineInitialized = true,
|
||||
hasMoreToLoadBackward = true,
|
||||
hasMoreToLoadForward = false,
|
||||
)
|
||||
assertThat(result).containsExactly(
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("BackwardLoadingIndicator"),
|
||||
virtual = VirtualTimelineItem.LoadingIndicator(
|
||||
direction = Timeline.PaginationDirection.BACKWARDS,
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
),
|
||||
messageEvent,
|
||||
messageEvent2,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LoadingIndicatorsPostProcessor adds Loading indicator at the bottom of the list if hasMoreToLoadForward is true`() {
|
||||
val clock = FakeSystemClock()
|
||||
val sut = LoadingIndicatorsPostProcessor(clock)
|
||||
val result = sut.process(
|
||||
items = listOf(messageEvent, messageEvent2),
|
||||
isTimelineInitialized = true,
|
||||
hasMoreToLoadBackward = false,
|
||||
hasMoreToLoadForward = true,
|
||||
)
|
||||
assertThat(result).containsExactly(
|
||||
messageEvent,
|
||||
messageEvent2,
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("ForwardLoadingIndicator"),
|
||||
virtual = VirtualTimelineItem.LoadingIndicator(
|
||||
direction = Timeline.PaginationDirection.FORWARDS,
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LoadingIndicatorsPostProcessor adds Loading indicator at the bottom and at the top of the list`() {
|
||||
val clock = FakeSystemClock()
|
||||
val sut = LoadingIndicatorsPostProcessor(clock)
|
||||
val result = sut.process(
|
||||
items = listOf(messageEvent, messageEvent2),
|
||||
isTimelineInitialized = true,
|
||||
hasMoreToLoadBackward = true,
|
||||
hasMoreToLoadForward = true,
|
||||
)
|
||||
assertThat(result).containsExactly(
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("BackwardLoadingIndicator"),
|
||||
virtual = VirtualTimelineItem.LoadingIndicator(
|
||||
direction = Timeline.PaginationDirection.BACKWARDS,
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
),
|
||||
messageEvent,
|
||||
messageEvent2,
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("ForwardLoadingIndicator"),
|
||||
virtual = VirtualTimelineItem.LoadingIndicator(
|
||||
direction = Timeline.PaginationDirection.FORWARDS,
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LoadingIndicatorsPostProcessor only adds 1 Loading indicator if there is no items in the list`() {
|
||||
val clock = FakeSystemClock()
|
||||
val sut = LoadingIndicatorsPostProcessor(clock)
|
||||
val result = sut.process(
|
||||
items = listOf(),
|
||||
isTimelineInitialized = true,
|
||||
hasMoreToLoadBackward = true,
|
||||
hasMoreToLoadForward = true,
|
||||
)
|
||||
assertThat(result).containsExactly(
|
||||
MatrixTimelineItem.Virtual(
|
||||
uniqueId = UniqueId("BackwardLoadingIndicator"),
|
||||
virtual = VirtualTimelineItem.LoadingIndicator(
|
||||
direction = Timeline.PaginationDirection.BACKWARDS,
|
||||
timestamp = clock.epochMillis()
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,37 +8,11 @@
|
|||
package io.element.android.libraries.matrix.impl.timeline.postprocessor
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.timeline.aMessageContent
|
||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||
import org.junit.Test
|
||||
|
||||
class RoomBeginningPostProcessorTest {
|
||||
private val roomCreateEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.create"),
|
||||
event = anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))
|
||||
)
|
||||
private val roomCreatorJoinEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.member"),
|
||||
event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, null, MembershipChange.JOINED))
|
||||
)
|
||||
private val otherMemberJoinEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.member_other"),
|
||||
event = anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, null, MembershipChange.JOINED))
|
||||
)
|
||||
private val messageEvent = MatrixTimelineItem.Event(
|
||||
uniqueId = UniqueId("m.room.message"),
|
||||
event = anEventTimelineItem(content = aMessageContent("hi"))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `processor returns empty list when empty list is provided`() {
|
||||
val processor = RoomBeginningPostProcessor(Timeline.Mode.LIVE)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.usersearch
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUserProfile
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import org.junit.Test
|
||||
|
||||
class UserProfileMapperTest {
|
||||
@Test
|
||||
fun map() {
|
||||
assertThat(UserProfileMapper.map(aRustUserProfile(A_USER_ID.value, "displayName", "avatarUrl")))
|
||||
.isEqualTo(MatrixUser(A_USER_ID, "displayName", "avatarUrl"))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.usersearch
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSearchUsersResults
|
||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustUserProfile
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.junit.Test
|
||||
|
||||
class UserSearchResultMapperTest {
|
||||
@Test
|
||||
fun `map limited list`() {
|
||||
assertThat(
|
||||
UserSearchResultMapper.map(
|
||||
aRustSearchUsersResults(
|
||||
results = listOf(aRustUserProfile(A_USER_ID.value, "displayName", "avatarUrl")),
|
||||
limited = true,
|
||||
)
|
||||
)
|
||||
)
|
||||
.isEqualTo(
|
||||
MatrixSearchUserResults(
|
||||
results = listOf(MatrixUser(A_USER_ID, "displayName", "avatarUrl")).toImmutableList(),
|
||||
limited = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map not limited list`() {
|
||||
assertThat(
|
||||
UserSearchResultMapper.map(
|
||||
aRustSearchUsersResults(
|
||||
results = listOf(aRustUserProfile(A_USER_ID.value, "displayName", "avatarUrl")),
|
||||
limited = false,
|
||||
)
|
||||
)
|
||||
)
|
||||
.isEqualTo(
|
||||
MatrixSearchUserResults(
|
||||
results = listOf(MatrixUser(A_USER_ID, "displayName", "avatarUrl")).toImmutableList(),
|
||||
limited = false,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.util
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
|
||||
import io.element.android.libraries.sessionstorage.test.aSessionData
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class SessionPathsProviderTest {
|
||||
@Test
|
||||
fun `if session is not found, provides returns null`() = runTest {
|
||||
val sut = SessionPathsProvider(InMemorySessionStore())
|
||||
val result = sut.provides(A_SESSION_ID)
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `if session is found, provides returns the data`() = runTest {
|
||||
val store = InMemorySessionStore()
|
||||
val sut = SessionPathsProvider(store)
|
||||
store.storeData(
|
||||
aSessionData(
|
||||
sessionPath = "/a/path/to/a/session",
|
||||
cachePath = "/a/path/to/a/cache",
|
||||
)
|
||||
)
|
||||
val result = sut.provides(A_SESSION_ID)!!
|
||||
assertThat(result.fileDirectory.absolutePath).isEqualTo("/a/path/to/a/session")
|
||||
assertThat(result.cacheDirectory.absolutePath).isEqualTo("/a/path/to/a/cache")
|
||||
}
|
||||
}
|
||||
|
|
@ -520,7 +520,7 @@ fun aRoomInfo(
|
|||
isTombstoned: Boolean = false,
|
||||
isFavorite: Boolean = false,
|
||||
canonicalAlias: RoomAlias? = null,
|
||||
alternativeAliases: List<String> = emptyList(),
|
||||
alternativeAliases: List<RoomAlias> = emptyList(),
|
||||
currentUserMembership: CurrentUserMembership = CurrentUserMembership.JOINED,
|
||||
inviter: RoomMember? = null,
|
||||
activeMembersCount: Long = 1,
|
||||
|
|
@ -531,7 +531,7 @@ fun aRoomInfo(
|
|||
userDefinedNotificationMode: RoomNotificationMode? = null,
|
||||
hasRoomCall: Boolean = false,
|
||||
userPowerLevels: ImmutableMap<UserId, Long> = persistentMapOf(),
|
||||
activeRoomCallParticipants: List<String> = emptyList(),
|
||||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreator: UserId? = null,
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ class DefaultFirebaseNewTokenHandlerTest {
|
|||
val pusherSubscriber = FakePusherSubscriber(registerPusherResult = registerPusherResult)
|
||||
val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler(
|
||||
sessionStore = InMemoryMultiSessionsStore().apply {
|
||||
storeData(aSessionData(A_USER_ID))
|
||||
storeData(aSessionData(A_USER_ID_2))
|
||||
storeData(aSessionData(A_USER_ID_3))
|
||||
storeData(aSessionData(A_USER_ID.value))
|
||||
storeData(aSessionData(A_USER_ID_2.value))
|
||||
storeData(aSessionData(A_USER_ID_3.value))
|
||||
},
|
||||
matrixClientProvider = FakeMatrixClientProvider { sessionId ->
|
||||
when (sessionId) {
|
||||
|
|
@ -90,7 +90,7 @@ class DefaultFirebaseNewTokenHandlerTest {
|
|||
val pusherSubscriber = FakePusherSubscriber(registerPusherResult = registerPusherResult)
|
||||
val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler(
|
||||
sessionStore = InMemoryMultiSessionsStore().apply {
|
||||
storeData(aSessionData(A_USER_ID))
|
||||
storeData(aSessionData(A_USER_ID.value))
|
||||
},
|
||||
matrixClientProvider = FakeMatrixClientProvider {
|
||||
Result.failure(IllegalStateException())
|
||||
|
|
@ -114,7 +114,7 @@ class DefaultFirebaseNewTokenHandlerTest {
|
|||
val pusherSubscriber = FakePusherSubscriber(registerPusherResult = registerPusherResult)
|
||||
val firebaseNewTokenHandler = createDefaultFirebaseNewTokenHandler(
|
||||
sessionStore = InMemoryMultiSessionsStore().apply {
|
||||
storeData(aSessionData(A_USER_ID))
|
||||
storeData(aSessionData(A_USER_ID.value))
|
||||
},
|
||||
matrixClientProvider = FakeMatrixClientProvider {
|
||||
Result.success(aMatrixClient1)
|
||||
|
|
|
|||
|
|
@ -7,19 +7,19 @@
|
|||
|
||||
package io.element.android.libraries.sessionstorage.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
|
||||
fun aSessionData(
|
||||
sessionId: SessionId = SessionId("@alice:server.org"),
|
||||
sessionId: String = "@alice:server.org",
|
||||
deviceId: String = "aDeviceId",
|
||||
isTokenValid: Boolean = false,
|
||||
sessionPath: String = "/a/path/to/a/session",
|
||||
cachePath: String = "/a/path/to/a/cache",
|
||||
): SessionData {
|
||||
return SessionData(
|
||||
userId = sessionId.value,
|
||||
deviceId = "aDeviceId",
|
||||
userId = sessionId,
|
||||
deviceId = deviceId,
|
||||
accessToken = "anAccessToken",
|
||||
refreshToken = "aRefreshToken",
|
||||
homeserverUrl = "aHomeserverUrl",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ private const val versionMinor = 6
|
|||
// Note: even values are reserved for regular release, odd values for hotfix release.
|
||||
// When creating a hotfix, you should decrease the value, since the current value
|
||||
// is the value for the next regular release.
|
||||
private const val versionPatch = 2
|
||||
private const val versionPatch = 3
|
||||
|
||||
object Versions {
|
||||
val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch
|
||||
|
|
|
|||
|
|
@ -68,11 +68,12 @@ class KonsistClassNameTest {
|
|||
.withoutName(
|
||||
"FakeFileSystem",
|
||||
"FakeImageLoader",
|
||||
"FakeRustRoom",
|
||||
)
|
||||
.assertTrue {
|
||||
val interfaceName = it.name.replace("Fake", "")
|
||||
it.name.startsWith("Fake") &&
|
||||
val interfaceName = it.name
|
||||
.replace("FakeRust", "")
|
||||
.replace("Fake", "")
|
||||
(it.name.startsWith("Fake") || it.name.startsWith("FakeRust")) &&
|
||||
it.parents().any { parent -> parent.name.replace(".", "") == interfaceName }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue