From 637f2476e0dca6d76c0922b3aa2f07d5e853b91a Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 12 Dec 2022 11:13:50 +0100 Subject: [PATCH 01/14] Adding changelog entry --- changelog.d/7767.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7767.feature diff --git a/changelog.d/7767.feature b/changelog.d/7767.feature new file mode 100644 index 0000000000..c4386b5e07 --- /dev/null +++ b/changelog.d/7767.feature @@ -0,0 +1 @@ +[Poll] When a poll is ended, use /relations API to ensure poll results are correct From 8c88140b3cc18efdc3fd497e859bdf6b6564b9c8 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 12 Dec 2022 11:40:36 +0100 Subject: [PATCH 02/14] Updating Room API to clarify usage --- .../matrix/android/sdk/internal/session/room/RoomAPI.kt | 9 ++++----- .../session/room/relation/FetchEditHistoryTask.kt | 2 +- .../room/relation/threads/FetchThreadTimelineTask.kt | 6 ++++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 4e55b2c40a..f69ee4dc37 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.room import org.matrix.android.sdk.api.session.events.model.Content 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.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomStrippedState import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams @@ -250,7 +249,7 @@ internal interface RoomAPI { * @param limit max number of Event to retrieve */ @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}/{eventType}") - suspend fun getRelations( + suspend fun getRelationsWithEventType( @Path("roomId") roomId: String, @Path("eventId") eventId: String, @Path("relationType") relationType: String, @@ -261,7 +260,7 @@ internal interface RoomAPI { ): RelationsResponse /** - * Paginate relations for thread events based in normal topological order. + * Paginate relations for events based in normal topological order. * * @param roomId the room Id * @param eventId the event Id @@ -271,10 +270,10 @@ internal interface RoomAPI { * @param limit max number of Event to retrieve */ @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}") - suspend fun getThreadsRelations( + suspend fun getRelations( @Path("roomId") roomId: String, @Path("eventId") eventId: String, - @Path("relationType") relationType: String = RelationType.THREAD, + @Path("relationType") relationType: String, @Query("from") from: String? = null, @Query("to") to: String? = null, @Query("limit") limit: Int? = null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt index 93c7f143fd..50439f51eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt @@ -43,7 +43,7 @@ internal class DefaultFetchEditHistoryTask @Inject constructor( override suspend fun execute(params: FetchEditHistoryTask.Params): List { val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId) val response = executeRequest(globalErrorReceiver) { - roomAPI.getRelations( + roomAPI.getRelationsWithEventType( roomId = params.roomId, eventId = params.eventId, relationType = RelationType.REPLACE, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt index 4cf6445920..1e9a785c80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult 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.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.DefaultCryptoService @@ -102,11 +103,12 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor( override suspend fun execute(params: FetchThreadTimelineTask.Params): Result { val response = executeRequest(globalErrorReceiver) { - roomAPI.getThreadsRelations( + roomAPI.getRelations( roomId = params.roomId, eventId = params.rootThreadEventId, + relationType = RelationType.THREAD, from = params.from, - limit = params.limit + limit = params.limit, ) } From 8b7c8e33519f22e287be3c251edbffb9159b6f9c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 11:26:17 +0100 Subject: [PATCH 03/14] Task to ensure aggregation of all poll responses when receiving ending poll event --- .../sdk/api/session/events/model/Event.kt | 8 +- .../sdk/internal/session/room/RoomModule.kt | 5 + .../poll/DefaultPollAggregationProcessor.kt | 29 +++- .../poll/FetchPollResponseEventsTask.kt | 130 ++++++++++++++++++ 4 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 40ce6ecb5c..9b5f4ac19f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -388,7 +388,13 @@ fun Event.isLocationMessage(): Boolean { } } -fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START.values || getClearType() in EventType.POLL_END.values +fun Event.isPoll(): Boolean = isPollStart() || isPollEnd() + +fun Event.isPollStart(): Boolean = getClearType() in EventType.POLL_START.values + +fun Event.isPollResponse(): Boolean = getClearType() in EventType.POLL_RESPONSE.values + +fun Event.isPollEnd(): Boolean = getClearType() in EventType.POLL_END.values fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 1475b67276..c28d24995f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -99,6 +99,8 @@ import org.matrix.android.sdk.internal.session.room.relation.DefaultUpdateQuickR import org.matrix.android.sdk.internal.session.room.relation.FetchEditHistoryTask import org.matrix.android.sdk.internal.session.room.relation.FindReactionEventForUndoTask import org.matrix.android.sdk.internal.session.room.relation.UpdateQuickReactionTask +import org.matrix.android.sdk.internal.session.room.relation.poll.DefaultFetchPollResponseEventsTask +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetchThreadSummariesTask import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetchThreadTimelineTask import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask @@ -354,4 +356,7 @@ internal abstract class RoomModule { @Binds abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask + + @Binds + abstract fun bindFetchPollResponseEventsTask(task: DefaultFetchPollResponseEventsTask): FetchPollResponseEventsTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt index 455ccabbc6..163d8e5e81 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll import io.realm.Realm +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event @@ -40,9 +41,14 @@ import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSumm import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask +import org.matrix.android.sdk.internal.task.TaskExecutor import javax.inject.Inject -class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationProcessor { +internal class DefaultPollAggregationProcessor @Inject constructor( + private val taskExecutor: TaskExecutor, + private val fetchPollResponseEventsTask: FetchPollResponseEventsTask, +) : PollAggregationProcessor { override fun handlePollStartEvent(realm: Realm, event: Event): Boolean { val content = event.getClearContent()?.toModel() @@ -174,6 +180,10 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro aggregatedPollSummaryEntity.sourceEvents.add(event.eventId) } + if (!isLocalEcho) { + ensurePollIsFullyAggregated(roomId, pollEventId) + } + return true } @@ -200,4 +210,21 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro eventAnnotationsSummaryEntity.pollResponseSummary = it } } + + // TODO add unit tests + /** + * Check that all related votes to a given poll are all retrieved and aggregated. + */ + private fun ensurePollIsFullyAggregated( + roomId: String, + pollEventId: String + ) { + taskExecutor.executorScope.launch { + val params = FetchPollResponseEventsTask.Params( + roomId = roomId, + startPollEventId = pollEventId, + ) + fetchPollResponseEventsTask.execute(params) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt new file mode 100644 index 0000000000..781a0a4d55 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * 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.relation.poll + +import com.zhuinden.monarchy.Monarchy +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult +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.crypto.EventDecryptor +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.EventInsertType +import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore +import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse +import org.matrix.android.sdk.internal.task.Task +import org.matrix.android.sdk.internal.util.awaitTransaction +import org.matrix.android.sdk.internal.util.time.Clock +import javax.inject.Inject + +private const val FETCH_RELATED_EVENTS_LIMIT = 50 + +/** + * Task to fetch all the vote events to ensure full aggregation for a given poll. + */ +internal interface FetchPollResponseEventsTask : Task> { + data class Params( + val roomId: String, + val startPollEventId: String, + ) +} + +// TODO add unit tests +internal class DefaultFetchPollResponseEventsTask @Inject constructor( + private val roomAPI: RoomAPI, + private val globalErrorReceiver: GlobalErrorReceiver, + @SessionDatabase private val monarchy: Monarchy, + private val clock: Clock, + private val eventDecryptor: EventDecryptor, +) : FetchPollResponseEventsTask { + + override suspend fun execute(params: FetchPollResponseEventsTask.Params): Result = runCatching { + var nextBatch: String? = fetchAndProcessRelatedEventsFrom(params) + + while (nextBatch?.isNotEmpty() == true) { + nextBatch = fetchAndProcessRelatedEventsFrom(params, from = nextBatch) + } + } + + private suspend fun fetchAndProcessRelatedEventsFrom(params: FetchPollResponseEventsTask.Params, from: String? = null): String? { + val response = getRelatedEvents(params, from) + + val filteredEvents = response.chunks + .map { decryptEventIfNeeded(it) } + .filter { it.isPollResponse() } + + addMissingEventsInDB(params.roomId, filteredEvents) + + return response.nextBatch + } + + private suspend fun getRelatedEvents(params: FetchPollResponseEventsTask.Params, from: String? = null): RelationsResponse { + return executeRequest(globalErrorReceiver, canRetry = true) { + roomAPI.getRelations( + roomId = params.roomId, + eventId = params.startPollEventId, + relationType = RelationType.REFERENCE, + from = from, + limit = FETCH_RELATED_EVENTS_LIMIT, + ) + } + } + + private suspend fun addMissingEventsInDB(roomId: String, events: List) { + monarchy.awaitTransaction { realm -> + val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() } + val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } + + events.filterNot { it.eventId in existingIds } + .map { + val ageLocalTs = clock.epochMillis() - (it.unsignedData?.age ?: 0) + it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = ageLocalTs) + } + .forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) } + } + } + + private suspend fun decryptEventIfNeeded(event: Event): Event { + // TODO move into a reusable task + if (event.isEncrypted()) { + tryOrNull(message = "Unable to decrypt the event") { + eventDecryptor.decryptEvent(event, "") + } + ?.let { result -> + event.mxDecryptionResult = OlmDecryptionResult( + payload = result.clearEvent, + senderKey = result.senderCurve25519Key, + keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, + forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, + isSafe = result.isSafe + ) + } + } + + event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) + + return event + } +} From 9338ec9805925f23ce4a065cb0c57ca545bb6545 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 11:49:41 +0100 Subject: [PATCH 04/14] Mutualizing decryption of event --- .../sdk/internal/crypto/EventDecryptor.kt | 23 +++++++++++++++++++ .../poll/FetchPollResponseEventsTask.kt | 16 +------------ .../session/room/timeline/GetEventTask.kt | 13 +---------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index bc3309132a..c9eabeab48 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -24,10 +24,12 @@ import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_OLM +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap +import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult 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.content.OlmEventContent @@ -85,6 +87,27 @@ internal class EventDecryptor @Inject constructor( return internalDecryptEvent(event, timeline) } + /** + * Decrypt an event and save the result in the given event. + * + * @param event the raw event. + * @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack. + */ + suspend fun decryptEventAndSaveResult(event: Event, timeline: String) { + tryOrNull(message = "Unable to decrypt the event") { + decryptEvent(event, timeline) + } + ?.let { result -> + event.mxDecryptionResult = OlmDecryptionResult( + payload = result.clearEvent, + senderKey = result.senderCurve25519Key, + keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, + forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, + isSafe = result.isSafe + ) + } + } + /** * Decrypt an event asynchronously. * diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt index 781a0a4d55..3cb8fc5540 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -17,8 +17,6 @@ package org.matrix.android.sdk.internal.session.room.relation.poll import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult 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 @@ -107,20 +105,8 @@ internal class DefaultFetchPollResponseEventsTask @Inject constructor( } private suspend fun decryptEventIfNeeded(event: Event): Event { - // TODO move into a reusable task if (event.isEncrypted()) { - tryOrNull(message = "Unable to decrypt the event") { - eventDecryptor.decryptEvent(event, "") - } - ?.let { result -> - event.mxDecryptionResult = OlmDecryptionResult( - payload = result.clearEvent, - senderKey = result.senderCurve25519Key, - keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, - forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, - isSafe = result.isSafe - ) - } + eventDecryptor.decryptEventAndSaveResult(event, timeline = "") } event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt index e0751865ad..c19272f08a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt @@ -48,18 +48,7 @@ internal class DefaultGetEventTask @Inject constructor( // Try to decrypt the Event if (event.isEncrypted()) { - tryOrNull(message = "Unable to decrypt the event") { - eventDecryptor.decryptEvent(event, "") - } - ?.let { result -> - event.mxDecryptionResult = OlmDecryptionResult( - payload = result.clearEvent, - senderKey = result.senderCurve25519Key, - keysClaimed = result.claimedEd25519Key?.let { mapOf("ed25519" to it) }, - forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain, - isSafe = result.isSafe - ) - } + eventDecryptor.decryptEventAndSaveResult(event, timeline = "") } event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) From 644803dcf3615c738d6f6ad77f39cb81a6cc4166 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 14:13:49 +0100 Subject: [PATCH 05/14] Adding unit test on aggregation processor --- .../poll/DefaultPollAggregationProcessor.kt | 1 - .../DefaultPollAggregationProcessorTest.kt | 38 ++++++++++++++++++- .../fakes/FakeFetchPollResponseEventsTask.kt | 22 +++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt index 163d8e5e81..a424becbd6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessor.kt @@ -211,7 +211,6 @@ internal class DefaultPollAggregationProcessor @Inject constructor( } } - // TODO add unit tests /** * Check that all related votes to a given poll are all retrieved and aggregated. */ diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt index c1fd615e25..6c2fbacd82 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt @@ -16,9 +16,13 @@ package org.matrix.android.sdk.internal.session.room.aggregation.poll +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.realm.RealmList +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeFalse import org.amshove.kluent.shouldBeTrue import org.junit.Before @@ -34,6 +38,7 @@ import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSumm import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.AN_EVENT_ID import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.AN_INVALID_POLL_RESPONSE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_BROKEN_POLL_REPLACE_EVENT +import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_END_CONTENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_END_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REFERENCE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REPLACE_EVENT @@ -43,13 +48,22 @@ import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_ROOM_ID import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_TIMELINE_EVENT import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_USER_ID_1 +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask +import org.matrix.android.sdk.test.fakes.FakeFetchPollResponseEventsTask import org.matrix.android.sdk.test.fakes.FakeRealm +import org.matrix.android.sdk.test.fakes.FakeTaskExecutor import org.matrix.android.sdk.test.fakes.givenEqualTo import org.matrix.android.sdk.test.fakes.givenFindFirst +@OptIn(ExperimentalCoroutinesApi::class) class DefaultPollAggregationProcessorTest { - private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor() + private val fakeTaskExecutor = FakeTaskExecutor() + private val fakeFetchPollResponseEventsTask = FakeFetchPollResponseEventsTask() + private val pollAggregationProcessor: PollAggregationProcessor = DefaultPollAggregationProcessor( + taskExecutor = fakeTaskExecutor.instance, + fetchPollResponseEventsTask = fakeFetchPollResponseEventsTask + ) private val realm = FakeRealm() private val session = mockk() @@ -135,6 +149,28 @@ class DefaultPollAggregationProcessorTest { pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse() } + @Test + fun `given a non local echo poll end event, when is processed, then ensure to aggregate all poll responses`() = runTest { + // Given + every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity() + val powerLevelsHelper = mockRedactionPowerLevels("another-sender-id", true) + val event = A_POLL_END_EVENT.copy(senderId = "another-sender-id") + every { fakeTaskExecutor.instance.executorScope } returns this + val expectedParams = FetchPollResponseEventsTask.Params( + roomId = A_POLL_END_EVENT.roomId.orEmpty(), + startPollEventId = A_POLL_END_CONTENT.relatesTo?.eventId.orEmpty(), + ) + + // When + pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event) + advanceUntilIdle() + + // Then + coVerify { + fakeFetchPollResponseEventsTask.execute(expectedParams) + } + } + private fun mockEventAnnotationsSummaryEntity() { realm.givenWhere() .givenFindFirst(EventAnnotationsSummaryEntity()) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt new file mode 100644 index 0000000000..575dd4f949 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * 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.test.fakes + +import io.mockk.mockk +import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask + +class FakeFetchPollResponseEventsTask : FetchPollResponseEventsTask by mockk(relaxed = true) From bd7b6d6495b1192fbf81ae0ac797354d3650620b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 16:33:27 +0100 Subject: [PATCH 06/14] Adding unit test on task to fetch the poll response events --- .../poll/FetchPollResponseEventsTask.kt | 24 +-- .../DefaultFetchPollResponseEventsTaskTest.kt | 163 ++++++++++++++++++ .../sdk/test/fakes/FakeEventDecryptor.kt | 35 ++++ .../android/sdk/test/fakes/FakeRealm.kt | 8 + .../android/sdk/test/fakes/FakeRoomApi.kt | 61 +++++++ 5 files changed, 280 insertions(+), 11 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt index 3cb8fc5540..3e70da395d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2022 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. @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.relation.poll +import androidx.annotation.VisibleForTesting import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.RelationType @@ -37,7 +38,8 @@ import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.time.Clock import javax.inject.Inject -private const val FETCH_RELATED_EVENTS_LIMIT = 50 +@VisibleForTesting +const val FETCH_RELATED_EVENTS_LIMIT = 50 /** * Task to fetch all the vote events to ensure full aggregation for a given poll. @@ -49,7 +51,6 @@ internal interface FetchPollResponseEventsTask : Task) { monarchy.awaitTransaction { realm -> val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() } - val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } + if(eventIdsToCheck.isNotEmpty()) { + val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } - events.filterNot { it.eventId in existingIds } - .map { - val ageLocalTs = clock.epochMillis() - (it.unsignedData?.age ?: 0) - it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = ageLocalTs) - } - .forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) } + events.filterNot { it.eventId in existingIds } + .map { it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = computeLocalTs(it)) } + .forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) } + } } } @@ -109,8 +109,10 @@ internal class DefaultFetchPollResponseEventsTask @Inject constructor( eventDecryptor.decryptEventAndSaveResult(event, timeline = "") } - event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0) + event.ageLocalTs = computeLocalTs(event) return event } + + private fun computeLocalTs(event: Event) = clock.epochMillis() - (event.unsignedData?.age ?: 0) } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt new file mode 100644 index 0000000000..8d50bac38f --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/relation/poll/DefaultFetchPollResponseEventsTaskTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 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.relation.poll + +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.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.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 defaultFetchPollResponseEventsTask = DefaultFetchPollResponseEventsTask( + roomAPI = fakeRoomAPI.instance, + globalErrorReceiver = fakeGlobalErrorReceiver, + 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 a poll when execute then fetch related events and store them in local if needed`() = runTest { + // Given + val aRoomId = "roomId" + val aPollEventId = "eventId" + val params = givenTaskParams(roomId = aRoomId, eventId = aPollEventId) + val aNextBatchToken = "nextBatch" + val anEventId1 = "eventId1" + val anEventId2 = "eventId2" + val anEventId3 = "eventId3" + val anEventId4 = "eventId4" + val event1 = givenAnEvent(eventId = anEventId1, isPollResponse = true, isEncrypted = true) + val event2 = givenAnEvent(eventId = anEventId2, isPollResponse = true, isEncrypted = true) + val event3 = givenAnEvent(eventId = anEventId3, isPollResponse = false, isEncrypted = false) + val event4 = givenAnEvent(eventId = anEventId4, isPollResponse = false, isEncrypted = false) + val firstEvents = listOf(event1, event2) + val secondEvents = listOf(event3, event4) + val firstResponse = givenARelationsResponse(events = firstEvents, nextBatch = aNextBatchToken) + 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 + + // When + defaultFetchPollResponseEventsTask.execute(params) + + // Then + fakeRoomAPI.verifyGetRelations( + roomId = params.roomId, + eventId = params.startPollEventId, + relationType = RelationType.REFERENCE, + from = null, + limit = FETCH_RELATED_EVENTS_LIMIT + ) + fakeRoomAPI.verifyGetRelations( + roomId = params.roomId, + eventId = params.startPollEventId, + relationType = RelationType.REFERENCE, + from = aNextBatchToken, + 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) + } + } + + private fun givenTaskParams(roomId: String, eventId: String) = FetchPollResponseEventsTask.Params( + roomId = roomId, + startPollEventId = eventId, + ) + + private fun givenARelationsResponse(events: List, nextBatch: String?): RelationsResponse { + return RelationsResponse( + chunks = events, + nextBatch = nextBatch, + prevBatch = null, + ) + } + + private fun givenAnEvent( + eventId: String, + isPollResponse: Boolean, + isEncrypted: Boolean, + ): Event { + val event = mockk(relaxed = true) + every { event.eventId } returns eventId + every { event.isPollResponse() } returns isPollResponse + every { event.isEncrypted() } returns isEncrypted + return event + } + + private fun givenExistingEventEntities(eventIdsToCheck: List, existingIds: List) { + val eventEntities = existingIds.map { EventEntity(eventId = it) } + fakeMonarchy.givenWhere() + .givenIn(EventEntityFields.EVENT_ID, eventIdsToCheck) + .givenFindAll(eventEntities) + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt new file mode 100644 index 0000000000..f2b62ad3ba --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventDecryptor.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 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.test.fakes + +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.internal.crypto.EventDecryptor + +internal class FakeEventDecryptor { + val instance: EventDecryptor = mockk() + + fun givenDecryptEventAndSaveResultSuccess(event: Event) { + coJustRun { instance.decryptEventAndSaveResult(event, any()) } + } + + fun verifyDecryptEventAndSaveResult(event: Event, timeline: String) { + coVerify { instance.decryptEventAndSaveResult(event, timeline) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt index afdcf111f8..ba124a86aa 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt @@ -109,6 +109,14 @@ inline fun RealmQuery.givenLessThan( return this } +inline fun RealmQuery.givenIn( + fieldName: String, + values: List, +): RealmQuery { + every { `in`(fieldName, values.toTypedArray()) } returns this + return this +} + /** * Should be called on a mocked RealmObject and not on a real RealmObject so that the underlying final method is mocked. */ diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt new file mode 100644 index 0000000000..68dbbe7ea6 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRoomApi.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 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.test.fakes + +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse + +internal class FakeRoomApi { + + val instance: RoomAPI = mockk() + + fun givenGetRelationsReturns( + from: String?, + relationsResponse: RelationsResponse, + ) { + coEvery { + instance.getRelations( + roomId = any(), + eventId = any(), + relationType = any(), + from = from, + limit = any() + ) + } returns relationsResponse + } + + fun verifyGetRelations( + roomId: String, + eventId: String, + relationType: String, + from: String?, + limit: Int, + ) { + coVerify { + instance.getRelations( + roomId = roomId, + eventId = eventId, + relationType = relationType, + from = from, + limit = limit + ) + } + } +} From 66abda63ee35bbba6a62f88206dcd3c36737b9c7 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 16:35:34 +0100 Subject: [PATCH 07/14] Removing unused imports --- .../android/sdk/internal/session/room/timeline/GetEventTask.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt index c19272f08a..3707205aef 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/GetEventTask.kt @@ -16,8 +16,6 @@ package org.matrix.android.sdk.internal.session.room.timeline -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.internal.crypto.EventDecryptor import org.matrix.android.sdk.internal.network.GlobalErrorReceiver From dd13e1cb6d8a46556f2dd20d881a90a693ee95e0 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 17:02:09 +0100 Subject: [PATCH 08/14] Fixing Copyright in SDK --- .../android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt index 575dd4f949..cb75d8b708 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeFetchPollResponseEventsTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 2022 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. From 4e4f72f241bc097c1e84e79747b508c1f0f63706 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 14 Dec 2022 17:37:45 +0100 Subject: [PATCH 09/14] Fixing code styling issues --- .../session/room/relation/poll/FetchPollResponseEventsTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt index 3e70da395d..e7dd8c57eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/poll/FetchPollResponseEventsTask.kt @@ -94,7 +94,7 @@ internal class DefaultFetchPollResponseEventsTask @Inject constructor( private suspend fun addMissingEventsInDB(roomId: String, events: List) { monarchy.awaitTransaction { realm -> val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() } - if(eventIdsToCheck.isNotEmpty()) { + if (eventIdsToCheck.isNotEmpty()) { val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId } events.filterNot { it.eventId in existingIds } From e5663ec1c3274d3e0e827414ca2d57711ee25c8d Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 09:45:17 +0100 Subject: [PATCH 10/14] Fixing unit tests --- .../DefaultPollAggregationProcessorTest.kt | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt index 6c2fbacd82..0888d82907 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/poll/DefaultPollAggregationProcessorTest.kt @@ -128,16 +128,28 @@ class DefaultPollAggregationProcessorTest { } @Test - fun `given a poll end event, when processing, then is processed and return true`() { + fun `given a poll end event, when processing, then is processed and return true`() = runTest { + // Given every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity() + every { fakeTaskExecutor.instance.executorScope } returns this + + // When val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, true) + + // Then pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue() } @Test - fun `given a poll end event for my own poll without enough redaction power level, when processing, then is processed and returns true`() { + fun `given a poll end event for my own poll without enough redaction power level, when processing, then is processed and returns true`() = runTest { + // Given every { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity() + every { fakeTaskExecutor.instance.executorScope } returns this + + // When val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, false) + + // Then pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue() } @@ -157,8 +169,8 @@ class DefaultPollAggregationProcessorTest { val event = A_POLL_END_EVENT.copy(senderId = "another-sender-id") every { fakeTaskExecutor.instance.executorScope } returns this val expectedParams = FetchPollResponseEventsTask.Params( - roomId = A_POLL_END_EVENT.roomId.orEmpty(), - startPollEventId = A_POLL_END_CONTENT.relatesTo?.eventId.orEmpty(), + roomId = A_POLL_END_EVENT.roomId.orEmpty(), + startPollEventId = A_POLL_END_CONTENT.relatesTo?.eventId.orEmpty(), ) // When From 301ecdf1f7f11645ae98e772958cf5d9c714b4e9 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 15 Dec 2022 16:46:05 +0100 Subject: [PATCH 11/14] Adding changelog entry --- changelog.d/7797.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7797.feature diff --git a/changelog.d/7797.feature b/changelog.d/7797.feature new file mode 100644 index 0000000000..280f310b91 --- /dev/null +++ b/changelog.d/7797.feature @@ -0,0 +1 @@ +[Session manager] Other sessions list: Security recommendation header should not be sticky From c2d25c8564e3eeb4637138c213c74c2b90610c18 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 16 Dec 2022 09:46:52 +0100 Subject: [PATCH 12/14] Collapsing header in other sessions screen --- .../res/layout/fragment_other_sessions.xml | 208 +++++++++--------- 1 file changed, 110 insertions(+), 98 deletions(-) diff --git a/vector/src/main/res/layout/fragment_other_sessions.xml b/vector/src/main/res/layout/fragment_other_sessions.xml index e25b8b185f..62384b7ee1 100644 --- a/vector/src/main/res/layout/fragment_other_sessions.xml +++ b/vector/src/main/res/layout/fragment_other_sessions.xml @@ -1,120 +1,132 @@ - + + + + + + +