Skip to content

Commit 2b87281

Browse files
committed
fix (left room membership) : use correct membership change and add test
1 parent b016f2e commit 2b87281

File tree

4 files changed

+120
-14
lines changed

4 files changed

+120
-14
lines changed

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembershipObserver.kt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,12 @@ class RoomMembershipObserver {
2222
private val _updates = MutableSharedFlow<RoomMembershipUpdate>(extraBufferCapacity = 10)
2323
val updates = _updates.asSharedFlow()
2424

25-
suspend fun notifyUserLeftRoom(roomId: RoomId) {
26-
_updates.emit(RoomMembershipUpdate(roomId, false, MembershipChange.LEFT))
27-
}
28-
29-
suspend fun notifyUserDeclinedInvite(roomId: RoomId) {
30-
_updates.emit(RoomMembershipUpdate(roomId, false, MembershipChange.INVITATION_REJECTED))
31-
}
32-
33-
suspend fun notifyUserCanceledKnock(roomId: RoomId) {
34-
_updates.emit(RoomMembershipUpdate(roomId, false, MembershipChange.KNOCK_RETRACTED))
25+
suspend fun notifyUserLeftRoom(roomId: RoomId, membershipBeforeLeft: CurrentUserMembership) {
26+
val membershipChange = when (membershipBeforeLeft) {
27+
CurrentUserMembership.INVITED -> MembershipChange.INVITATION_REJECTED
28+
CurrentUserMembership.KNOCKED -> MembershipChange.KNOCK_RETRACTED
29+
else -> MembershipChange.LEFT
30+
}
31+
_updates.emit(RoomMembershipUpdate(roomId, false, membershipChange))
3532
}
3633
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,11 @@ class RustBaseRoom(
137137
}
138138

139139
override suspend fun leave(): Result<Unit> = withContext(roomDispatcher) {
140+
val membershipBeforeLeft = roomInfoFlow.value.currentUserMembership
140141
runCatching {
141142
innerRoom.leave()
142143
}.onSuccess {
143-
roomMembershipObserver.notifyUserLeftRoom(roomId)
144+
roomMembershipObserver.notifyUserLeftRoom(roomId, membershipBeforeLeft)
144145
}
145146
}
146147

libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustRoom.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class FakeRustRoom(
1818
private val roomId: RoomId = A_ROOM_ID,
1919
private val getMembers: () -> RoomMembersIterator = { lambdaError() },
2020
private val getMembersNoSync: () -> RoomMembersIterator = { lambdaError() },
21+
private val leaveLambda: () -> Unit = { lambdaError() },
2122
) : Room(NoPointer) {
2223
override fun id(): String {
2324
return roomId.value
@@ -31,6 +32,10 @@ class FakeRustRoom(
3132
return getMembersNoSync()
3233
}
3334

35+
override suspend fun leave() {
36+
leaveLambda()
37+
}
38+
3439
override fun close() {
3540
// No-op
3641
}

libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoomTest.kt

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,21 @@
77

88
package io.element.android.libraries.matrix.impl.room
99

10+
import app.cash.turbine.test
1011
import com.google.common.truth.Truth.assertThat
12+
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
13+
import io.element.android.libraries.matrix.api.room.RoomInfo
1114
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
15+
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
1216
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoom
1317
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
1418
import io.element.android.libraries.matrix.test.A_DEVICE_ID
1519
import io.element.android.libraries.matrix.test.A_SESSION_ID
1620
import io.element.android.libraries.matrix.test.room.aRoomInfo
1721
import io.element.android.tests.testutils.testCoroutineDispatchers
1822
import kotlinx.coroutines.CoroutineScope
23+
import kotlinx.coroutines.flow.SharingStarted
24+
import kotlinx.coroutines.flow.shareIn
1925
import kotlinx.coroutines.isActive
2026
import kotlinx.coroutines.test.TestScope
2127
import kotlinx.coroutines.test.runTest
@@ -33,23 +39,120 @@ class RustBaseRoomTest {
3339
assertThat(rustBaseRoom.roomCoroutineScope.isActive).isFalse()
3440
}
3541

42+
@Test
43+
fun `when currentUserMembership=JOINED and user leave room succeed then roomMembershipObserver emits change as LEFT`() = runTest {
44+
val roomMembershipObserver = RoomMembershipObserver()
45+
val rustBaseRoom = createRustBaseRoom(
46+
sessionCoroutineScope = this,
47+
initialRoomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.JOINED),
48+
innerRoom = FakeRustRoom(
49+
leaveLambda = {
50+
// Simulate a successful leave
51+
}
52+
),
53+
roomMembershipObserver = roomMembershipObserver,
54+
)
55+
val shared = roomMembershipObserver.updates.shareIn(scope = backgroundScope, started = SharingStarted.Eagerly, replay = 1)
56+
rustBaseRoom.leave()
57+
shared.test {
58+
val membershipUpdate = awaitItem()
59+
assertThat(membershipUpdate.roomId).isEqualTo(rustBaseRoom.roomId)
60+
assertThat(membershipUpdate.isUserInRoom).isFalse()
61+
assertThat(membershipUpdate.change).isEqualTo(MembershipChange.LEFT)
62+
ensureAllEventsConsumed()
63+
}
64+
rustBaseRoom.destroy()
65+
}
66+
67+
@Test
68+
fun `when currentUserMembership=KNOCKED and user leave room succeed then roomMembershipObserver emits change as KNOCK_RETRACTED`() = runTest {
69+
val roomMembershipObserver = RoomMembershipObserver()
70+
val rustBaseRoom = createRustBaseRoom(
71+
sessionCoroutineScope = this,
72+
initialRoomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.KNOCKED),
73+
innerRoom = FakeRustRoom(
74+
leaveLambda = {
75+
// Simulate a successful leave
76+
}
77+
),
78+
roomMembershipObserver = roomMembershipObserver,
79+
)
80+
val shared = roomMembershipObserver.updates.shareIn(scope = backgroundScope, started = SharingStarted.Eagerly, replay = 1)
81+
rustBaseRoom.leave()
82+
shared.test {
83+
val membershipUpdate = awaitItem()
84+
assertThat(membershipUpdate.roomId).isEqualTo(rustBaseRoom.roomId)
85+
assertThat(membershipUpdate.isUserInRoom).isFalse()
86+
assertThat(membershipUpdate.change).isEqualTo(MembershipChange.KNOCK_RETRACTED)
87+
ensureAllEventsConsumed()
88+
}
89+
rustBaseRoom.destroy()
90+
}
91+
92+
@Test
93+
fun `when currentUserMembership=INVITED and user leave room succeed then roomMembershipObserver emits change as INVITATION_REJECTED`() = runTest {
94+
val roomMembershipObserver = RoomMembershipObserver()
95+
val rustBaseRoom = createRustBaseRoom(
96+
sessionCoroutineScope = this,
97+
initialRoomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED),
98+
innerRoom = FakeRustRoom(
99+
leaveLambda = {
100+
// Simulate a successful leave
101+
}
102+
),
103+
roomMembershipObserver = roomMembershipObserver,
104+
)
105+
val shared = roomMembershipObserver.updates.shareIn(scope = backgroundScope, started = SharingStarted.Eagerly, replay = 1)
106+
rustBaseRoom.leave()
107+
shared.test {
108+
val membershipUpdate = awaitItem()
109+
assertThat(membershipUpdate.roomId).isEqualTo(rustBaseRoom.roomId)
110+
assertThat(membershipUpdate.isUserInRoom).isFalse()
111+
assertThat(membershipUpdate.change).isEqualTo(MembershipChange.INVITATION_REJECTED)
112+
ensureAllEventsConsumed()
113+
}
114+
rustBaseRoom.destroy()
115+
}
116+
117+
@Test
118+
fun `when user leave room fails then roomMembershipObserver emits nothing`() = runTest {
119+
val roomMembershipObserver = RoomMembershipObserver()
120+
val rustBaseRoom = createRustBaseRoom(
121+
sessionCoroutineScope = this,
122+
initialRoomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED),
123+
innerRoom = FakeRustRoom(
124+
leaveLambda = { error("Leave failed") }
125+
),
126+
roomMembershipObserver = roomMembershipObserver,
127+
)
128+
val shared = roomMembershipObserver.updates.shareIn(scope = backgroundScope, started = SharingStarted.Eagerly, replay = 1)
129+
rustBaseRoom.leave()
130+
shared.test {
131+
ensureAllEventsConsumed()
132+
}
133+
rustBaseRoom.destroy()
134+
}
135+
36136
private fun TestScope.createRustBaseRoom(
37137
sessionCoroutineScope: CoroutineScope,
138+
initialRoomInfo: RoomInfo = aRoomInfo(),
139+
innerRoom: FakeRustRoom = FakeRustRoom(),
140+
roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(),
38141
): RustBaseRoom {
39142
val dispatchers = testCoroutineDispatchers()
40143
return RustBaseRoom(
41144
sessionId = A_SESSION_ID,
42145
deviceId = A_DEVICE_ID,
43-
innerRoom = FakeRustRoom(),
146+
innerRoom = innerRoom,
44147
coroutineDispatchers = dispatchers,
45148
roomSyncSubscriber = RoomSyncSubscriber(
46149
roomListService = FakeRustRoomListService(),
47150
dispatchers = dispatchers,
48151
),
49-
roomMembershipObserver = RoomMembershipObserver(),
152+
roomMembershipObserver = roomMembershipObserver,
50153
sessionCoroutineScope = sessionCoroutineScope,
51154
roomInfoMapper = RoomInfoMapper(),
52-
initialRoomInfo = aRoomInfo(),
155+
initialRoomInfo = initialRoomInfo,
53156
)
54157
}
55158
}

0 commit comments

Comments
 (0)