Skip to content

Commit

Permalink
feat: mokkery
Browse files Browse the repository at this point in the history
  • Loading branch information
alvr committed Feb 28, 2024
1 parent 62ba35c commit bdc1e73
Show file tree
Hide file tree
Showing 36 changed files with 491 additions and 490 deletions.
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

0 comments on commit bdc1e73

Please sign in to comment.