diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 844201e1..c937faf7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -12,6 +12,6 @@ dependencies { testImplementation(libs.kotlinx.serialization.json) testImplementation(libs.kotlin.coroutines.core) testImplementation(kotlin("test-junit")) - testImplementation(libs.retrofit.kotlinxSerializationConverter) { isTransitive = false } - testImplementation(libs.testing.mockWebServer) + testImplementation(libs.retrofit.kotlinxSerializationConverter) + testImplementation(libs.retrofit.mock) } diff --git a/api/src/test/kotlin/dev/msfjarvis/claw/api/ApiTest.kt b/api/src/test/kotlin/dev/msfjarvis/claw/api/ApiTest.kt new file mode 100644 index 00000000..29d532c8 --- /dev/null +++ b/api/src/test/kotlin/dev/msfjarvis/claw/api/ApiTest.kt @@ -0,0 +1,32 @@ +package dev.msfjarvis.claw.api + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.runBlocking +import retrofit2.Retrofit +import retrofit2.mock.MockRetrofit +import retrofit2.mock.create + +class ApiTest { + private val retrofit = Retrofit.Builder().baseUrl(LobstersApi.BASE_URL).build() + private val api = MockRetrofit.Builder(retrofit).build().create().let(::FakeApi) + + @Test + fun `api gets correct number of items`() = runBlocking { + val posts = api.getHottestPosts(1) + assertEquals(25, posts.size) + } + + @Test + fun `posts with no urls`() = runBlocking { + val posts = api.getHottestPosts(1) + val commentsOnlyPosts = posts.asSequence().filter { it.url.isEmpty() }.toSet() + assertEquals(2, commentsOnlyPosts.size) + } + + @Test + fun `post details with comments`() = runBlocking { + val postDetails = api.getPostDetails("d9ucpe") + assertEquals(7, postDetails.comments.size) + } +} diff --git a/api/src/test/kotlin/dev/msfjarvis/claw/api/FakeApi.kt b/api/src/test/kotlin/dev/msfjarvis/claw/api/FakeApi.kt new file mode 100644 index 00000000..29e50900 --- /dev/null +++ b/api/src/test/kotlin/dev/msfjarvis/claw/api/FakeApi.kt @@ -0,0 +1,27 @@ +package dev.msfjarvis.claw.api + +import dev.msfjarvis.claw.model.LobstersPost +import dev.msfjarvis.claw.model.LobstersPostDetails +import dev.msfjarvis.claw.util.TestUtils.getJson +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import retrofit2.mock.BehaviorDelegate + +class FakeApi(private val delegate: BehaviorDelegate) : LobstersApi { + private val json = Json { ignoreUnknownKeys = true } + private val hottest: List = json.decodeFromString(getJson("hottest.json")) + private val postDetails: LobstersPostDetails = + json.decodeFromString(getJson("post_details_d9ucpe.json")) + + override suspend fun getHottestPosts(page: Int): List { + return delegate.returningResponse(hottest).getHottestPosts(page) + } + + override suspend fun getNewestPosts(page: Int): List { + TODO("Not yet implemented") + } + + override suspend fun getPostDetails(postId: String): LobstersPostDetails { + return delegate.returningResponse(postDetails).getPostDetails(postId) + } +} diff --git a/api/src/test/kotlin/dev/msfjarvis/claw/api/LobstersApiTest.kt b/api/src/test/kotlin/dev/msfjarvis/claw/api/LobstersApiTest.kt deleted file mode 100644 index ead44f1d..00000000 --- a/api/src/test/kotlin/dev/msfjarvis/claw/api/LobstersApiTest.kt +++ /dev/null @@ -1,84 +0,0 @@ -package dev.msfjarvis.claw.api - -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory -import dev.msfjarvis.claw.util.TestUtils -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.fail -import kotlinx.coroutines.runBlocking -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.Json -import mockwebserver3.Dispatcher -import mockwebserver3.MockResponse -import mockwebserver3.MockWebServer -import mockwebserver3.RecordedRequest -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import org.junit.AfterClass -import org.junit.BeforeClass -import retrofit2.Retrofit -import retrofit2.create - -@OptIn(ExperimentalSerializationApi::class) -class LobstersApiTest { - - companion object { - private val contentType = "application/json".toMediaType() - private val webServer = MockWebServer() - private val okHttp = OkHttpClient.Builder().build() - private val json = Json { ignoreUnknownKeys = true } - private val retrofit = - Retrofit.Builder() - .client(okHttp) - .baseUrl("http://localhost:8080/") - .addConverterFactory(json.asConverterFactory(contentType)) - .build() - private val apiClient = retrofit.create() - - @JvmStatic - @BeforeClass - fun setUp() { - webServer.start(8080) - webServer.dispatcher = - object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse { - val path = requireNotNull(request.path) - return when { - path.startsWith("/hottest") -> - MockResponse().setBody(TestUtils.getJson("hottest.json")).setResponseCode(200) - path.startsWith("/s/") -> - MockResponse() - .setBody(TestUtils.getJson("post_details_d9ucpe.json")) - .setResponseCode(200) - else -> fail("'$path' unexpected") - } - } - } - } - - @JvmStatic - @AfterClass - fun tearDown() { - webServer.shutdown() - } - } - - @Test - fun `api gets correct number of items`() = runBlocking { - val posts = apiClient.getHottestPosts(1) - assertEquals(25, posts.size) - } - - @Test - fun `posts with no urls`() = runBlocking { - val posts = apiClient.getHottestPosts(1) - val commentsOnlyPosts = posts.asSequence().filter { it.url.isEmpty() }.toSet() - assertEquals(2, commentsOnlyPosts.size) - } - - @Test - fun `post details with comments`() = runBlocking { - val postDetails = apiClient.getPostDetails("d9ucpe") - assertEquals(7, postDetails.comments.size) - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 66faac24..fe7d2ce4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,7 @@ dagger = "2.41" hilt = "1.0.0" kotlin = "1.6.10" material_motion = "0.8.4" +retrofit = "2.9.0" richtext = "0.11.0" serialization = "1.3.2" sqldelight = "2.0.0-alpha02" @@ -52,13 +53,13 @@ multiplatform-paging = "io.github.kuuuurt:multiplatform-paging:0.4.7" napier = "io.github.aakira:napier:2.5.0" r8 = "com.android.tools:r8:3.3.28" retrofit-kotlinxSerializationConverter = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0" -retrofit-lib = "com.squareup.retrofit2:retrofit:2.9.0" +retrofit-lib = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } +retrofit-mock = { module = "com.squareup.retrofit2:retrofit-mock", version.ref = "retrofit" } svg-transcoder = "org.pushing-pixels:aurora-tools-svg-transcoder-gradle-plugin:1.1.0" sqldelight-androidDriver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-extensions-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" } sqldelight-jvmDriver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-primitiveAdapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "sqldelight" } -testing-mockWebServer = "com.squareup.okhttp3:mockwebserver3-junit4:5.0.0-alpha.7" [plugins] compose = "org.jetbrains.compose:1.1.1"