Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mokkery #789

Merged
merged 1 commit into from
Feb 28, 2024
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
2 changes: 1 addition & 1 deletion build-logic/katana-convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies {
implementation(libs.gradle.kotlin)
implementation(libs.gradle.kover)
implementation(libs.gradle.ksp)
implementation(libs.gradle.mockmp)
implementation(libs.gradle.mokkery)
implementation(libs.gradle.sentry)
implementation(libs.kotlinpoet)
implementation(libs.yamlbeans)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.kodein.mock.gradle.MocKMPGradlePlugin

internal class KatanaMultiplatformCorePlugin : Plugin<Project> {

Expand All @@ -26,14 +25,13 @@ internal class KatanaMultiplatformCorePlugin : Plugin<Project> {
apply(plugin = "com.google.devtools.ksp")
apply(plugin = "io.kotest.multiplatform")
apply(plugin = "org.jetbrains.kotlinx.kover")
apply(plugin = "org.kodein.mock.mockmp")
apply(plugin = "dev.mokkery")

with(extensions) {
create<KatanaMultiplatformCoreExtension>(KATANA_MULTIPLATFORM_EXTENSION)

commonExtensions()
configure<KotlinMultiplatformExtension> { configureMultiplatform() }
configure<MocKMPGradlePlugin.Extension> { installWorkaround() }
}

with(tasks) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.getting
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.kodein.mock.gradle.MocKMPGradlePlugin

internal abstract class KatanaMultiplatformMobileBasePlugin(
private val androidPlugin: String,
Expand All @@ -37,7 +36,7 @@ internal abstract class KatanaMultiplatformMobileBasePlugin(
apply(plugin = "com.google.devtools.ksp")
apply(plugin = "io.kotest.multiplatform")
apply(plugin = "org.jetbrains.kotlinx.kover")
apply(plugin = "org.kodein.mock.mockmp")
apply(plugin = "dev.mokkery")

with(extensions) {
create<KatanaMultiplatformMobileExtension>(KATANA_MULTIPLATFORM_EXTENSION)
Expand All @@ -46,7 +45,6 @@ internal abstract class KatanaMultiplatformMobileBasePlugin(
configureAndroid()
configure<BuildConfigExtension> { configureBuildConfig() }
configure<KotlinMultiplatformExtension> { configureMultiplatform() }
configure<MocKMPGradlePlugin.Extension> { installWorkaround() }
}

tasks.commonTasks()
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ plugins {
alias(libs.plugins.kotlin) apply false
alias(libs.plugins.kover) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.mockmp) apply false
alias(libs.plugins.mokkery) apply false
alias(libs.plugins.sentry) apply false
}
1 change: 0 additions & 1 deletion common/tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ katanaMultiplatform {
implementation(libs.arrow)
implementation(libs.koin)
implementation(libs.koin.test.get().toString()) { exclude(group = "junit", module = "junit") }
implementation(libs.mockmp)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package dev.alvr.katana.data.preferences.base.serializers

import androidx.datastore.core.CorruptionException
import dev.alvr.katana.common.tests.invoke
import dev.alvr.katana.data.preferences.base.encrypt.MockPreferencesEncrypt
import dev.alvr.katana.data.preferences.base.encrypt.PreferencesEncrypt
import dev.mokkery.answering.calls
import dev.mokkery.answering.returns
import dev.mokkery.every
import dev.mokkery.matcher.any
import dev.mokkery.mock
import dev.mokkery.verify
import io.kotest.assertions.throwables.shouldThrowExactlyUnit
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
Expand All @@ -15,13 +19,9 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import okio.Buffer
import org.kodein.mock.Mocker
import org.kodein.mock.UsesMocks

@UsesMocks(PreferencesEncrypt::class)
internal class EncryptedPreferencesSerializerTest : FreeSpec() {
private val mocker = Mocker()
private val encrypt = MockPreferencesEncrypt(mocker)
private val encrypt = mock<PreferencesEncrypt>()

private val serializer = EncryptedPreferencesSerializer(
encrypt = encrypt,
Expand All @@ -33,73 +33,69 @@ internal class EncryptedPreferencesSerializerTest : FreeSpec() {
"writing and reading from the buffer" {
val source = Buffer()

mocker.every { encrypt.encrypt(isAny()) } runs { it.first() as ByteArray }
mocker.every { encrypt.decrypt(isAny()) } runs { it.first() as ByteArray }
every { encrypt.encrypt(any()) } calls { it.arg(0) as ByteArray }
every { encrypt.decrypt(any()) } calls { it.arg(0) as ByteArray }

serializer.writeTo(Color(0x123456), source)
serializer.readFrom(source) shouldBe Color(0x123456)

mocker.verify {
encrypt.encrypt(isAny())
encrypt.decrypt(isAny())
verify {
encrypt.encrypt(any())
encrypt.decrypt(any())
}
}

"reading from an empty buffer" {
val source = Buffer()

mocker.every { encrypt.encrypt(isAny()) } returns byteArrayOf()
mocker.every { encrypt.decrypt(isAny()) } returns byteArrayOf()
every { encrypt.encrypt(any()) } returns byteArrayOf()
every { encrypt.decrypt(any()) } returns byteArrayOf()

shouldThrowExactlyUnit<CorruptionException> {
serializer.readFrom(source)
}.message shouldBe "secured read"

mocker.verify { encrypt.decrypt(isAny()) }
verify { encrypt.decrypt(any()) }
}

"reading an invalid data" {
val source = Buffer()
source.write(byteArrayOf())

mocker.every { encrypt.encrypt(isAny()) } returns byteArrayOf()
mocker.every { encrypt.decrypt(isAny()) } returns byteArrayOf()
every { encrypt.encrypt(any()) } returns byteArrayOf()
every { encrypt.decrypt(any()) } returns byteArrayOf()

shouldThrowExactlyUnit<CorruptionException> {
serializer.readFrom(source)
}.message shouldBe "secured read"

mocker.verify { encrypt.decrypt(isAny()) }
verify { encrypt.decrypt(any()) }
}

"error when writing secure data" {
val source = Buffer()

mocker.every { encrypt.encrypt(isAny()) } runs { error("oops") }
mocker.every { encrypt.decrypt(isAny()) } returns byteArrayOf()
every { encrypt.encrypt(any()) } calls { error("oops") }
every { encrypt.decrypt(any()) } returns byteArrayOf()

shouldThrowExactlyUnit<CorruptionException> {
serializer.writeTo(Color(0x123456), source)
}.message shouldBe "secured write"

mocker.verify {
threw<IllegalStateException> { encrypt.encrypt(isAny()) }
}
verify { encrypt.encrypt(any()) }
}

"error when reading secure data" {
val source = Buffer()

mocker.every { encrypt.encrypt(isAny()) } returns byteArrayOf()
mocker.every { encrypt.decrypt(isAny()) } runs { error("oops") }
every { encrypt.encrypt(any()) } returns byteArrayOf()
every { encrypt.decrypt(any()) } calls { error("oops") }

shouldThrowExactlyUnit<CorruptionException> {
serializer.readFrom(source)
}.message shouldBe "secured read"

mocker.verify {
threw<IllegalStateException> { encrypt.decrypt(isAny()) }
}
verify { encrypt.decrypt(any()) }
}
}

Expand All @@ -120,6 +116,4 @@ internal class EncryptedPreferencesSerializerTest : FreeSpec() {
return Color(string.toInt(16))
}
}

override fun extensions() = listOf(mocker())
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.alvr.katana.data.preferences.session.mocks

import dev.alvr.katana.data.preferences.session.models.Session
import dev.alvr.katana.domain.session.models.AnilistToken
import io.kotest.property.Arb
import io.kotest.property.arbitrary.boolean
import io.kotest.property.arbitrary.next

internal val anilistTokenMock = AnilistToken("TOKEN")

internal val sessionMock = Session(
anilistToken = anilistTokenMock,
isSessionActive = Arb.boolean().next(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,33 @@ import arrow.core.left
import arrow.core.none
import arrow.core.right
import arrow.core.some
import dev.alvr.katana.common.tests.invoke
import dev.alvr.katana.common.tests.shouldBeLeft
import dev.alvr.katana.common.tests.shouldBeNone
import dev.alvr.katana.common.tests.shouldBeRight
import dev.alvr.katana.common.tests.shouldBeSome
import dev.alvr.katana.data.preferences.session.sources.MockSessionLocalSource
import dev.alvr.katana.data.preferences.session.mocks.anilistTokenMock
import dev.alvr.katana.data.preferences.session.sources.SessionLocalSource
import dev.alvr.katana.domain.session.failures.SessionFailure
import dev.alvr.katana.domain.session.models.AnilistToken
import dev.alvr.katana.domain.session.repositories.SessionRepository
import dev.mokkery.answering.returns
import dev.mokkery.every
import dev.mokkery.everySuspend
import dev.mokkery.mock
import dev.mokkery.verify
import dev.mokkery.verifySuspend
import io.kotest.core.spec.style.FreeSpec
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.flow.flowOf
import org.kodein.mock.Mocker
import org.kodein.mock.UsesMocks

@UsesMocks(SessionLocalSource::class)
internal class SessionRepositoryTest : FreeSpec() {
private val mocker = Mocker()
private val source: SessionLocalSource = MockSessionLocalSource(mocker)
private val repo: SessionRepository = SessionRepositoryImpl(source)
private val source: SessionLocalSource = mock<SessionLocalSource>()

private val anilistToken = AnilistToken("TOKEN")
private val repo: SessionRepository = SessionRepositoryImpl(source)

init {
"observing" - {
"observing if the session is active" {
mocker.every { source.sessionActive } returns flowOf(
every { source.sessionActive } returns flowOf(
true.right(),
true.right(),
false.right(),
Expand All @@ -45,80 +44,78 @@ internal class SessionRepositoryTest : FreeSpec() {
awaitComplete()
}

mocker.verify { source.sessionActive }
verify { source.sessionActive }
}
}

"getting" - {
"getting a non-existing token" {
mocker.everySuspending { source.getAnilistToken() } returns none()
everySuspend { source.getAnilistToken() } returns none()
repo.getAnilistToken().shouldBeNone()
mocker.verifyWithSuspend { source.getAnilistToken() }
verifySuspend { source.getAnilistToken() }
}

"getting an existing token" {
mocker.everySuspending { source.getAnilistToken() } returns anilistToken.some()
repo.getAnilistToken().shouldBeSome(anilistToken)
mocker.verifyWithSuspend { source.getAnilistToken() }
everySuspend { source.getAnilistToken() } returns anilistTokenMock.some()
repo.getAnilistToken().shouldBeSome(anilistTokenMock)
verifySuspend { source.getAnilistToken() }
}
}

"saving" - {
"saving a session without error" {
mocker.everySuspending { source.saveSession(isAny()) } returns Unit.right()
repo.saveSession(anilistToken).shouldBeRight(Unit)
mocker.verifyWithSuspend { source.saveSession(anilistToken) }
everySuspend { source.saveSession(anilistTokenMock) } returns Unit.right()
repo.saveSession(anilistTokenMock).shouldBeRight(Unit)
verifySuspend { source.saveSession(anilistTokenMock) }
}

"saving a session with an error" {
mocker.everySuspending { source.saveSession(isAny()) } returns SessionFailure.SavingSession.left()
repo.saveSession(anilistToken).shouldBeLeft(SessionFailure.SavingSession)
mocker.verifyWithSuspend { source.saveSession(anilistToken) }
everySuspend { source.saveSession(anilistTokenMock) } returns SessionFailure.SavingSession.left()
repo.saveSession(anilistTokenMock).shouldBeLeft(SessionFailure.SavingSession)
verifySuspend { source.saveSession(anilistTokenMock) }
}
}

"deleting" - {
"deleting a session without error" {
mocker.everySuspending { source.deleteAnilistToken() } returns Unit.right()
everySuspend { source.deleteAnilistToken() } returns Unit.right()
repo.deleteAnilistToken().shouldBeRight(Unit)
mocker.verifyWithSuspend { source.deleteAnilistToken() }
verifySuspend { source.deleteAnilistToken() }
}

"deleting a session with an error" {
mocker.everySuspending { source.deleteAnilistToken() } returns SessionFailure.DeletingToken.left()
everySuspend { source.deleteAnilistToken() } returns SessionFailure.DeletingToken.left()
repo.deleteAnilistToken().shouldBeLeft(SessionFailure.DeletingToken)
mocker.verifyWithSuspend { source.deleteAnilistToken() }
verifySuspend { source.deleteAnilistToken() }
}
}

"clearing" - {
"clearing a session without error" {
mocker.everySuspending { source.clearActiveSession() } returns Unit.right()
everySuspend { source.clearActiveSession() } returns Unit.right()
repo.clearActiveSession().shouldBeRight(Unit)
mocker.verifyWithSuspend { source.clearActiveSession() }
verifySuspend { source.clearActiveSession() }
}

"clearing a session with an error" {
mocker.everySuspending { source.clearActiveSession() } returns SessionFailure.ClearingSession.left()
everySuspend { source.clearActiveSession() } returns SessionFailure.ClearingSession.left()
repo.clearActiveSession().shouldBeLeft(SessionFailure.ClearingSession)
mocker.verifyWithSuspend { source.clearActiveSession() }
verifySuspend { source.clearActiveSession() }
}
}

"logging out" - {
"logging out without error" {
mocker.everySuspending { source.logout() } returns Unit.right()
everySuspend { source.logout() } returns Unit.right()
repo.logout().shouldBeRight(Unit)
mocker.verifyWithSuspend { source.logout() }
verifySuspend { source.logout() }
}

"logging out with an error" {
mocker.everySuspending { source.logout() } returns SessionFailure.LoggingOut.left()
everySuspend { source.logout() } returns SessionFailure.LoggingOut.left()
repo.logout().shouldBeLeft(SessionFailure.LoggingOut)
mocker.verifyWithSuspend { source.logout() }
verifySuspend { source.logout() }
}
}
}

override fun extensions() = listOf(mocker())
}
Loading
Loading