test(arch): keep Room and query strings inside the data layer
The seam is only worth having if something checks it. This fails the build if androidx.room, an entity or a query string appears outside data/, rather than leaving it to review to notice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package de.jeanlucmakiola.clockula
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* docs/PLAN.md §0's boundary, made mechanical: the app talks to repository
|
||||
* interfaces and plain-Kotlin models, and only `data/` knows Room exists.
|
||||
* A grep is a weaker tool than a compiler, but it is the one that fails the
|
||||
* build when someone reaches through the seam.
|
||||
*/
|
||||
class ArchitectureRulesTest {
|
||||
|
||||
private val mainSourceRoot: File = listOf(File("src/main/java"), File("app/src/main/java"))
|
||||
.firstOrNull { it.isDirectory }
|
||||
?: error("Could not resolve the main source root from ${File("").absolutePath}")
|
||||
|
||||
private val kotlinSources: List<File> =
|
||||
mainSourceRoot.walkTopDown().filter { it.isFile && it.extension == "kt" }.toList()
|
||||
|
||||
private val outsideDataLayer: List<File> =
|
||||
kotlinSources.filterNot { it.invariantSeparatorsPath.contains("de/jeanlucmakiola/clockula/data/") }
|
||||
|
||||
private fun List<File>.containing(vararg needles: String): List<String> =
|
||||
filter { file -> needles.any { it in file.readText() } }.map { it.invariantSeparatorsPath }
|
||||
|
||||
@Test
|
||||
fun `Room does not leak above the data layer`() {
|
||||
val offenders = outsideDataLayer.containing("androidx.room")
|
||||
|
||||
assertThat(offenders).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no Dao or Query annotation appears outside the data layer`() {
|
||||
val offenders = outsideDataLayer.containing("@Dao", "@Query")
|
||||
|
||||
assertThat(offenders).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no entity or dao type is named outside the data layer`() {
|
||||
val offenders = outsideDataLayer.containing(
|
||||
"AlarmEntity",
|
||||
"TimerEntity",
|
||||
"WorldClockEntity",
|
||||
"LapEntity",
|
||||
"AlarmDao",
|
||||
"TimerDao",
|
||||
"WorldClockDao",
|
||||
"LapDao",
|
||||
"ClockulaDatabase",
|
||||
)
|
||||
|
||||
assertThat(offenders).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the domain is free of Android types`() {
|
||||
val domain = kotlinSources
|
||||
.filter { it.invariantSeparatorsPath.contains("de/jeanlucmakiola/clockula/domain/") }
|
||||
val offenders = domain.filter { file ->
|
||||
file.readLines().any { it.startsWith("import android.") || it.startsWith("import androidx.") }
|
||||
}.map { it.invariantSeparatorsPath }
|
||||
|
||||
assertThat(offenders).isEmpty()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user