Skip to content

Commit

Permalink
test: 优化API请求结果反序列化以及校验的测试完善
Browse files Browse the repository at this point in the history
  • Loading branch information
ForteScarlet committed Jan 30, 2024
1 parent 0b85a42 commit 85ce507
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/qodana_code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2023.2
uses: JetBrains/qodana-action@v2023.3.1
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref =
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" }
ktor-client-js = { group = "io.ktor", name = "ktor-client-js", version.ref = "ktor" }
ktor-client-java = { group = "io.ktor", name = "ktor-client-java", version.ref = "ktor" }
ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock", version.ref = "ktor" }

# for linuxX64, macosX64, macosArm64, mingwX64
# see https://ktor.io/docs/http-client-engines.html#curl
Expand Down
2 changes: 2 additions & 0 deletions simbot-component-qq-guild-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ kotlin {
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
// https://ktor.io/docs/http-client-testing.html
implementation(libs.ktor.client.mock)
}

jvmMain.dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,19 @@ public suspend fun <R : Any> QQGuildApi<R>.requestData(

checkStatus(text, QQGuild.DefaultJson, resp.status, resp)

return decodeResponse(decoder, text)
return try {
decodeResponse(decoder, text)
} catch (serEx: SerializationException) {
val status = resp.status
// 反序列化异常
throw QQGuildResultSerializationException(
status.value,
status.description,
"Response(status=${status.value}) deserialization failed: ${serEx.message}"
).also {
it.initCause0(serEx)
}
}
}


Expand All @@ -213,12 +225,16 @@ internal fun checkStatus(
resp: HttpResponse,
) {
// 如果出现了序列化异常,抛出 QQGuildResultSerializationException
fun <T> decodeFromStringWithCatch(deserializer: DeserializationStrategy<T>, string: String): T {
fun <T> decodeFromStringWithCatch(deserializer: DeserializationStrategy<T>): T {
return try {
decoder.decodeFromString(deserializer, remainingText)
} catch (serEx: SerializationException) {
// 反序列化异常
throw QQGuildResultSerializationException(status.value, status.description, "Response(status=${status.value}) deserialization failed: ${serEx.message}").also {
throw QQGuildResultSerializationException(
status.value,
status.description,
"Response(status=${status.value}) deserialization failed: ${serEx.message}"
).also {
it.initCause0(serEx)
}
}
Expand All @@ -227,15 +243,15 @@ internal fun checkStatus(

// TODO 201,202 异步操作成功,虽然说成功,但是会返回一个 error body,需要特殊处理
if (!status.isSuccess()) {
val info = decodeFromStringWithCatch(ErrInfo.serializer(), remainingText)
val info = decodeFromStringWithCatch(ErrInfo.serializer())

// throw err
throw QQGuildApiException(info, status.value, status.description)
}

// 202 消息审核
if (status == HttpStatusCode.Accepted) {
val info = decodeFromStringWithCatch(ErrInfo.serializer(), remainingText)
val info = decodeFromStringWithCatch(ErrInfo.serializer())
// maybe audited
if (MessageAuditedException.isAuditResultCode(info.code)) {
throw MessageAuditedException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package test

import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.utils.io.*
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.SerializationException
import love.forte.simbot.qguild.QQGuild
import love.forte.simbot.qguild.QQGuildResultSerializationException
import love.forte.simbot.qguild.api.GatewayApis
import love.forte.simbot.qguild.api.channel.GetChannelApi
import love.forte.simbot.qguild.api.checkStatus
import love.forte.simbot.qguild.api.request
import love.forte.simbot.qguild.api.requestData
import kotlin.test.Test
import kotlin.test.assertFails
import kotlin.test.assertIs


/**
*
* @author ForteScarlet
*/
class ApiResultSerializationCheckTests {

@Test
fun statusDeserializationHtmlBadResultTest() = runTest {
val client = HttpClient(
MockEngine.invoke { // request ->
respond(
content = ByteReadChannel("""<html></html>"""),
status = HttpStatusCode.BadRequest,
// headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}) {

}

val resp = GatewayApis.Normal.request(client, "test")
val ex = assertFails { checkStatus(resp.bodyAsText(), QQGuild.DefaultJson, resp.status, resp) }
assertIs<QQGuildResultSerializationException>(ex)
assertIs<SerializationException>(ex.cause ?: ex.suppressedExceptions.firstOrNull())
}

@Test
fun statusDeserializationHtmlOKResultTest() = runTest {
val client = HttpClient(
MockEngine.invoke { // request ->
respond(
content = ByteReadChannel("""<html></html>"""),
status = HttpStatusCode.OK,
)
}) {

}

val ex = assertFails { GetChannelApi.create("test").requestData(client, "test") }
assertIs<QQGuildResultSerializationException>(ex)
assertIs<SerializationException>(ex.cause ?: ex.suppressedExceptions.firstOrNull())
}

}

0 comments on commit 85ce507

Please sign in to comment.