From 6dc98ef8d3f36402a542621df3a99de4480eb105 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 6 Nov 2022 18:31:59 +0000 Subject: [PATCH] adding tests around marking room as read - fixes wrong message instance being used for filtering --- .../app/dapk/st/engine/ReadMarkingTimeline.kt | 22 +++---- .../dapk/st/engine/ReadMarkingTimelineTest.kt | 66 +++++++++++++++++++ .../app/dapk/st/engine/TimelineUseCaseTest.kt | 2 +- 3 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/ReadMarkingTimelineTest.kt diff --git a/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/ReadMarkingTimeline.kt b/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/ReadMarkingTimeline.kt index 1a2480f..43199b5 100644 --- a/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/ReadMarkingTimeline.kt +++ b/matrix-chat-engine/src/main/kotlin/app/dapk/st/engine/ReadMarkingTimeline.kt @@ -5,9 +5,7 @@ import app.dapk.st.matrix.common.EventId import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.UserId import app.dapk.st.matrix.room.RoomService -import app.dapk.st.matrix.sync.RoomEvent import app.dapk.st.matrix.sync.RoomStore -import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.* @@ -24,7 +22,7 @@ class ReadMarkingTimeline( val credentials = credentialsStore.credentials()!! roomStore.markRead(roomId) emit(credentials) - }.flatMapMerge { credentials -> + }.flatMapConcat { credentials -> var lastKnownReadEvent: EventId? = null observeTimelineUseCase.invoke(roomId, credentials.userId).distinctUntilChanged().onEach { state -> state.latestMessageEventFromOthers(self = credentials.userId)?.let { @@ -37,8 +35,9 @@ class ReadMarkingTimeline( } } - private suspend fun updateRoomReadStateAsync(latestReadEvent: EventId, state: MessengerPageState, isReadReceiptsDisabled: Boolean): Deferred<*> { - return coroutineScope { + @Suppress("DeferredResultUnused") + private suspend fun updateRoomReadStateAsync(latestReadEvent: EventId, state: MessengerPageState, isReadReceiptsDisabled: Boolean) { + coroutineScope { async { runCatching { roomService.markFullyRead(state.roomState.roomOverview.roomId, latestReadEvent, isPrivate = isReadReceiptsDisabled) @@ -48,10 +47,9 @@ class ReadMarkingTimeline( } } -} - -private fun MessengerPageState.latestMessageEventFromOthers(self: UserId) = this.roomState.events - .filterIsInstance() - .filterNot { it.author.id == self } - .firstOrNull() - ?.eventId + private fun MessengerPageState.latestMessageEventFromOthers(self: UserId) = this.roomState.events + .filterIsInstance() + .filterNot { it.author.id == self } + .firstOrNull() + ?.eventId +} \ No newline at end of file diff --git a/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/ReadMarkingTimelineTest.kt b/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/ReadMarkingTimelineTest.kt new file mode 100644 index 0000000..f70b33b --- /dev/null +++ b/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/ReadMarkingTimelineTest.kt @@ -0,0 +1,66 @@ +package app.dapk.st.engine + +import app.dapk.st.matrix.common.RoomId +import app.dapk.st.matrix.common.UserId +import fake.FakeCredentialsStore +import fake.FakeRoomStore +import fixture.* +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import test.delegateReturn +import test.runExpectTest + +private val A_ROOM_ID = aRoomId() +private val A_USER_CREDENTIALS = aUserCredentials() +private val A_ROOM_MESSAGE_FROM_OTHER_USER = aRoomMessageEvent(author = aRoomMember(id = aUserId("another-user"))) +private val A_ROOM_MESSAGE_FROM_SELF = aRoomMessageEvent(author = aRoomMember(id = A_USER_CREDENTIALS.userId)) +private val READ_RECEIPTS_ARE_DISABLED = true + +class ReadMarkingTimelineTest { + + private val fakeRoomStore = FakeRoomStore() + private val fakeCredentialsStore = FakeCredentialsStore().apply { givenCredentials().returns(A_USER_CREDENTIALS) } + private val fakeObserveTimelineUseCase = FakeObserveTimelineUseCase() + private val fakeRoomService = FakeRoomService() + + private val readMarkingTimeline = ReadMarkingTimeline( + fakeRoomStore, + fakeCredentialsStore, + fakeObserveTimelineUseCase, + fakeRoomService, + ) + + @Test + fun `given a message from self, when fetching, then only marks room as read on initial launch`() = runExpectTest { + fakeRoomStore.expectUnit(times = 1) { it.markRead(A_ROOM_ID) } + val messengerState = aMessengerState(roomState = aRoomState(events = listOf(A_ROOM_MESSAGE_FROM_SELF))) + fakeObserveTimelineUseCase.given(A_ROOM_ID, A_USER_CREDENTIALS.userId).returns(flowOf(messengerState)) + + val result = readMarkingTimeline.fetch(A_ROOM_ID, isReadReceiptsDisabled = READ_RECEIPTS_ARE_DISABLED).first() + + result shouldBeEqualTo messengerState + verifyExpects() + } + + @Test + fun `given a message from other user, when fetching, then marks room as read`() = runExpectTest { + fakeRoomStore.expectUnit(times = 2) { it.markRead(A_ROOM_ID) } + fakeRoomService.expectUnit { it.markFullyRead(A_ROOM_ID, A_ROOM_MESSAGE_FROM_OTHER_USER.eventId, isPrivate = READ_RECEIPTS_ARE_DISABLED) } + val messengerState = aMessengerState(roomState = aRoomState(events = listOf(A_ROOM_MESSAGE_FROM_OTHER_USER))) + fakeObserveTimelineUseCase.given(A_ROOM_ID, A_USER_CREDENTIALS.userId).returns(flowOf(messengerState)) + + val result = readMarkingTimeline.fetch(A_ROOM_ID, isReadReceiptsDisabled = READ_RECEIPTS_ARE_DISABLED).first() + + result shouldBeEqualTo messengerState + verifyExpects() + } + +} + +class FakeObserveTimelineUseCase : ObserveTimelineUseCase by mockk() { + fun given(roomId: RoomId, userId: UserId) = every { this@FakeObserveTimelineUseCase.invoke(roomId, userId) }.delegateReturn() +} diff --git a/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/TimelineUseCaseTest.kt b/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/TimelineUseCaseTest.kt index 73d980e..38f950b 100644 --- a/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/TimelineUseCaseTest.kt +++ b/matrix-chat-engine/src/test/kotlin/app/dapk/st/engine/TimelineUseCaseTest.kt @@ -136,7 +136,7 @@ class FakeRoomService : RoomService by mockk() { fun aMessengerState( self: UserId = aUserId(), - roomState: app.dapk.st.engine.RoomState, + roomState: app.dapk.st.engine.RoomState = aRoomState(), typing: Typing? = null, isMuted: Boolean = IS_ROOM_MUTED, ) = MessengerPageState(self, roomState, typing, isMuted) \ No newline at end of file