diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed84fad..68dc4f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,6 +35,7 @@ koin-compose = { module = "io.insert-koin:koin-compose" } koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" } koin-core = { module = "io.insert-koin:koin-core" } koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel" } +koin-test = { module = "io.insert-koin:koin-test" } kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinxBrowser" } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 5bc6db6..8d9db5f 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -92,6 +92,11 @@ kotlin { commonTest.dependencies { implementation(libs.kotlin.test) } + jvmTest.dependencies { + // Koin configuration validation + implementation(project.dependencies.platform(libs.koin.bom)) + implementation(libs.koin.test) + } jsMain.dependencies { implementation(libs.wrappers.browser) } diff --git a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/App.kt b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/App.kt index 5a38fd2..40e4716 100644 --- a/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/App.kt +++ b/shared/src/commonMain/kotlin/fr/ajaury/gwenedeg/App.kt @@ -18,19 +18,25 @@ import fr.ajaury.gwenedeg.subtitle.di.subtitleModule import fr.ajaury.gwenedeg.theme.GwenedegTheme import org.koin.compose.KoinApplication import org.koin.dsl.koinConfiguration +import org.koin.dsl.module + + +val appModule = module { + includes( + loggingModule, + sharedModule, + audioPlayerModule, + coroutinesModule, + subtitleModule + ) +} @Composable @Preview fun App() { KoinApplication( configuration = koinConfiguration { - modules( - sharedModule, - audioPlayerModule, - coroutinesModule, - loggingModule, - subtitleModule - ) + modules(appModule) }, ) { val serializer = SnapshotStateListSerializer() diff --git a/shared/src/jvmTest/kotlin/fr/ajaury/gwenedeg/di/KoinModulesTest.kt b/shared/src/jvmTest/kotlin/fr/ajaury/gwenedeg/di/KoinModulesTest.kt new file mode 100644 index 0000000..aa4c69c --- /dev/null +++ b/shared/src/jvmTest/kotlin/fr/ajaury/gwenedeg/di/KoinModulesTest.kt @@ -0,0 +1,23 @@ +package fr.ajaury.gwenedeg.di + +import fr.ajaury.gwenedeg.appModule +import fr.ajaury.gwenedeg.player.di.audioPlayerModule +import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.test.verify.verify +import kotlin.test.Test + +/** + * Statically validates the Koin graph: [verify] inspects every definition's constructor and fails + * the build if a dependency cannot be resolved from the declared modules. Acts as a compile-/build- + * time safety net so DI misconfiguration surfaces in CI rather than at runtime. + * + * Uses the JVM platform bindings (the `actual` [audioPlayerModule]); the shared graph is identical + * across targets, so verifying it here covers all of them. + */ +class KoinModulesTest { + @OptIn(KoinExperimentalAPI::class) + @Test + fun koinConfigurationIsValid() { + appModule.verify() + } +}