Skip to content
Merged
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
1 change: 0 additions & 1 deletion feature/mail/message/list/internal/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
plugins {
id(ThunderbirdPlugins.Library.androidCompose)
alias(libs.plugins.dev.mokkery)
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ package net.thunderbird.feature.mail.message.list.internal.domain.usecase

import app.cash.turbine.test
import assertk.assertThat
import assertk.assertions.containsExactly
import assertk.assertions.hasMessage
import assertk.assertions.isEmpty
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import assertk.assertions.prop
import com.fsck.k9.backend.api.FolderInfo
import com.fsck.k9.mail.folders.FolderServerId
import dev.mokkery.matcher.any
import dev.mokkery.spy
import dev.mokkery.verify
import dev.mokkery.verify.VerifyMode.Companion.exactly
import dev.mokkery.verifySuspend
import kotlin.test.Test
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
Expand Down Expand Up @@ -44,7 +41,7 @@ class CreateArchiveFolderTest {
// Arrange
val accountUuid = Uuid.random().toHexString()
val accounts = createAccountList(accountUuid = accountUuid)
val accountManager = spy(FakeLegacyAccountManager(accounts))
val accountManager = FakeLegacyAccountManager(accounts)
val testSubject = createTestSubject(accountManager = accountManager)
val folderName = ""

Expand All @@ -59,7 +56,7 @@ class CreateArchiveFolderTest {
.prop("folderName") { it.folderName }
.isEqualTo(folderName)

verify(exactly(0)) { accountManager.getById(id = any()) }
assertThat(accountManager.getByIdCalls).isEmpty()

awaitComplete()
}
Expand All @@ -71,7 +68,7 @@ class CreateArchiveFolderTest {
// Arrange
val accountUuid = Uuid.random().toHexString()
val accounts = createAccountList()
val accountManager = spy(FakeLegacyAccountManager(accounts))
val accountManager = FakeLegacyAccountManager(accounts)
val testSubject = createTestSubject(accountManager = accountManager)
val folderName = "TheFolder"

Expand All @@ -84,7 +81,7 @@ class CreateArchiveFolderTest {
.prop("error") { it.error }
.isEqualTo(CreateArchiveFolderOutcome.Error.AccountNotFound)

verify(exactly(1)) { accountManager.getById(AccountIdFactory.of(accountUuid)) }
assertThat(accountManager.getByIdCalls).containsExactly(AccountIdFactory.of(accountUuid))
awaitComplete()
}
}
Expand All @@ -97,7 +94,7 @@ class CreateArchiveFolderTest {
val accounts = createAccountList(accountUuid)
val exception = MessagingException("this is an error")
val backendFolderUpdater = FakeBackendFolderUpdater(exception)
val remoteFolderCreatorFactory = spy(FakeRemoteFolderCreatorFactory(outcome = null))
val remoteFolderCreatorFactory = FakeRemoteFolderCreatorFactory(outcome = null)
val testSubject = createTestSubject(
accounts = accounts,
backendStorageFactory = FakeBackendStorageFactory(backendFolderUpdater),
Expand All @@ -116,9 +113,7 @@ class CreateArchiveFolderTest {
.prop("throwable") { it.throwable }
.hasMessage(exception.message)

verifySuspend(exactly(0)) {
remoteFolderCreatorFactory.create(accountId = any())
}
assertThat(remoteFolderCreatorFactory.createCalls).isEmpty()

awaitComplete()
}
Expand All @@ -135,7 +130,7 @@ class CreateArchiveFolderTest {
returnEmptySetWhenCreatingFolders = true,
),
)
val remoteFolderCreatorFactory = spy(FakeRemoteFolderCreatorFactory(outcome = null))
val remoteFolderCreatorFactory = FakeRemoteFolderCreatorFactory(outcome = null)
val testSubject = createTestSubject(
accounts = accounts,
backendStorageFactory = backendStorageFactory,
Expand All @@ -153,22 +148,9 @@ class CreateArchiveFolderTest {
.prop("folderName") { it.folderName }
.isEqualTo(folderName)

verify(exactly(1)) {
// verify doesn't support verifying the extension function `createFolder`,
// thus we verify the call of `createFolders(list)` instead.
backendStorageFactory.backendFolderUpdater.createFolders(
listOf(
FolderInfo(
serverId = folderName,
name = folderName,
type = LegacyFolderType.ARCHIVE,
),
),
)
}
verifySuspend(exactly(0)) {
remoteFolderCreatorFactory.create(accountId = any())
}
assertThat(backendStorageFactory.backendFolderUpdater.createFoldersCalls)
.containsExactly(createExpectedFolderInfo(folderName))
assertThat(remoteFolderCreatorFactory.createCalls).isEmpty()
awaitComplete()
}
}
Expand Down Expand Up @@ -197,19 +179,8 @@ class CreateArchiveFolderTest {
.prop("data") { it.data }
.isEqualTo(CreateArchiveFolderOutcome.Success.LocalFolderCreated)

verify(exactly(1)) {
// verify doesn't support verifying the extension function `createFolder`,
// thus we verify the call of `createFolders(list)` instead.
backendStorageFactory.backendFolderUpdater.createFolders(
listOf(
FolderInfo(
serverId = folderName,
name = folderName,
type = LegacyFolderType.ARCHIVE,
),
),
)
}
assertThat(backendStorageFactory.backendFolderUpdater.createFoldersCalls)
.containsExactly(createExpectedFolderInfo(folderName))

cancelAndIgnoreRemainingEvents()
}
Expand Down Expand Up @@ -242,19 +213,8 @@ class CreateArchiveFolderTest {
.prop("serverId") { it.serverId }
.isEqualTo(FolderServerId(folderName))

verify(exactly(1)) {
// verify doesn't support verifying the extension function `createFolder`,
// thus we verify the call of `createFolders(list)` instead.
backendStorageFactory.backendFolderUpdater.createFolders(
listOf(
FolderInfo(
serverId = folderName,
name = folderName,
type = LegacyFolderType.ARCHIVE,
),
),
)
}
assertThat(backendStorageFactory.backendFolderUpdater.createFoldersCalls)
.containsExactly(createExpectedFolderInfo(folderName))

cancelAndIgnoreRemainingEvents()
}
Expand Down Expand Up @@ -293,19 +253,8 @@ class CreateArchiveFolderTest {
),
)

verify(exactly(1)) {
// verify doesn't support verifying the extension function `createFolder`,
// thus we verify the call of `createFolders(list)` instead.
backendStorageFactory.backendFolderUpdater.createFolders(
listOf(
FolderInfo(
serverId = folderName,
name = folderName,
type = LegacyFolderType.ARCHIVE,
),
),
)
}
assertThat(backendStorageFactory.backendFolderUpdater.createFoldersCalls)
.containsExactly(createExpectedFolderInfo(folderName))

cancelAndIgnoreRemainingEvents()
}
Expand All @@ -317,7 +266,7 @@ class CreateArchiveFolderTest {
// Arrange
val accountUuid = Uuid.random().toHexString()
val accounts = createAccountList(accountUuid)
val accountManager = spy(FakeLegacyAccountManager(accounts))
val accountManager = FakeLegacyAccountManager(accounts)
val backendStorageFactory = FakeBackendStorageFactory(
FakeBackendFolderUpdater(),
)
Expand Down Expand Up @@ -349,44 +298,26 @@ class CreateArchiveFolderTest {
.prop("data") { it.data }
.isEqualTo(CreateArchiveFolderOutcome.Success.Created)

verify(exactly(1)) { accountManager.getById(AccountIdFactory.of(accountUuid)) }
verify(exactly(1)) {
// verify doesn't support verifying the extension function `createFolder`,
// thus we verify the call of `createFolders(list)` instead.
backendStorageFactory.backendFolderUpdater.createFolders(
listOf(
FolderInfo(
serverId = folderName,
name = folderName,
type = LegacyFolderType.ARCHIVE,
),
assertThat(accountManager.getByIdCalls).containsExactly(AccountIdFactory.of(accountUuid))
assertThat(backendStorageFactory.backendFolderUpdater.createFoldersCalls)
.containsExactly(createExpectedFolderInfo(folderName))

assertThat(remoteFolderCreatorFactory.instance.createCalls)
.containsExactly(
FakeRemoteFolderCreatorFactory.CreateCall(
folderServerId = FolderServerId(folderName),
mustCreate = false,
folderType = LegacyFolderType.ARCHIVE,
),
)
}

verifySuspend(exactly(1)) {
remoteFolderCreatorFactory.instance.create(
folderServerId = FolderServerId(folderName),
mustCreate = false,
folderType = LegacyFolderType.ARCHIVE,
)
}
assertThat(specialFolderUpdaterFactory.specialFolderUpdater.setSpecialFolderCalls)
.transform { calls -> calls.map { it.type to it.selection } }
.containsExactly(FolderType.ARCHIVE to SpecialFolderSelection.MANUAL)

verify(exactly(1)) {
specialFolderUpdaterFactory.specialFolderUpdater.setSpecialFolder(
type = FolderType.ARCHIVE,
folderId = any(),
selection = SpecialFolderSelection.MANUAL,
)
}

verify(exactly(1)) {
specialFolderUpdaterFactory.specialFolderUpdater.updateSpecialFolders()
}
assertThat(specialFolderUpdaterFactory.specialFolderUpdater.updateSpecialFoldersCalls).isEqualTo(1)

verify(exactly(1)) {
accountManager.saveAccount(account = any())
}
assertThat(accountManager.savedAccounts.map { it.id }).containsExactly(AccountIdFactory.of(accountUuid))

awaitComplete()
}
Expand Down Expand Up @@ -421,22 +352,47 @@ class CreateArchiveFolderTest {
val id = if (it == 0) AccountIdFactory.of(accountUuid) else AccountIdFactory.create()
FakeLegacyAccount(id = id)
}

private fun createExpectedFolderInfo(folderName: String) = listOf(
FolderInfo(
serverId = folderName,
name = folderName,
type = LegacyFolderType.ARCHIVE,
),
)
}

private open class FakeRemoteFolderCreatorFactory(
protected open val outcome: Outcome<RemoteFolderCreationOutcome.Success, RemoteFolderCreationOutcome.Error>?,
private class FakeRemoteFolderCreatorFactory(
private val outcome: Outcome<RemoteFolderCreationOutcome.Success, RemoteFolderCreationOutcome.Error>?,
) : RemoteFolderCreator.Factory {
open var instance: RemoteFolderCreator = spy<RemoteFolderCreator>(FakeRemoteFolderCreator())
protected set
val createCalls = mutableListOf<AccountId>()
val instance = FakeRemoteFolderCreator()

override suspend fun create(accountId: AccountId): RemoteFolderCreator {
createCalls += accountId
return instance
}

override suspend fun create(accountId: AccountId): RemoteFolderCreator = instance
data class CreateCall(
val folderServerId: FolderServerId,
val mustCreate: Boolean,
val folderType: LegacyFolderType,
)

inner class FakeRemoteFolderCreator : RemoteFolderCreator {
val createCalls = mutableListOf<CreateCall>()

private open inner class FakeRemoteFolderCreator : RemoteFolderCreator {
override suspend fun create(
folderServerId: FolderServerId,
mustCreate: Boolean,
folderType: LegacyFolderType,
): Outcome<RemoteFolderCreationOutcome.Success, RemoteFolderCreationOutcome.Error> =
outcome ?: error("Not expected to be called in this context.")
): Outcome<RemoteFolderCreationOutcome.Success, RemoteFolderCreationOutcome.Error> {
createCalls += CreateCall(
folderServerId = folderServerId,
mustCreate = mustCreate,
folderType = folderType,
)
return outcome ?: error("Not expected to be called in this context.")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package net.thunderbird.feature.mail.message.list.internal.domain.usecase

import assertk.assertThat
import assertk.assertions.isEqualTo
import dev.mokkery.spy
import dev.mokkery.verifySuspend
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
Expand All @@ -24,9 +22,7 @@ class GetSortCriteriaPerAccountTest {
fun `invoke should return default sort type when accounts are empty`() = runTest {
// Arrange
val defaultSortCriteria = SortCriteria(primary = SortType.DateDesc)
val getDefaultSortCriteria = spy(
obj = DomainContract.UseCase.GetDefaultSortCriteria { defaultSortCriteria },
)
val getDefaultSortCriteria = FakeGetDefaultSortCriteria(defaultSortCriteria)
val useCase = createTestSubject(
accounts = emptyList(),
getDefaultSortCriteria = getDefaultSortCriteria,
Expand All @@ -38,7 +34,7 @@ class GetSortCriteriaPerAccountTest {
// Assert
val expected = mapOf(null to defaultSortCriteria)
assertThat(result).isEqualTo(expected)
verifySuspend { getDefaultSortCriteria() }
getDefaultSortCriteria.assertWasCalledOnce()
}

@Test
Expand All @@ -60,9 +56,7 @@ class GetSortCriteriaPerAccountTest {
sortAscending = mapOf(DomainSortType.SORT_DATE to false),
)

val getDefaultSortCriteria = spy(
obj = DomainContract.UseCase.GetDefaultSortCriteria { defaultSortCriteria },
)
val getDefaultSortCriteria = FakeGetDefaultSortCriteria(defaultSortCriteria)
val useCase = createTestSubject(
accounts = listOf(account1, account2),
getDefaultSortCriteria = getDefaultSortCriteria,
Expand All @@ -78,7 +72,7 @@ class GetSortCriteriaPerAccountTest {
accountId2 to SortCriteria(primary = SortType.DateDesc),
)
assertThat(result).isEqualTo(expected)
verifySuspend { getDefaultSortCriteria() }
getDefaultSortCriteria.assertWasCalledOnce()
}

@Test
Expand All @@ -92,9 +86,7 @@ class GetSortCriteriaPerAccountTest {
sortAscending = emptyMap(),
)

val getDefaultSortCriteria = spy(
obj = DomainContract.UseCase.GetDefaultSortCriteria { defaultSortCriteria },
)
val getDefaultSortCriteria = FakeGetDefaultSortCriteria(defaultSortCriteria)
val useCase = createTestSubject(
accounts = listOf(account),
getDefaultSortCriteria = getDefaultSortCriteria,
Expand All @@ -109,7 +101,7 @@ class GetSortCriteriaPerAccountTest {
accountId to SortCriteria(primary = SortType.ArrivalDesc),
)
assertThat(result).isEqualTo(expected)
verifySuspend { getDefaultSortCriteria() }
getDefaultSortCriteria.assertWasCalledOnce()
}

private fun createTestSubject(
Expand All @@ -120,4 +112,19 @@ class GetSortCriteriaPerAccountTest {
getDefaultSortCriteria = getDefaultSortCriteria,
ioDispatcher = UnconfinedTestDispatcher(),
)

private class FakeGetDefaultSortCriteria(
private val sortCriteria: SortCriteria,
) : DomainContract.UseCase.GetDefaultSortCriteria {
private var callCount = 0

override suspend fun invoke(): SortCriteria {
callCount += 1
return sortCriteria
}

fun assertWasCalledOnce() {
assertThat(callCount).isEqualTo(1)
}
}
}
Loading
Loading