porting messenger tests to the engine module

This commit is contained in:
Adam Brown 2022-10-12 19:57:16 +01:00
parent 6e076e7c9f
commit 8f1d8cdcc1
18 changed files with 140 additions and 120 deletions

View File

@ -1,8 +1,12 @@
plugins { plugins {
id 'kotlin' id 'kotlin'
id 'java-test-fixtures'
} }
dependencies { dependencies {
api Dependencies.mavenCentral.kotlinCoroutinesCore api Dependencies.mavenCentral.kotlinCoroutinesCore
api project(":matrix:common") api project(":matrix:common")
kotlinFixtures(it)
testFixturesImplementation(testFixtures(project(":matrix:common")))
} }

View File

@ -7,13 +7,11 @@ import app.dapk.st.matrix.common.RoomMember
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import java.io.InputStream import java.io.InputStream
interface ChatEngine: TaskRunner { interface ChatEngine : TaskRunner {
fun directory(): Flow<DirectoryState> fun directory(): Flow<DirectoryState>
fun invites(): Flow<InviteState> fun invites(): Flow<InviteState>
fun messages(roomId: RoomId, disableReadReceipts: Boolean): Flow<MessengerState>
suspend fun messages(roomId: RoomId, disableReadReceipts: Boolean): Flow<MessengerState>
suspend fun login(request: LoginRequest): LoginResult suspend fun login(request: LoginRequest): LoginResult
@ -49,7 +47,6 @@ interface TaskRunner {
} }
data class ChatEngineTask(val type: String, val jsonPayload: String) data class ChatEngineTask(val type: String, val jsonPayload: String)
interface MediaDecrypter { interface MediaDecrypter {

View File

@ -0,0 +1,31 @@
package fixture
import app.dapk.st.engine.*
import app.dapk.st.matrix.common.*
fun aMessengerState(
self: UserId = aUserId(),
roomState: RoomState,
typing: Typing? = null
) = MessengerState(self, roomState, typing)
fun aRoomOverview(
roomId: RoomId = aRoomId(),
roomCreationUtc: Long = 0L,
roomName: String? = null,
roomAvatarUrl: AvatarUrl? = null,
lastMessage: RoomOverview.LastMessage? = null,
isGroup: Boolean = false,
readMarker: EventId? = null,
isEncrypted: Boolean = false,
) = RoomOverview(roomId, roomCreationUtc, roomName, roomAvatarUrl, lastMessage, isGroup, readMarker, isEncrypted)
fun anEncryptedRoomMessageEvent(
eventId: EventId = anEventId(),
utcTimestamp: Long = 0L,
content: String = "encrypted-content",
author: RoomMember = aRoomMember(),
meta: MessageMeta = MessageMeta.FromServer,
edited: Boolean = false,
redacted: Boolean = false,
) = RoomEvent.Message(eventId, utcTimestamp, content, author, meta, edited, redacted)

View File

@ -1,3 +1,5 @@
package test
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

View File

@ -3,6 +3,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import org.amshove.kluent.internal.assertEquals import org.amshove.kluent.internal.assertEquals
import test.ExpectTestScope import test.ExpectTestScope
import test.FlowTestObserver
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal class ViewModelTestScopeImpl( internal class ViewModelTestScopeImpl(

View File

@ -13,11 +13,10 @@ dependencies {
kotlinTest(it) kotlinTest(it)
androidImportFixturesWorkaround(project, project(":matrix:services:sync"))
androidImportFixturesWorkaround(project, project(":matrix:services:message"))
androidImportFixturesWorkaround(project, project(":matrix:common")) androidImportFixturesWorkaround(project, project(":matrix:common"))
androidImportFixturesWorkaround(project, project(":core")) androidImportFixturesWorkaround(project, project(":core"))
androidImportFixturesWorkaround(project, project(":domains:store")) androidImportFixturesWorkaround(project, project(":domains:store"))
androidImportFixturesWorkaround(project, project(":domains:android:viewmodel")) androidImportFixturesWorkaround(project, project(":domains:android:viewmodel"))
androidImportFixturesWorkaround(project, project(":domains:android:stub")) androidImportFixturesWorkaround(project, project(":domains:android:stub"))
androidImportFixturesWorkaround(project, project(":chat-engine"))
} }

View File

@ -2,58 +2,38 @@ package app.dapk.st.messenger
import ViewModelTest import ViewModelTest
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.matrix.common.EventId import app.dapk.st.engine.*
import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.*
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.internal.ImageContentReader
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.FakeMessageOptionsStore import fake.FakeMessageOptionsStore
import fake.FakeRoomStore
import fixture.* import fixture.*
import internalfake.FakeLocalIdFactory
import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import org.junit.Test import org.junit.Test
import test.delegateReturn import test.delegateReturn
import java.time.Clock
import java.time.Instant
import java.time.ZoneOffset
private const val A_CURRENT_TIMESTAMP = 10000L
private const val READ_RECEIPTS_ARE_DISABLED = true private const val READ_RECEIPTS_ARE_DISABLED = true
private val A_ROOM_ID = aRoomId("messenger state room id") private val A_ROOM_ID = aRoomId("messenger state room id")
private const val A_MESSAGE_CONTENT = "message content" private const val A_MESSAGE_CONTENT = "message content"
private const val A_LOCAL_ID = "local.1111-2222-3333"
private val AN_EVENT_ID = anEventId("state event") private val AN_EVENT_ID = anEventId("state event")
private val A_SELF_ID = aUserId("self") private val A_SELF_ID = aUserId("self")
class FakeChatEngine : ChatEngine by mockk() {
fun givenMessages(roomId: RoomId, disableReadReceipts: Boolean) = every { messages(roomId, disableReadReceipts) }.delegateReturn()
}
class MessengerViewModelTest { class MessengerViewModelTest {
private val runViewModelTest = ViewModelTest() private val runViewModelTest = ViewModelTest()
private val fakeMessageService = FakeMessageService()
private val fakeRoomService = FakeRoomService()
private val fakeRoomStore = FakeRoomStore()
private val fakeCredentialsStore = FakeCredentialsStore().also { it.givenCredentials().returns(aUserCredentials(userId = A_SELF_ID)) }
private val fakeObserveTimelineUseCase = FakeObserveTimelineUseCase()
private val fakeMessageOptionsStore = FakeMessageOptionsStore() private val fakeMessageOptionsStore = FakeMessageOptionsStore()
private val fakeChatEngine = FakeChatEngine()
private val viewModel = MessengerViewModel( private val viewModel = MessengerViewModel(
fakeMessageService, fakeChatEngine,
fakeRoomService, fakeMessageOptionsStore.instance,
fakeRoomStore,
fakeCredentialsStore,
fakeObserveTimelineUseCase,
localIdFactory = FakeLocalIdFactory().also { it.givenCreate().returns(A_LOCAL_ID) }.instance,
imageContentReader = FakeImageContentReader(),
messageOptionsStore = fakeMessageOptionsStore.instance,
clock = fixedClock(A_CURRENT_TIMESTAMP),
factory = runViewModelTest.testMutableStateFactory(), factory = runViewModelTest.testMutableStateFactory(),
) )
@ -73,10 +53,8 @@ class MessengerViewModelTest {
@Test @Test
fun `given timeline emits state, when starting, then updates state and marks room and events as read`() = runViewModelTest { fun `given timeline emits state, when starting, then updates state and marks room and events as read`() = runViewModelTest {
fakeMessageOptionsStore.givenReadReceiptsDisabled().returns(READ_RECEIPTS_ARE_DISABLED) fakeMessageOptionsStore.givenReadReceiptsDisabled().returns(READ_RECEIPTS_ARE_DISABLED)
fakeRoomStore.expectUnit(times = 2) { it.markRead(A_ROOM_ID) }
fakeRoomService.expectUnit { it.markFullyRead(A_ROOM_ID, AN_EVENT_ID, isPrivate = READ_RECEIPTS_ARE_DISABLED) }
val state = aMessengerStateWithEvent(AN_EVENT_ID, A_SELF_ID) val state = aMessengerStateWithEvent(AN_EVENT_ID, A_SELF_ID)
fakeObserveTimelineUseCase.given(A_ROOM_ID, A_SELF_ID).returns(flowOf(state)) fakeChatEngine.givenMessages(A_ROOM_ID, READ_RECEIPTS_ARE_DISABLED).returns(flowOf(state))
viewModel.test().post(MessengerAction.OnMessengerVisible(A_ROOM_ID, attachments = null)) viewModel.test().post(MessengerAction.OnMessengerVisible(A_ROOM_ID, attachments = null))
@ -98,7 +76,7 @@ class MessengerViewModelTest {
@Test @Test
fun `given composer message state when posting send text, then resets composer state and sends message`() = runViewModelTest { fun `given composer message state when posting send text, then resets composer state and sends message`() = runViewModelTest {
fakeMessageService.expectUnit { it.scheduleMessage(expectEncryptedMessage(A_ROOM_ID, A_LOCAL_ID, A_CURRENT_TIMESTAMP, A_MESSAGE_CONTENT)) } fakeChatEngine.expectUnit { it.send(expectTextMessage(A_MESSAGE_CONTENT), aRoomOverview()) }
viewModel.test(initialState = initialStateWithComposerMessage(A_ROOM_ID, A_MESSAGE_CONTENT)).post(MessengerAction.ComposerSendText) viewModel.test(initialState = initialStateWithComposerMessage(A_ROOM_ID, A_MESSAGE_CONTENT)).post(MessengerAction.ComposerSendText)
@ -114,9 +92,8 @@ class MessengerViewModelTest {
return aMessageScreenState(roomId, aMessengerState(roomState = roomState), messageContent) return aMessageScreenState(roomId, aMessengerState(roomState = roomState), messageContent)
} }
private fun expectEncryptedMessage(roomId: RoomId, localId: String, timestamp: Long, messageContent: String): MessageService.Message.TextMessage { private fun expectTextMessage(messageContent: String): SendMessage.TextMessage {
val content = MessageService.Message.Content.TextContent(body = messageContent) return SendMessage.TextMessage(messageContent, reply = null)
return MessageService.Message.TextMessage(content, sendEncrypted = true, roomId, localId, timestamp)
} }
private fun aMessengerStateWithEvent(eventId: EventId, selfId: UserId) = aRoomStateWithEventId(eventId).toMessengerState(selfId) private fun aMessengerStateWithEvent(eventId: EventId, selfId: UserId) = aRoomStateWithEventId(eventId).toMessengerState(selfId)
@ -135,27 +112,3 @@ fun aMessageScreenState(roomId: RoomId = aRoomId(), roomState: MessengerState, m
roomState = Lce.Content(roomState), roomState = Lce.Content(roomState),
composerState = ComposerState.Text(value = messageContent ?: "", reply = null) composerState = ComposerState.Text(value = messageContent ?: "", reply = null)
) )
fun aMessengerState(
self: UserId = aUserId(),
roomState: RoomState,
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() {
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)
class FakeImageContentReader : ImageContentReader by mockk()

View File

@ -1,4 +1,5 @@
plugins { plugins {
id 'java-test-fixtures'
id 'kotlin' id 'kotlin'
} }
@ -20,4 +21,14 @@ dependencies {
implementation project(":matrix:services:device") implementation project(":matrix:services:device")
implementation project(":matrix:services:crypto") implementation project(":matrix:services:crypto")
implementation project(":matrix:services:profile") implementation project(":matrix:services:profile")
kotlinTest(it)
kotlinFixtures(it)
testImplementation(testFixtures(project(":matrix:services:sync")))
testImplementation(testFixtures(project(":matrix:services:message")))
testImplementation(testFixtures(project(":matrix:common")))
testImplementation(testFixtures(project(":core")))
testImplementation(testFixtures(project(":domains:store")))
testImplementation(testFixtures(project(":chat-engine")))
} }

View File

@ -46,8 +46,8 @@ class MatrixEngine internal constructor(
override fun directory() = directoryUseCase.value.state() override fun directory() = directoryUseCase.value.state()
override fun invites() = inviteUseCase.value.invites() override fun invites() = inviteUseCase.value.invites()
override suspend fun messages(roomId: RoomId, disableReadReceipts: Boolean): Flow<MessengerState> { override fun messages(roomId: RoomId, disableReadReceipts: Boolean): Flow<MessengerState> {
return timelineUseCase.value.foo(roomId, isReadReceiptsDisabled = disableReadReceipts) return timelineUseCase.value.fetch(roomId, isReadReceiptsDisabled = disableReadReceipts)
} }
override suspend fun login(request: LoginRequest): LoginResult { override suspend fun login(request: LoginRequest): LoginResult {

View File

@ -10,9 +10,7 @@ import app.dapk.st.matrix.sync.RoomStore
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
class ReadMarkingTimeline( class ReadMarkingTimeline(
private val roomStore: RoomStore, private val roomStore: RoomStore,
@ -21,15 +19,19 @@ class ReadMarkingTimeline(
private val roomService: RoomService, private val roomService: RoomService,
) { ) {
suspend fun foo(roomId: RoomId, isReadReceiptsDisabled: Boolean): Flow<MessengerState> { fun fetch(roomId: RoomId, isReadReceiptsDisabled: Boolean): Flow<MessengerState> {
var lastKnownReadEvent: EventId? = null return flow {
val credentials = credentialsStore.credentials()!! val credentials = credentialsStore.credentials()!!
roomStore.markRead(roomId) roomStore.markRead(roomId)
return observeTimelineUseCase.invoke(roomId, credentials.userId).distinctUntilChanged().onEach { state -> emit(credentials)
state.latestMessageEventFromOthers(self = credentials.userId)?.let { }.flatMapMerge { credentials ->
if (lastKnownReadEvent != it) { var lastKnownReadEvent: EventId? = null
updateRoomReadStateAsync(latestReadEvent = it, state, isReadReceiptsDisabled) observeTimelineUseCase.invoke(roomId, credentials.userId).distinctUntilChanged().onEach { state ->
lastKnownReadEvent = it state.latestMessageEventFromOthers(self = credentials.userId)?.let {
if (lastKnownReadEvent != it) {
updateRoomReadStateAsync(latestReadEvent = it, state, isReadReceiptsDisabled)
lastKnownReadEvent = it
}
} }
} }
} }

View File

@ -1,10 +1,10 @@
package app.dapk.st.messenger package app.dapk.st.engine
import app.dapk.st.matrix.common.EventId import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.sync.MessageMeta import app.dapk.st.matrix.sync.MessageMeta
import fake.FakeMetaMapper
import fixture.* import fixture.*
import internalfake.FakeMetaMapper
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test import org.junit.Test
@ -28,7 +28,7 @@ class LocalEchoMapperTest {
eventId = echo.eventId!!, eventId = echo.eventId!!,
content = AN_ECHO_CONTENT.content.body, content = AN_ECHO_CONTENT.content.body,
meta = A_META meta = A_META
) ).engine()
} }
@Test @Test
@ -41,24 +41,24 @@ class LocalEchoMapperTest {
eventId = anEventId(echo.localId), eventId = anEventId(echo.localId),
content = AN_ECHO_CONTENT.content.body, content = AN_ECHO_CONTENT.content.body,
meta = A_META meta = A_META
) ).engine()
} }
@Test @Test
fun `when merging with echo then updates meta with the echos meta`() = runWith(localEchoMapper) { fun `when merging with echo then updates meta with the echos meta`() = runWith(localEchoMapper) {
val previousMeta = MessageMeta.LocalEcho("previous", MessageMeta.LocalEcho.State.Sending) val previousMeta = MessageMeta.LocalEcho("previous", MessageMeta.LocalEcho.State.Sending)
val event = aRoomMessageEvent(meta = previousMeta) val event = aRoomMessageEvent(meta = previousMeta).engine()
val echo = aLocalEcho() val echo = aLocalEcho()
fakeMetaMapper.given(echo).returns(A_META) fakeMetaMapper.given(echo).returns(A_META.engine() as app.dapk.st.engine.MessageMeta.LocalEcho)
val result = event.mergeWith(echo) val result = event.mergeWith(echo)
result shouldBeEqualTo aRoomMessageEvent(meta = A_META) result shouldBeEqualTo aRoomMessageEvent(meta = A_META).engine()
} }
private fun givenEcho(eventId: EventId? = null, localId: String = "", meta: MessageMeta.LocalEcho = A_META): MessageService.LocalEcho { private fun givenEcho(eventId: EventId? = null, localId: String = "", meta: MessageMeta.LocalEcho = A_META): MessageService.LocalEcho {
return aLocalEcho(eventId = eventId, message = aTextMessage(localId = localId)).also { return aLocalEcho(eventId = eventId, message = aTextMessage(localId = localId)).also {
fakeMetaMapper.given(it).returns(meta) fakeMetaMapper.given(it).returns(meta.engine() as app.dapk.st.engine.MessageMeta.LocalEcho)
} }
} }
} }

View File

@ -1,11 +1,8 @@
package app.dapk.st.messenger package app.dapk.st.engine
import app.dapk.st.matrix.common.EventId import app.dapk.st.matrix.common.EventId
import app.dapk.st.matrix.common.MessageType
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
import fixture.* import fixture.*
import internalfake.FakeLocalEventMapper
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test import org.junit.Test
@ -18,12 +15,12 @@ private val ANOTHER_ROOM_MESSAGE_EVENT = A_ROOM_MESSAGE_EVENT.copy(eventId = anE
class MergeWithLocalEchosUseCaseTest { class MergeWithLocalEchosUseCaseTest {
private val fakeLocalEchoMapper = FakeLocalEventMapper() private val fakeLocalEchoMapper = fake.FakeLocalEventMapper()
private val mergeWithLocalEchosUseCase = MergeWithLocalEchosUseCaseImpl(fakeLocalEchoMapper.instance) private val mergeWithLocalEchosUseCase = MergeWithLocalEchosUseCaseImpl(fakeLocalEchoMapper.instance)
@Test @Test
fun `given no local echos, when merging text message, then returns original state`() { fun `given no local echos, when merging text message, then returns original state`() {
val roomState = aRoomState(events = listOf(A_ROOM_MESSAGE_EVENT)) val roomState = aRoomState(events = listOf(A_ROOM_MESSAGE_EVENT)).engine()
val result = mergeWithLocalEchosUseCase.invoke(roomState, A_ROOM_MEMBER, emptyList()) val result = mergeWithLocalEchosUseCase.invoke(roomState, A_ROOM_MEMBER, emptyList())
@ -32,7 +29,7 @@ class MergeWithLocalEchosUseCaseTest {
@Test @Test
fun `given no local echos, when merging events, then returns original ordered by timestamp descending`() { fun `given no local echos, when merging events, then returns original ordered by timestamp descending`() {
val roomState = aRoomState(events = listOf(A_ROOM_IMAGE_MESSAGE_EVENT.copy(utcTimestamp = 1500), A_ROOM_MESSAGE_EVENT.copy(utcTimestamp = 1000))) val roomState = aRoomState(events = listOf(A_ROOM_IMAGE_MESSAGE_EVENT.copy(utcTimestamp = 1500), A_ROOM_MESSAGE_EVENT.copy(utcTimestamp = 1000))).engine()
val result = mergeWithLocalEchosUseCase.invoke(roomState, A_ROOM_MEMBER, emptyList()) val result = mergeWithLocalEchosUseCase.invoke(roomState, A_ROOM_MEMBER, emptyList())
@ -42,15 +39,15 @@ class MergeWithLocalEchosUseCaseTest {
@Test @Test
fun `given local echo with sending state, when merging then maps to room event with local echo state`() { fun `given local echo with sending state, when merging then maps to room event with local echo state`() {
val second = createLocalEcho(A_LOCAL_ECHO_EVENT_ID, A_LOCAL_ECHO_BODY, state = MessageService.LocalEcho.State.Sending) val second = createLocalEcho(A_LOCAL_ECHO_EVENT_ID, A_LOCAL_ECHO_BODY, state = MessageService.LocalEcho.State.Sending)
fakeLocalEchoMapper.givenMapping(second, A_ROOM_MEMBER).returns(ANOTHER_ROOM_MESSAGE_EVENT) fakeLocalEchoMapper.givenMapping(second, A_ROOM_MEMBER).returns(ANOTHER_ROOM_MESSAGE_EVENT.engine())
val roomState = aRoomState(events = listOf(A_ROOM_MESSAGE_EVENT)) val roomState = aRoomState(events = listOf(A_ROOM_MESSAGE_EVENT)).engine()
val result = mergeWithLocalEchosUseCase.invoke(roomState, A_ROOM_MEMBER, listOf(second)) val result = mergeWithLocalEchosUseCase.invoke(roomState, A_ROOM_MEMBER, listOf(second))
result shouldBeEqualTo roomState.copy( result shouldBeEqualTo roomState.copy(
events = listOf( events = listOf(
A_ROOM_MESSAGE_EVENT, A_ROOM_MESSAGE_EVENT.engine(),
ANOTHER_ROOM_MESSAGE_EVENT, ANOTHER_ROOM_MESSAGE_EVENT.engine(),
) )
) )
} }
@ -60,4 +57,4 @@ class MergeWithLocalEchosUseCaseTest {
aTextMessage(aTextContent(body)), aTextMessage(aTextContent(body)),
state, state,
) )
} }

View File

@ -1,7 +1,6 @@
package app.dapk.st.messenger package app.dapk.st.engine
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.sync.MessageMeta
import fixture.aLocalEcho import fixture.aLocalEcho
import fixture.aTextMessage import fixture.aTextMessage
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo

View File

@ -1,13 +1,15 @@
package app.dapk.st.messenger package app.dapk.st.engine
import FlowTestObserver
import app.dapk.st.matrix.common.RoomId import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.RoomMember import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.message.MessageService 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.RoomState
import app.dapk.st.matrix.sync.SyncService import app.dapk.st.matrix.sync.SyncService
import fake.FakeSyncService import fake.FakeSyncService
import fixture.* import fixture.*
import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -16,6 +18,7 @@ import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
import test.FlowTestObserver
import test.delegateReturn import test.delegateReturn
private val A_ROOM_ID = aRoomId() private val A_ROOM_ID = aRoomId()
@ -47,7 +50,7 @@ class TimelineUseCaseTest {
.test(this) .test(this)
.assertValues( .assertValues(
listOf( listOf(
aMessengerState(self = AN_USER_ID, roomState = A_ROOM_STATE) aMessengerState(self = AN_USER_ID, roomState = A_ROOM_STATE.engine())
) )
) )
} }
@ -57,13 +60,13 @@ class TimelineUseCaseTest {
givenSyncEmission(roomState = A_ROOM_STATE, echos = A_LOCAL_ECHOS_LIST) givenSyncEmission(roomState = A_ROOM_STATE, echos = A_LOCAL_ECHOS_LIST)
fakeRoomService.givenFindMember(A_ROOM_ID, AN_USER_ID).returns(A_ROOM_MEMBER) 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) fakeMergeWithLocalEchosUseCase.givenMerging(A_ROOM_STATE, A_ROOM_MEMBER, A_LOCAL_ECHOS_LIST).returns(A_MERGED_ROOM_STATE.engine())
timelineUseCase.invoke(A_ROOM_ID, AN_USER_ID) timelineUseCase.invoke(A_ROOM_ID, AN_USER_ID)
.test(this) .test(this)
.assertValues( .assertValues(
listOf( listOf(
aMessengerState(self = AN_USER_ID, roomState = A_MERGED_ROOM_STATE) aMessengerState(self = AN_USER_ID, roomState = A_MERGED_ROOM_STATE.engine())
) )
) )
} }
@ -81,7 +84,11 @@ class TimelineUseCaseTest {
.test(this) .test(this)
.assertValues( .assertValues(
listOf( listOf(
aMessengerState(self = AN_USER_ID, roomState = A_ROOM_STATE, typing = aTypingSyncEvent(A_ROOM_ID, members = listOf(A_ROOM_MEMBER))) aMessengerState(
self = AN_USER_ID,
roomState = A_ROOM_STATE.engine(),
typing = aTypingSyncEvent(A_ROOM_ID, members = listOf(A_ROOM_MEMBER)).engine()
)
) )
) )
} }
@ -104,11 +111,27 @@ suspend fun <T> Flow<T>.test(scope: CoroutineScope) = FlowTestObserver(scope, th
class FakeMergeWithLocalEchosUseCase : MergeWithLocalEchosUseCase by mockk() { class FakeMergeWithLocalEchosUseCase : MergeWithLocalEchosUseCase by mockk() {
fun givenMerging(roomState: RoomState, roomMember: RoomMember, echos: List<MessageService.LocalEcho>) = every { fun givenMerging(roomState: RoomState, roomMember: RoomMember, echos: List<MessageService.LocalEcho>) = every {
this@FakeMergeWithLocalEchosUseCase.invoke(roomState, roomMember, echos) this@FakeMergeWithLocalEchosUseCase.invoke(roomState.engine(), roomMember, echos)
}.delegateReturn() }.delegateReturn()
} }
fun aTypingSyncEvent( fun aTypingSyncEvent(
roomId: RoomId = aRoomId(), roomId: RoomId = aRoomId(),
members: List<RoomMember> = listOf(aRoomMember()) members: List<RoomMember> = listOf(aRoomMember())
) = SyncService.SyncEvent.Typing(roomId, members) ) = SyncService.SyncEvent.Typing(roomId, members)
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 aMessengerState(
self: UserId = aUserId(),
roomState: app.dapk.st.engine.RoomState,
typing: Typing? = null
) = MessengerState(self, roomState, typing)

View File

@ -1,8 +1,8 @@
package internalfake package fake
import app.dapk.st.engine.LocalEchoMapper
import app.dapk.st.matrix.common.RoomMember import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
import app.dapk.st.messenger.LocalEchoMapper
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk

View File

@ -1,6 +1,6 @@
package internalfake package fake
import app.dapk.st.messenger.LocalIdFactory import app.dapk.st.engine.LocalIdFactory
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import test.delegateReturn import test.delegateReturn

View File

@ -1,7 +1,7 @@
package internalfake package fake
import app.dapk.st.engine.MetaMapper
import app.dapk.st.matrix.message.MessageService import app.dapk.st.matrix.message.MessageService
import app.dapk.st.messenger.MetaMapper
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import test.delegateReturn import test.delegateReturn

View File

@ -5,11 +5,12 @@ import app.dapk.st.matrix.sync.SyncService
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import test.delegateReturn import test.delegateReturn
class FakeSyncService : SyncService by mockk() { class FakeSyncService : SyncService by mockk() {
fun givenStartsSyncing() { fun givenStartsSyncing() {
every { startSyncing() }.returns(emptyFlow()) every { startSyncing() }.returns(flowOf(Unit))
} }
fun givenRoom(roomId: RoomId) = every { room(roomId) }.delegateReturn() fun givenRoom(roomId: RoomId) = every { room(roomId) }.delegateReturn()