Fixing unit tests in SDK

This commit is contained in:
Maxime NATUREL 2023-01-26 13:50:46 +01:00
parent c7d3e1926f
commit 030e37655e
3 changed files with 150 additions and 42 deletions

View File

@ -58,7 +58,6 @@ internal class DefaultFilterAndStoreEventsTask @Inject constructor(
private suspend fun addMissingEventsInDB(roomId: String, events: List<Event>) {
monarchy.awaitTransaction { realm ->
// TODO we should insert TimelineEventEntity as well, how to do that????
val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() }
if (eventIdsToCheck.isNotEmpty()) {
val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId }

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.room.event
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.isPollResponse
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
import org.matrix.android.sdk.internal.session.room.relation.poll.FETCH_RELATED_EVENTS_LIMIT
import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask
import org.matrix.android.sdk.test.fakes.FakeClock
import org.matrix.android.sdk.test.fakes.FakeEventDecryptor
import org.matrix.android.sdk.test.fakes.FakeMonarchy
import org.matrix.android.sdk.test.fakes.givenFindAll
import org.matrix.android.sdk.test.fakes.givenIn
@OptIn(ExperimentalCoroutinesApi::class)
internal class DefaultFilterAndStoreEventsTaskTest {
private val fakeMonarchy = FakeMonarchy()
private val fakeClock = FakeClock()
private val fakeEventDecryptor = FakeEventDecryptor()
private val defaultFilterAndStoreEventsTask = DefaultFilterAndStoreEventsTask(
monarchy = fakeMonarchy.instance,
clock = fakeClock,
eventDecryptor = fakeEventDecryptor.instance,
)
@Before
fun setup() {
mockkStatic("org.matrix.android.sdk.api.session.events.model.EventKt")
mockkStatic("org.matrix.android.sdk.internal.database.mapper.EventMapperKt")
mockkStatic("org.matrix.android.sdk.internal.database.query.EventEntityQueriesKt")
}
@After
fun tearDown() {
unmockkAll()
}
@Test
fun `given a room and list of events when execute then filter in using given predicate and store them in local if needed`() = runTest {
// Given
val aRoomId = "roomId"
val anEventId1 = "eventId1"
val anEventId2 = "eventId2"
val anEventId3 = "eventId3"
val anEventId4 = "eventId4"
val event1 = givenAnEvent(eventId = anEventId1, isEncrypted = true, clearType = EventType.ENCRYPTED)
val event2 = givenAnEvent(eventId = anEventId2, isEncrypted = true, clearType = EventType.MESSAGE)
val event3 = givenAnEvent(eventId = anEventId3, isEncrypted = false, clearType = EventType.MESSAGE)
val event4 = givenAnEvent(eventId = anEventId4, isEncrypted = false, clearType = EventType.MESSAGE)
val events = listOf(event1, event2, event3, event4)
val filterPredicate = { event: Event -> event == event2 }
val params = givenTaskParams(roomId = aRoomId, events = events, predicate = filterPredicate)
fakeEventDecryptor.givenDecryptEventAndSaveResultSuccess(event1)
fakeEventDecryptor.givenDecryptEventAndSaveResultSuccess(event2)
fakeClock.givenEpoch(123)
givenExistingEventEntities(eventIdsToCheck = listOf(anEventId1, anEventId2), existingIds = listOf(anEventId1))
val eventEntityToSave = EventEntity(eventId = anEventId2)
every { event2.toEntity(any(), any(), any()) } returns eventEntityToSave
every { eventEntityToSave.copyToRealmOrIgnore(any(), any()) } returns eventEntityToSave
// When
defaultFilterAndStoreEventsTask.execute(params)
// Then
fakeEventDecryptor.verifyDecryptEventAndSaveResult(event1, timeline = "")
fakeEventDecryptor.verifyDecryptEventAndSaveResult(event2, timeline = "")
// Check we save in DB the event2 which is a non stored poll response
verify {
event2.toEntity(aRoomId, SendState.SYNCED, any())
eventEntityToSave.copyToRealmOrIgnore(fakeMonarchy.fakeRealm.instance, EventInsertType.PAGINATION)
}
}
private fun givenTaskParams(roomId: String, events: List<Event>, predicate: (Event) -> Boolean) = FilterAndStoreEventsTask.Params(
roomId = roomId,
events = events,
filterPredicate = predicate,
)
private fun givenAnEvent(
eventId: String,
isEncrypted: Boolean,
clearType: String,
): Event {
val event = mockk<Event>(relaxed = true)
every { event.eventId } returns eventId
every { event.isEncrypted() } returns isEncrypted
every { event.getClearType() } returns clearType
return event
}
private fun givenExistingEventEntities(eventIdsToCheck: List<String>, existingIds: List<String>) {
val eventEntities = existingIds.map { EventEntity(eventId = it) }
fakeMonarchy.givenWhere<EventEntity>()
.givenIn(EventEntityFields.EVENT_ID, eventIdsToCheck)
.givenFindAll(eventEntities)
}
}

View File

@ -16,11 +16,12 @@
package org.matrix.android.sdk.internal.session.room.relation.poll
import io.mockk.coJustRun
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.After
@ -29,41 +30,28 @@ import org.junit.Test
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.isPollResponse
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.internal.database.mapper.toEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
import org.matrix.android.sdk.internal.session.room.event.FilterAndStoreEventsTask
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
import org.matrix.android.sdk.test.fakes.FakeClock
import org.matrix.android.sdk.test.fakes.FakeEventDecryptor
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
import org.matrix.android.sdk.test.fakes.FakeMonarchy
import org.matrix.android.sdk.test.fakes.FakeRoomApi
import org.matrix.android.sdk.test.fakes.givenFindAll
import org.matrix.android.sdk.test.fakes.givenIn
@OptIn(ExperimentalCoroutinesApi::class)
internal class DefaultFetchPollResponseEventsTaskTest {
private val fakeRoomAPI = FakeRoomApi()
private val fakeGlobalErrorReceiver = FakeGlobalErrorReceiver()
private val fakeMonarchy = FakeMonarchy()
private val fakeClock = FakeClock()
private val fakeEventDecryptor = FakeEventDecryptor()
private val filterAndStoreEventsTask = mockk<FilterAndStoreEventsTask>()
private val defaultFetchPollResponseEventsTask = DefaultFetchPollResponseEventsTask(
roomAPI = fakeRoomAPI.instance,
globalErrorReceiver = fakeGlobalErrorReceiver,
monarchy = fakeMonarchy.instance,
clock = fakeClock,
eventDecryptor = fakeEventDecryptor.instance,
filterAndStoreEventsTask = filterAndStoreEventsTask,
)
@Before
fun setup() {
mockkStatic("org.matrix.android.sdk.api.session.events.model.EventKt")
mockkStatic("org.matrix.android.sdk.internal.database.mapper.EventMapperKt")
mockkStatic("org.matrix.android.sdk.internal.database.query.EventEntityQueriesKt")
}
@ -74,7 +62,7 @@ internal class DefaultFetchPollResponseEventsTaskTest {
}
@Test
fun `given a room and a poll when execute then fetch related events and store them in local if needed`() = runTest {
fun `given a room and a poll when execute then fetch related events and store them in local`() = runTest {
// Given
val aRoomId = "roomId"
val aPollEventId = "eventId"
@ -94,13 +82,7 @@ internal class DefaultFetchPollResponseEventsTaskTest {
fakeRoomAPI.givenGetRelationsReturns(from = null, relationsResponse = firstResponse)
val secondResponse = givenARelationsResponse(events = secondEvents, nextBatch = null)
fakeRoomAPI.givenGetRelationsReturns(from = aNextBatchToken, relationsResponse = secondResponse)
fakeEventDecryptor.givenDecryptEventAndSaveResultSuccess(event1)
fakeEventDecryptor.givenDecryptEventAndSaveResultSuccess(event2)
fakeClock.givenEpoch(123)
givenExistingEventEntities(eventIdsToCheck = listOf(anEventId1, anEventId2), existingIds = listOf(anEventId1))
val eventEntityToSave = EventEntity(eventId = anEventId2)
every { event2.toEntity(any(), any(), any()) } returns eventEntityToSave
every { eventEntityToSave.copyToRealmOrIgnore(any(), any()) } returns eventEntityToSave
coJustRun { filterAndStoreEventsTask.execute(any()) }
// When
defaultFetchPollResponseEventsTask.execute(params)
@ -111,21 +93,22 @@ internal class DefaultFetchPollResponseEventsTaskTest {
eventId = params.startPollEventId,
relationType = RelationType.REFERENCE,
from = null,
limit = FETCH_RELATED_EVENTS_LIMIT
limit = FETCH_RELATED_EVENTS_LIMIT,
)
fakeRoomAPI.verifyGetRelations(
roomId = params.roomId,
eventId = params.startPollEventId,
relationType = RelationType.REFERENCE,
from = aNextBatchToken,
limit = FETCH_RELATED_EVENTS_LIMIT
limit = FETCH_RELATED_EVENTS_LIMIT,
)
fakeEventDecryptor.verifyDecryptEventAndSaveResult(event1, timeline = "")
fakeEventDecryptor.verifyDecryptEventAndSaveResult(event2, timeline = "")
// Check we save in DB the event2 which is a non stored poll response
verify {
event2.toEntity(aRoomId, SendState.SYNCED, any())
eventEntityToSave.copyToRealmOrIgnore(fakeMonarchy.fakeRealm.instance, EventInsertType.PAGINATION)
coVerify {
filterAndStoreEventsTask.execute(match {
it.roomId == aRoomId && it.events == firstEvents
})
filterAndStoreEventsTask.execute(match {
it.roomId == aRoomId && it.events == secondEvents
})
}
}
@ -153,11 +136,4 @@ internal class DefaultFetchPollResponseEventsTaskTest {
every { event.isEncrypted() } returns isEncrypted
return event
}
private fun givenExistingEventEntities(eventIdsToCheck: List<String>, existingIds: List<String>) {
val eventEntities = existingIds.map { EventEntity(eventId = it) }
fakeMonarchy.givenWhere<EventEntity>()
.givenIn(EventEntityFields.EVENT_ID, eventIdsToCheck)
.givenFindAll(eventEntities)
}
}