Merge pull request #7779 from vector-im/feature/mna/relations-api-poll-end
[Poll] When a poll is ended, use /relations API to ensure poll results are correct (PSG-1013)
This commit is contained in:
commit
648f97ba78
|
@ -0,0 +1 @@
|
||||||
|
[Poll] When a poll is ended, use /relations API to ensure poll results are correct
|
|
@ -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
|
fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,12 @@ import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_OLM
|
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.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
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.MXEventDecryptionResult
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
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.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.OlmEventContent
|
import org.matrix.android.sdk.api.session.events.model.content.OlmEventContent
|
||||||
|
@ -85,6 +87,27 @@ internal class EventDecryptor @Inject constructor(
|
||||||
return internalDecryptEvent(event, timeline)
|
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.
|
* Decrypt an event asynchronously.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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.Content
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
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.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomStrippedState
|
import org.matrix.android.sdk.api.session.room.model.RoomStrippedState
|
||||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
|
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||||
|
@ -251,7 +250,7 @@ internal interface RoomAPI {
|
||||||
* @param limit max number of Event to retrieve
|
* @param limit max number of Event to retrieve
|
||||||
*/
|
*/
|
||||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}/{eventType}")
|
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}/{eventType}")
|
||||||
suspend fun getRelations(
|
suspend fun getRelationsWithEventType(
|
||||||
@Path("roomId") roomId: String,
|
@Path("roomId") roomId: String,
|
||||||
@Path("eventId") eventId: String,
|
@Path("eventId") eventId: String,
|
||||||
@Path("relationType") relationType: String,
|
@Path("relationType") relationType: String,
|
||||||
|
@ -262,7 +261,7 @@ internal interface RoomAPI {
|
||||||
): RelationsResponse
|
): 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 roomId the room Id
|
||||||
* @param eventId the event Id
|
* @param eventId the event Id
|
||||||
|
@ -272,10 +271,10 @@ internal interface RoomAPI {
|
||||||
* @param limit max number of Event to retrieve
|
* @param limit max number of Event to retrieve
|
||||||
*/
|
*/
|
||||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}")
|
@GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}")
|
||||||
suspend fun getThreadsRelations(
|
suspend fun getRelations(
|
||||||
@Path("roomId") roomId: String,
|
@Path("roomId") roomId: String,
|
||||||
@Path("eventId") eventId: String,
|
@Path("eventId") eventId: String,
|
||||||
@Path("relationType") relationType: String = RelationType.THREAD,
|
@Path("relationType") relationType: String,
|
||||||
@Query("from") from: String? = null,
|
@Query("from") from: String? = null,
|
||||||
@Query("to") to: String? = null,
|
@Query("to") to: String? = null,
|
||||||
@Query("limit") limit: Int? = null
|
@Query("limit") limit: Int? = null
|
||||||
|
|
|
@ -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.FetchEditHistoryTask
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.FindReactionEventForUndoTask
|
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.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.DefaultFetchThreadSummariesTask
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.DefaultFetchThreadTimelineTask
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadSummariesTask
|
||||||
|
@ -354,4 +356,7 @@ internal abstract class RoomModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask
|
abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindFetchPollResponseEventsTask(task: DefaultFetchPollResponseEventsTask): FetchPollResponseEventsTask
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.matrix.android.sdk.internal.session.room.aggregation.poll
|
package org.matrix.android.sdk.internal.session.room.aggregation.poll
|
||||||
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
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.create
|
||||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
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
|
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 {
|
override fun handlePollStartEvent(realm: Realm, event: Event): Boolean {
|
||||||
val content = event.getClearContent()?.toModel<MessagePollContent>()
|
val content = event.getClearContent()?.toModel<MessagePollContent>()
|
||||||
|
@ -174,6 +180,10 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro
|
||||||
aggregatedPollSummaryEntity.sourceEvents.add(event.eventId)
|
aggregatedPollSummaryEntity.sourceEvents.add(event.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isLocalEcho) {
|
||||||
|
ensurePollIsFullyAggregated(roomId, pollEventId)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,4 +210,20 @@ class DefaultPollAggregationProcessor @Inject constructor() : PollAggregationPro
|
||||||
eventAnnotationsSummaryEntity.pollResponseSummary = it
|
eventAnnotationsSummaryEntity.pollResponseSummary = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ internal class DefaultFetchEditHistoryTask @Inject constructor(
|
||||||
override suspend fun execute(params: FetchEditHistoryTask.Params): List<Event> {
|
override suspend fun execute(params: FetchEditHistoryTask.Params): List<Event> {
|
||||||
val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId)
|
val isRoomEncrypted = cryptoSessionInfoProvider.isRoomEncrypted(params.roomId)
|
||||||
val response = executeRequest(globalErrorReceiver) {
|
val response = executeRequest(globalErrorReceiver) {
|
||||||
roomAPI.getRelations(
|
roomAPI.getRelationsWithEventType(
|
||||||
roomId = params.roomId,
|
roomId = params.roomId,
|
||||||
eventId = params.eventId,
|
eventId = params.eventId,
|
||||||
relationType = RelationType.REPLACE,
|
relationType = RelationType.REPLACE,
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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 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
|
||||||
|
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
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
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<FetchPollResponseEventsTask.Params, Result<Unit>> {
|
||||||
|
data class Params(
|
||||||
|
val roomId: String,
|
||||||
|
val startPollEventId: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Unit> = 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<Event>) {
|
||||||
|
monarchy.awaitTransaction { realm ->
|
||||||
|
val eventIdsToCheck = events.mapNotNull { it.eventId }.filter { it.isNotEmpty() }
|
||||||
|
if (eventIdsToCheck.isNotEmpty()) {
|
||||||
|
val existingIds = EventEntity.where(realm, eventIdsToCheck).findAll().toList().map { it.eventId }
|
||||||
|
|
||||||
|
events.filterNot { it.eventId in existingIds }
|
||||||
|
.map { it.toEntity(roomId = roomId, sendState = SendState.SYNCED, ageLocalTs = computeLocalTs(it)) }
|
||||||
|
.forEach { it.copyToRealmOrIgnore(realm, EventInsertType.PAGINATION) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun decryptEventIfNeeded(event: Event): Event {
|
||||||
|
if (event.isEncrypted()) {
|
||||||
|
eventDecryptor.decryptEventAndSaveResult(event, timeline = "")
|
||||||
|
}
|
||||||
|
|
||||||
|
event.ageLocalTs = computeLocalTs(event)
|
||||||
|
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeLocalTs(event: Event) = clock.epochMillis() - (event.unsignedData?.age ?: 0)
|
||||||
|
}
|
|
@ -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.crypto.model.OlmDecryptionResult
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
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.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.model.RoomMemberContent
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
|
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 {
|
override suspend fun execute(params: FetchThreadTimelineTask.Params): Result {
|
||||||
val response = executeRequest(globalErrorReceiver) {
|
val response = executeRequest(globalErrorReceiver) {
|
||||||
roomAPI.getThreadsRelations(
|
roomAPI.getRelations(
|
||||||
roomId = params.roomId,
|
roomId = params.roomId,
|
||||||
eventId = params.rootThreadEventId,
|
eventId = params.rootThreadEventId,
|
||||||
|
relationType = RelationType.THREAD,
|
||||||
from = params.from,
|
from = params.from,
|
||||||
limit = params.limit
|
limit = params.limit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.room.timeline
|
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.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
||||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
|
@ -48,18 +46,7 @@ internal class DefaultGetEventTask @Inject constructor(
|
||||||
|
|
||||||
// Try to decrypt the Event
|
// Try to decrypt the Event
|
||||||
if (event.isEncrypted()) {
|
if (event.isEncrypted()) {
|
||||||
tryOrNull(message = "Unable to decrypt the event") {
|
eventDecryptor.decryptEventAndSaveResult(event, timeline = "")
|
||||||
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)
|
event.ageLocalTs = clock.epochMillis() - (event.unsignedData?.age ?: 0)
|
||||||
|
|
|
@ -16,9 +16,13 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.room.aggregation.poll
|
package org.matrix.android.sdk.internal.session.room.aggregation.poll
|
||||||
|
|
||||||
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.realm.RealmList
|
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.shouldBeFalse
|
||||||
import org.amshove.kluent.shouldBeTrue
|
import org.amshove.kluent.shouldBeTrue
|
||||||
import org.junit.Before
|
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_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.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_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_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_REFERENCE_EVENT
|
||||||
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollEventsTestData.A_POLL_REPLACE_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_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_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.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.FakeRealm
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeTaskExecutor
|
||||||
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
||||||
import org.matrix.android.sdk.test.fakes.givenFindFirst
|
import org.matrix.android.sdk.test.fakes.givenFindFirst
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class DefaultPollAggregationProcessorTest {
|
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 realm = FakeRealm()
|
||||||
private val session = mockk<Session>()
|
private val session = mockk<Session>()
|
||||||
|
|
||||||
|
@ -114,16 +128,28 @@ class DefaultPollAggregationProcessorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
|
||||||
|
every { fakeTaskExecutor.instance.executorScope } returns this
|
||||||
|
|
||||||
|
// When
|
||||||
val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, true)
|
val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, true)
|
||||||
|
|
||||||
|
// Then
|
||||||
pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue()
|
pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 { realm.instance.createObject(PollResponseAggregatedSummaryEntity::class.java) } returns PollResponseAggregatedSummaryEntity()
|
||||||
|
every { fakeTaskExecutor.instance.executorScope } returns this
|
||||||
|
|
||||||
|
// When
|
||||||
val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, false)
|
val powerLevelsHelper = mockRedactionPowerLevels(A_USER_ID_1, false)
|
||||||
|
|
||||||
|
// Then
|
||||||
pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue()
|
pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, A_POLL_END_EVENT).shouldBeTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +161,28 @@ class DefaultPollAggregationProcessorTest {
|
||||||
pollAggregationProcessor.handlePollEndEvent(session, powerLevelsHelper, realm.instance, event).shouldBeFalse()
|
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() {
|
private fun mockEventAnnotationsSummaryEntity() {
|
||||||
realm.givenWhere<EventAnnotationsSummaryEntity>()
|
realm.givenWhere<EventAnnotationsSummaryEntity>()
|
||||||
.givenFindFirst(EventAnnotationsSummaryEntity())
|
.givenFindFirst(EventAnnotationsSummaryEntity())
|
||||||
|
|
|
@ -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<Event>, nextBatch: String?): RelationsResponse {
|
||||||
|
return RelationsResponse(
|
||||||
|
chunks = events,
|
||||||
|
nextBatch = nextBatch,
|
||||||
|
prevBatch = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenAnEvent(
|
||||||
|
eventId: String,
|
||||||
|
isPollResponse: Boolean,
|
||||||
|
isEncrypted: Boolean,
|
||||||
|
): Event {
|
||||||
|
val event = mockk<Event>(relaxed = true)
|
||||||
|
every { event.eventId } returns eventId
|
||||||
|
every { event.isPollResponse() } returns isPollResponse
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.room.relation.poll.FetchPollResponseEventsTask
|
||||||
|
|
||||||
|
class FakeFetchPollResponseEventsTask : FetchPollResponseEventsTask by mockk(relaxed = true)
|
|
@ -109,6 +109,14 @@ inline fun <reified T : RealmModel> RealmQuery<T>.givenLessThan(
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : RealmModel> RealmQuery<T>.givenIn(
|
||||||
|
fieldName: String,
|
||||||
|
values: List<String>,
|
||||||
|
): RealmQuery<T> {
|
||||||
|
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.
|
* Should be called on a mocked RealmObject and not on a real RealmObject so that the underlying final method is mocked.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue