Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ public class DebugScope internal constructor(
eventProcessor = eventProcessor
)

public val getConversationEpochFromCC: GetConversationEpochFromCCUseCase
get() = GetConversationEpochFromCCUseCaseImpl(
conversationRepository = conversationRepository,
transactionProvider = transactionProvider
)

public val synchronizeExternalData: SynchronizeExternalDataUseCase
get() = SynchronizeExternalDataUseCaseImpl(
eventRepository = eventRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.debug

import com.wire.kalium.common.error.CoreFailure
import com.wire.kalium.common.functional.fold
import com.wire.kalium.common.functional.right
import com.wire.kalium.logic.data.client.CryptoTransactionProvider
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.id.ConversationId

/**
* Returns the current conv epoch from core-crypto
* see [MlsCoreCryptoContext.conversationEpoch]
*/
public interface GetConversationEpochFromCCUseCase {
public suspend operator fun invoke(conversationId: ConversationId): GetConversationEpochFromCCResult
}

internal class GetConversationEpochFromCCUseCaseImpl(
private val conversationRepository: ConversationRepository,
private val transactionProvider: CryptoTransactionProvider,
) : GetConversationEpochFromCCUseCase {

override suspend fun invoke(conversationId: ConversationId): GetConversationEpochFromCCResult =
conversationRepository.getConversationById(conversationId).fold(
fnL = { GetConversationEpochFromCCResult.Failure.Generic(it) },
fnR = { conversation ->
val protocol = conversation.protocol as? Conversation.ProtocolInfo.MLSCapable
if (protocol == null) {
GetConversationEpochFromCCResult.Failure.NotMlsConversation
} else {
transactionProvider.mlsTransaction("GetConversationEpochFromCC") { mlsContext ->
mlsContext.conversationEpoch(protocol.groupId.value).right()
}.fold(
fnL = { GetConversationEpochFromCCResult.Failure.Generic(it) },
fnR = { GetConversationEpochFromCCResult.Success(it) }
)
}
}
)
}

public sealed class GetConversationEpochFromCCResult {
public data class Success(val epoch: ULong) : GetConversationEpochFromCCResult()

public sealed class Failure : GetConversationEpochFromCCResult() {
public data object NotMlsConversation : Failure()
public data class Generic(val coreFailure: CoreFailure) : Failure()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.debug

import com.wire.kalium.common.error.StorageFailure
import com.wire.kalium.common.functional.Either
import com.wire.kalium.common.functional.right
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.framework.TestConversation
import com.wire.kalium.logic.util.arrangement.provider.CryptoTransactionProviderArrangement
import com.wire.kalium.logic.util.arrangement.provider.CryptoTransactionProviderArrangementMokkeryImpl
import dev.mokkery.answering.returns
import dev.mokkery.everySuspend
import dev.mokkery.matcher.any
import dev.mokkery.matcher.eq
import dev.mokkery.mock
import dev.mokkery.verify.VerifyMode
import dev.mokkery.verifySuspend
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

class GetConversationEpochFromCCUseCaseTest {

@Test
fun givenMLSConversation_whenInvoked_thenReturnEpochFromCC() = runTest {
val (arrangement, useCase) = Arrangement()
.withConversation(TestConversation.GROUP(TestConversation.MLS_PROTOCOL_INFO).right())
.withCCEpoch(EXPECTED_EPOCH)
.arrange()

val result = useCase(TestConversation.ID)

verifySuspend(VerifyMode.exactly(1)) {
arrangement.mlsContext.conversationEpoch(eq(TestConversation.GROUP_ID.value))
}
assertEquals(GetConversationEpochFromCCResult.Success(EXPECTED_EPOCH), result)
}

@Test
fun givenProteusConversation_whenInvoked_thenReturnNotMlsConversationFailure() = runTest {
val (arrangement, useCase) = Arrangement()
.withConversation(TestConversation.GROUP(Conversation.ProtocolInfo.Proteus).right())
.arrange()

val result = useCase(TestConversation.ID)

verifySuspend(VerifyMode.not) {
arrangement.cryptoTransactionProvider.mlsTransaction<ULong>(any(), any())
}
assertIs<GetConversationEpochFromCCResult.Failure.NotMlsConversation>(result)
}

private class Arrangement : CryptoTransactionProviderArrangement by CryptoTransactionProviderArrangementMokkeryImpl() {
val conversationRepository = mock<ConversationRepository>()
private var ccEpoch = EXPECTED_EPOCH

suspend fun withConversation(result: Either<StorageFailure, Conversation>) = apply {
everySuspend { conversationRepository.getConversationById(any()) } returns result
}

suspend fun withCCEpoch(epoch: ULong) = apply {
ccEpoch = epoch
everySuspend { mlsContext.conversationEpoch(any()) } returns epoch
}

suspend fun arrange(): Pair<Arrangement, GetConversationEpochFromCCUseCaseImpl> {
withMLSTransactionReturning(ccEpoch.right())
return this to GetConversationEpochFromCCUseCaseImpl(
conversationRepository = conversationRepository,
transactionProvider = cryptoTransactionProvider,
)
}
}

private companion object {
val EXPECTED_EPOCH = 42UL
}
}
Loading