feat(api): add integration tests for Shiori API

This commit is contained in:
Harsh Shandilya 2023-10-24 23:55:45 +05:30
parent f39bf0b043
commit 3663ca1ec1
No known key found for this signature in database
3 changed files with 176 additions and 0 deletions

View file

@ -27,7 +27,9 @@ dependencies {
implementation(libs.jsoup) implementation(libs.jsoup)
testImplementation(testFixtures(libs.eithernet)) testImplementation(testFixtures(libs.eithernet))
testImplementation(libs.testcontainers)
testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.kotlinx.serialization.json) testImplementation(libs.kotlinx.serialization.json)
testImplementation(libs.retrofit.kotlinxSerializationConverter)
addTestDependencies(project) addTestDependencies(project)
} }

View file

@ -0,0 +1,173 @@
/*
* Copyright © 2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.api
import com.google.common.truth.Truth.assertThat
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dev.msfjarvis.claw.model.shiori.AuthRequest
import dev.msfjarvis.claw.model.shiori.AuthResponse
import dev.msfjarvis.claw.model.shiori.Bookmark
import dev.msfjarvis.claw.model.shiori.BookmarkRequest
import dev.msfjarvis.claw.model.shiori.EditedBookmark
import dev.msfjarvis.claw.model.shiori.Tag
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import okhttp3.MediaType
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.testcontainers.containers.GenericContainer
import retrofit2.Retrofit
import retrofit2.create
class ShioriApiTest {
private lateinit var credentials: AuthResponse
@BeforeEach
fun setUp() {
runBlocking { credentials = api.login(AuthRequest(USER, PASSWORD)) }
}
@AfterEach
fun tearDown() {
runBlocking {
val ids = api.getBookmarks(credentials.session).bookmarks.map(Bookmark::id)
if (ids.isNotEmpty()) {
api.deleteBookmark(credentials.session, ids)
}
api.logout(credentials.session)
}
}
@Test
fun getBookmarks() = runTest {
val response = api.getBookmarks(credentials.session)
assertThat(response.page).isEqualTo(1)
assertThat(response.bookmarks).isEmpty()
}
@Test
fun addBookmark() = runTest {
val response =
api.addBookmark(
credentials.session,
BookmarkRequest(
"https://example.com",
false,
0,
emptyList(),
"Example Domain",
"""
This domain is for use in illustrative examples in documents.
You may use this domain in literature without prior coordination or asking for permission.
"""
.trimIndent(),
)
)
assertThat(response.url).isEqualTo("https://example.com")
assertThat(response.title).isEqualTo("Example Domain")
assertThat(response.excerpt)
.isEqualTo(
"""
This domain is for use in illustrative examples in documents.
You may use this domain in literature without prior coordination or asking for permission.
"""
.trimIndent()
)
assertThat(response.id).isAtLeast(0)
}
@Test
@Disabled("Server returns HTTP 500, needs debugging")
fun editBookmark() = runTest {
val response =
api.addBookmark(
credentials.session,
BookmarkRequest(
"https://example.com",
false,
0,
emptyList(),
"Example Domain",
"""
This domain is for use in illustrative examples in documents.
You may use this domain in literature without prior coordination or asking for permission.
"""
.trimIndent(),
)
)
assertThat(response.tags).isEmpty()
val newBookmark =
EditedBookmark(
id = response.id,
tags = listOf(Tag("examples")),
)
val edited = api.editBookmark(credentials.session, newBookmark)
assertThat(edited.tags).isNotEmpty()
assertThat(edited.tags).containsExactly(Tag("examples"))
}
@Test
fun deleteBookmark() = runTest {
val response =
api.addBookmark(
credentials.session,
BookmarkRequest(
"https://example.com",
false,
0,
emptyList(),
"Example Domain",
"""
This domain is for use in illustrative examples in documents.
You may use this domain in literature without prior coordination or asking for permission.
"""
.trimIndent(),
)
)
val count = api.deleteBookmark(credentials.session, listOf(response.id))
assertThat(count).isEqualTo(1)
}
companion object {
// Default settings for the container
private const val USER = "shiori"
private const val PASSWORD = "gopher"
private val container =
GenericContainer("ghcr.io/go-shiori/shiori:v1.5.5").withExposedPorts(8080)
private val json = Json { ignoreUnknownKeys = true }
// The mapped port can only be obtained after the container has started, so this has to be lazy.
private val api by
lazy(LazyThreadSafetyMode.NONE) {
Retrofit.Builder()
.baseUrl("http://${container.host}:${container.firstMappedPort}")
.addConverterFactory(json.asConverterFactory(MediaType.get("application/json")))
.build()
.create<ShioriApi>()
}
@JvmStatic
@BeforeAll
fun setupContainer() {
container.start()
}
@JvmStatic
@AfterAll
fun destroyContainer() {
container.stop()
}
}
}

View file

@ -97,6 +97,7 @@ sqldelight-jvmDriver = { module = "app.cash.sqldelight:sqlite-driver", version.r
sqldelight-primitiveAdapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "sqldelight" } sqldelight-primitiveAdapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "sqldelight" }
sqlite-android = "com.github.requery:sqlite-android:3.43.0" sqlite-android = "com.github.requery:sqlite-android:3.43.0"
swipe = "me.saket.swipe:swipe:1.3.0-SNAPSHOT" swipe = "me.saket.swipe:swipe:1.3.0-SNAPSHOT"
testcontainers = "org.testcontainers:testcontainers:1.19.1"
truth = "com.google.truth:truth:1.1.5" truth = "com.google.truth:truth:1.1.5"
unfurl = "me.saket.unfurl:unfurl:1.7.0" unfurl = "me.saket.unfurl:unfurl:1.7.0"