From c6abba5638ddf247eecdeca97a5efbfe9a3d2923 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Sun, 13 Mar 2022 14:12:13 +0000 Subject: [PATCH] adding tests around the timeline usecase --- build.gradle | 1 + .../st/messenger/MessengerViewModelTest.kt | 16 ++- .../dapk/st/messenger/TimelineUseCaseTest.kt | 114 ++++++++++++++++++ .../kotlin/fake/FakeSyncService.kt | 15 ++- 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 features/messenger/src/test/kotlin/app/dapk/st/messenger/TimelineUseCaseTest.kt diff --git a/build.gradle b/build.gradle index e79aeb2..6d391bd 100644 --- a/build.gradle +++ b/build.gradle @@ -136,6 +136,7 @@ ext.kotlinTest = { dependencies -> ext.kotlinFixtures = { dependencies -> dependencies.testFixturesImplementation 'io.mockk:mockk:1.12.2' dependencies.testFixturesImplementation Dependencies.mavenCentral.kluent + dependencies.testFixturesImplementation Dependencies.mavenCentral.kotlinCoroutinesCore } ext.androidImportFixturesWorkaround = { project, fixtures -> diff --git a/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerViewModelTest.kt b/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerViewModelTest.kt index 0fd0aec..3a88489 100644 --- a/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerViewModelTest.kt +++ b/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerViewModelTest.kt @@ -8,11 +8,13 @@ import app.dapk.st.matrix.common.UserId import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.room.RoomService import app.dapk.st.matrix.sync.RoomState +import app.dapk.st.matrix.sync.SyncService import fake.FakeCredentialsStore import fake.FakeRoomStore import fixture.* import internalfake.FakeLocalIdFactory import io.mockk.coEvery +import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.flow.flowOf import org.junit.Test @@ -130,13 +132,21 @@ fun aMessageScreenState(roomId: RoomId = aRoomId(), roomState: MessengerState, m fun aMessengerState( self: UserId = aUserId(), roomState: RoomState, -) = MessengerState(self, roomState, typing = null) + typing: SyncService.SyncEvent.Typing? = null +) = MessengerState(self, roomState, typing) class FakeObserveTimelineUseCase : ObserveTimelineUseCase by mockk() { fun given(roomId: RoomId, selfId: UserId) = coEvery { this@FakeObserveTimelineUseCase.invoke(roomId, selfId) }.delegateReturn() } -class FakeMessageService : MessageService by mockk() -class FakeRoomService : RoomService by mockk() +class FakeMessageService : MessageService by mockk() { + + fun givenEchos(roomId: RoomId) = every { localEchos(roomId) }.delegateReturn() + +} + +class FakeRoomService : RoomService by mockk() { + fun givenFindMember(roomId: RoomId, userId: UserId) = coEvery { findMember(roomId, userId) }.delegateReturn() +} fun fixedClock(timestamp: Long = 0) = Clock.fixed(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC) diff --git a/features/messenger/src/test/kotlin/app/dapk/st/messenger/TimelineUseCaseTest.kt b/features/messenger/src/test/kotlin/app/dapk/st/messenger/TimelineUseCaseTest.kt new file mode 100644 index 0000000..7b840fd --- /dev/null +++ b/features/messenger/src/test/kotlin/app/dapk/st/messenger/TimelineUseCaseTest.kt @@ -0,0 +1,114 @@ +package app.dapk.st.messenger + +import FlowTestObserver +import app.dapk.st.matrix.common.RoomId +import app.dapk.st.matrix.common.RoomMember +import app.dapk.st.matrix.message.MessageService +import app.dapk.st.matrix.sync.RoomState +import app.dapk.st.matrix.sync.SyncService +import fake.FakeSyncService +import fixture.* +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.Test +import test.delegateReturn + +private val A_ROOM_ID = aRoomId() +private val AN_USER_ID = aUserId() +private val A_ROOM_STATE = aRoomState() +private val A_MERGED_ROOM_STATE = A_ROOM_STATE.copy(events = listOf(aRoomMessageEvent(content = "a merged event"))) +private val A_LOCAL_ECHOS_LIST = listOf(aLocalEcho()) +private val A_ROOM_MEMBER = aRoomMember() + +class TimelineUseCaseTest { + + private val fakeSyncService = FakeSyncService() + private val fakeMessageService = FakeMessageService() + private val fakeRoomService = FakeRoomService() + private val fakeMergeWithLocalEchosUseCase = FakeMergeWithLocalEchosUseCase() + + private val timelineUseCase = TimelineUseCaseImpl( + fakeSyncService, + fakeMessageService, + fakeRoomService, + fakeMergeWithLocalEchosUseCase, + ) + + @Test + fun `when observing timeline, then emits sync emission`() = runTest { + givenSyncEmission(roomState = A_ROOM_STATE) + + timelineUseCase.invoke(A_ROOM_ID, AN_USER_ID) + .test(this) + .assertValues( + listOf( + aMessengerState(self = AN_USER_ID, roomState = A_ROOM_STATE) + ) + ) + } + + @Test + fun `given local echos, when observing timeline, then merges room and local echos`() = runTest { + givenSyncEmission(roomState = A_ROOM_STATE, echos = A_LOCAL_ECHOS_LIST) + fakeRoomService.givenFindMember(A_ROOM_ID, AN_USER_ID).returns(A_ROOM_MEMBER) + + fakeMergeWithLocalEchosUseCase.givenMerging(A_ROOM_STATE, A_ROOM_MEMBER, A_LOCAL_ECHOS_LIST).returns(A_MERGED_ROOM_STATE) + + timelineUseCase.invoke(A_ROOM_ID, AN_USER_ID) + .test(this) + .assertValues( + listOf( + aMessengerState(self = AN_USER_ID, roomState = A_MERGED_ROOM_STATE) + ) + ) + } + + @Test + fun `given sync events from current and other rooms, when observing timeline, then filters by current room`() = runTest { + givenSyncEmission( + events = listOf( + aTypingSyncEvent(aRoomId("another room"), members = listOf(A_ROOM_MEMBER)), + aTypingSyncEvent(A_ROOM_ID, members = listOf(A_ROOM_MEMBER)), + ) + ) + + timelineUseCase.invoke(A_ROOM_ID, AN_USER_ID) + .test(this) + .assertValues( + listOf( + aMessengerState(self = AN_USER_ID, roomState = A_ROOM_STATE, typing = aTypingSyncEvent(A_ROOM_ID, members = listOf(A_ROOM_MEMBER))) + ) + ) + } + + private fun givenSyncEmission( + roomState: RoomState = A_ROOM_STATE, + echos: List = emptyList(), + events: List = emptyList() + ) { + fakeSyncService.givenSyncs() + fakeSyncService.givenRoom(A_ROOM_ID).returns(flowOf(roomState)) + fakeMessageService.givenEchos(A_ROOM_ID).returns(flowOf(echos)) + fakeSyncService.givenEvents(A_ROOM_ID).returns(flowOf(events)) + } +} + +suspend fun Flow.test(scope: CoroutineScope) = FlowTestObserver(scope, this).also { + this.collect() +} + +class FakeMergeWithLocalEchosUseCase : MergeWithLocalEchosUseCase by mockk() { + fun givenMerging(roomState: RoomState, roomMember: RoomMember, echos: List) = every { + this@FakeMergeWithLocalEchosUseCase.invoke(roomState, roomMember, echos) + }.delegateReturn() +} + +fun aTypingSyncEvent( + roomId: RoomId = aRoomId(), + members: List = listOf(aRoomMember()) +) = SyncService.SyncEvent.Typing(roomId, members) \ No newline at end of file diff --git a/matrix/services/sync/src/testFixtures/kotlin/fake/FakeSyncService.kt b/matrix/services/sync/src/testFixtures/kotlin/fake/FakeSyncService.kt index 8d40e52..33b461d 100644 --- a/matrix/services/sync/src/testFixtures/kotlin/fake/FakeSyncService.kt +++ b/matrix/services/sync/src/testFixtures/kotlin/fake/FakeSyncService.kt @@ -1,6 +1,19 @@ package fake +import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.sync.SyncService +import io.mockk.every import io.mockk.mockk +import kotlinx.coroutines.flow.flowOf +import test.delegateReturn -class FakeSyncService : SyncService by mockk() +class FakeSyncService : SyncService by mockk() { + fun givenSyncs() { + every { startSyncing() }.returns(flowOf(Unit)) + } + + fun givenRoom(roomId: RoomId) = every { room(roomId) }.delegateReturn() + + fun givenEvents(roomId: RoomId) = every { events() }.delegateReturn() + +}