Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ subprojects {
"testImplementation"("org.slf4j:slf4j-simple:2.0.4")
"testImplementation"("io.github.artsok:rerunner-jupiter:2.1.6")
"implementation"("com.goncalossilva:murmurhash:0.4.0")
"testImplementation"("io.mockk:mockk:1.13.4")
}

publishing {
Expand Down
19 changes: 11 additions & 8 deletions core/src/main/kotlin/dev/usbharu/multim/api/ApiClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) {
return Err(HttpClientClientError(e))
} catch (e: ServerResponseException) {
return Err(HttpClientServerError(e))
} catch (e: Exception) {
return Err(ThrowableError(e))
}
return Ok(Unit)

Expand All @@ -100,15 +102,16 @@ abstract class ApiClient(var baseUrl: String, val client: HttpClient) {
block: HttpRequestBuilder.() -> Unit
): Result<HttpResponse, ThrowableError> {
Logger.trace("Api Client", "Get $baseUrl$path")
return runCatching<HttpResponse> {
client.get(
baseUrl + path,
block
)
}.fold(onSuccess = { Ok(it) }, onFailure = {
return try {
Ok(client.get(baseUrl + path, block))
} catch (e: ClientRequestException) {
Err(HttpClientClientError(e))
} catch (e: ServerResponseException) {
Err(HttpClientServerError(e))
} catch (e: Exception) {
Logger.warn("Api Client", "FAILURE Get $baseUrl$path")
Err(ThrowableError(it))
})
Err(ThrowableError(e))
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions core/src/test/kotlin/dev/usbharu/multim/TestUtil.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.usbharu.multim

import com.github.michaelbull.result.*
import dev.usbharu.multim.error.Error
import dev.usbharu.multim.error.ErrorType
import dev.usbharu.multim.error.MultiMResult
import io.ktor.client.*
Expand Down Expand Up @@ -72,11 +73,14 @@ object TestUtil {
}


fun assertIsOk(result: Result<*, *>) {
fun assertIsOk(result: Result<*, Error>) {
result.getError()?.let {
Logger.warn("Test Util", "エラーが発生しました", it)
}
assertInstanceOf(Ok::class.java, result, "resultの型がOkではない")
}

inline fun <T, reified R : T> assertIsErr(result: Result<*, T>) {
inline fun <T : Error, reified R : T> assertIsErr(result: Result<*, T>) {
assertInstanceOf(Err::class.java, result, "resultの型がErrではない")
assertInstanceOf(R::class.java, result.getError(), "Errorの型が違う")
}
Expand Down
167 changes: 100 additions & 67 deletions core/src/test/kotlin/dev/usbharu/multim/api/ApiClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,40 @@ import org.junit.jupiter.api.Test
@OptIn(ExperimentalCoroutinesApi::class)
abstract class ApiClientTest {

abstract var apiClient: ApiClient
abstract val apiClient: ApiClient

val postEmpty_emptyRequest_returnOk = "postEmpty/emptyRequest/returnOk"
@Test
open fun postEmpty_emptyRequest_returnOk() = runTest {
val postEmpty = apiClient.postEmpty<PostEmptyData>("postEmpty/emptyRequest/returnOk")
assertIsOk(postEmpty)
open fun postEmpty_emptyRequest_returnOk() {
runTest {
val postEmpty = apiClient.postEmpty<PostEmptyData>(postEmpty_emptyRequest_returnOk)
assertIsOk(postEmpty)
}
}

val postEmpty_illegalRequest_returnErr = "postEmpty/illegalRequest/returnErr"
@Test
open fun postEmpty_illegalRequest_returnErr() = runTest {
val postEmpty = apiClient.postEmpty<PostEmptyData>("postEmpty/illegalRequest/returnErr")
assertInstanceOf(Err::class.java, postEmpty, "返り値がErr型ではない")
assertInstanceOf(
HttpClientClientError::class.java,
(postEmpty as Err).error,
"ErrorがHttpClientClientError型ではない"
)
assertInstanceOf(
ClientRequestException::class.java,
postEmpty.error.throwable,
"throwableがClientRequestException型ではない"
)
open fun postEmpty_illegalRequest_returnErr() {
runTest {
val postEmpty = apiClient.postEmpty<PostEmptyData>(postEmpty_illegalRequest_returnErr)
assertInstanceOf(Err::class.java, postEmpty, "返り値がErr型ではない")
assertInstanceOf(
HttpClientClientError::class.java,
(postEmpty as Err).error,
"ErrorがHttpClientClientError型ではない"
)
assertInstanceOf(
ClientRequestException::class.java,
postEmpty.error.throwable,
"throwableがClientRequestException型ではない"
)
}
}

val postEmpty_serverError_returnErr = "postEmpty/serverError/returnErr"
@Test
fun postEmpty_serverError_returnErr() = runTest {
val postEmpty = apiClient.postEmpty<PostEmptyData>("postEmpty/serverError/returnErr")
val postEmpty = apiClient.postEmpty<PostEmptyData>(postEmpty_serverError_returnErr)
assertInstanceOf(Err::class.java, postEmpty, "返り値がErr型ではない")
assertInstanceOf(
HttpClientServerError::class.java,
Expand All @@ -58,79 +65,105 @@ abstract class ApiClientTest {
)
}

val post_request_returnOkWithEchoBody = "post/request/returnOkWithEchoBody"
@Test
fun post_request_returnOkWithEchoBody() = runTest {
val expectBody = PostEmptyData("957a29f2-03bc-4856-b880-3ef42fbc4c77")
val actual = apiClient.post<PostEmptyData, PostEmptyData>(
expectBody,
"post/request/returnOkWithEchoBody"
)
assertIsOk(actual)
assertEquals(expectBody, actual.get())
fun post_request_returnOkWithEchoBody() {
runTest {
val expectBody = PostEmptyData("957a29f2-03bc-4856-b880-3ef42fbc4c77")
val actual = apiClient.post<PostEmptyData, PostEmptyData>(
expectBody,
post_request_returnOkWithEchoBody
)
assertIsOk(actual)
assertEquals(expectBody, actual.get())
}
}

val post_IllegalRequest_returnErr = "post/illegalRequest/returnErr"
@Test
fun post_illegalRequest_returnErr() = runTest {
val body = PostEmptyData("1974d2a0-552e-442d-a60c-07ab410d4e44")
val actual = apiClient.post<PostEmptyData, PostEmptyData>(
body,
"post/illegalRequest/returnErr"
)
assertIsErr<ThrowableError, HttpClientClientError>(actual)
fun post_illegalRequest_returnErr() {
runTest {
val body = PostEmptyData("1974d2a0-552e-442d-a60c-07ab410d4e44")
val actual = apiClient.post<PostEmptyData, PostEmptyData>(
body,
post_IllegalRequest_returnErr
)
assertIsErr<ThrowableError, HttpClientClientError>(actual)
}
}

val post_serverError_returnErr = "post/serverError/returnErr"
@Test
fun post_serverError_returnErr() = runTest {
val body = PostEmptyData("3a84e1a1-5323-4158-824d-ccf9efee0bf7")
val actual = apiClient.post<PostEmptyData, PostEmptyData>(
body,
"post/serverError/returnErr"
)
assertIsErr<ThrowableError, HttpClientServerError>(actual)
fun post_serverError_returnErr() {
runTest {
val body = PostEmptyData("3a84e1a1-5323-4158-824d-ccf9efee0bf7")
val actual = apiClient.post<PostEmptyData, PostEmptyData>(
body,
post_serverError_returnErr
)
assertIsErr<ThrowableError, HttpClientServerError>(actual)
}
}

val postWithoutResponse_request_returnOk = "postWithoutResponse/request/returnOk"
@Test
fun postWithoutResponse_request_returnOk() = runTest {
val body = PostEmptyData("497676e7-731b-42cb-bc44-dee573858e66")
val actual =
apiClient.postWithoutResponse(body, "postWithoutResponse/request/returnOk")
assertIsOk(actual)
fun postWithoutResponse_request_returnOk() {
runTest {
val body = PostEmptyData("497676e7-731b-42cb-bc44-dee573858e66")
val actual =
apiClient.postWithoutResponse(body, postWithoutResponse_request_returnOk)
assertIsOk(actual)
}
}

val postWithoutResponse_IllegalRequest_returnErr = "postWithoutResponse/illegalRequest/returnErr"
@Test
fun postWithoutResponse_illegalRequest_returnErr() = runTest {
val body = PostEmptyData("20b04325-1386-4076-8068-fb6370ba6150")
val actual =
apiClient.postWithoutResponse(body, "postWithoutResponse/illegalRequest/returnErr")
assertIsErr<ThrowableError, HttpClientClientError>(actual)
fun postWithoutResponse_illegalRequest_returnErr() {
runTest {
val body = PostEmptyData("20b04325-1386-4076-8068-fb6370ba6150")
val actual =
apiClient.postWithoutResponse(body, postWithoutResponse_IllegalRequest_returnErr)
assertIsErr<ThrowableError, HttpClientClientError>(actual)
}
}

val postWithoutResponse_serverError_returnErr = "postWithoutResponse/serverError/returnErr"
@Test
fun postWithoutResponse_serverError_returnErr() = runTest {
val body = PostEmptyData("5ef770aa-b228-4b02-90dd-1c1866933f16")
val actual =
apiClient.postWithoutResponse(body, "postWithoutResponse/serverError/returnErr")
assertIsErr<ThrowableError, HttpClientServerError>(actual)
fun postWithoutResponse_serverError_returnErr() {
runTest {
val body = PostEmptyData("5ef770aa-b228-4b02-90dd-1c1866933f16")
val actual =
apiClient.postWithoutResponse(body, postWithoutResponse_serverError_returnErr)
assertIsErr<ThrowableError, HttpClientServerError>(actual)
}
}

val get_request_returnOk = "get/request/returnOk"
@Test
fun get_request_returnOk() = runTest {
val expectBody = PostEmptyData("43c56ee1-ad7a-418e-9411-2fcb61b34cf9")
val actual = apiClient.get("get/request/returnOk") {}
assertIsOk(actual)
assertEquals(expectBody, actual.get()?.body<PostEmptyData>())
fun get_request_returnOk() {
runTest {
val expectBody = PostEmptyData("43c56ee1-ad7a-418e-9411-2fcb61b34cf9")
val actual = apiClient.get(get_request_returnOk) {}
assertIsOk(actual)
assertEquals(expectBody, actual.get()?.body<PostEmptyData>())
}
}

val get_IllegalRequest_returnErr = "get/illegalRequest/returnErr"
@Test
fun get_illegalRequest_returnErr() = runTest {
val actual = apiClient.get("get/illegalRequest/returnErr") {}
assertIsErr<ThrowableError, HttpClientClientError>(actual)
fun get_illegalRequest_returnErr() {
runTest {
val actual = apiClient.get(get_IllegalRequest_returnErr) {}
assertIsErr<ThrowableError, HttpClientClientError>(actual)
}
}

val get_serverError_returnErr = "get/serverError/returnErr"
@Test
fun get_serverError_returnErr() = runTest {
val actual = apiClient.get("get/serverError/returnErr") {}
assertIsErr<ThrowableError, HttpClientServerError>(actual)
fun get_serverError_returnErr() {
runTest {
val actual = apiClient.get(get_serverError_returnErr) {}
assertIsErr<ThrowableError, HttpClientServerError>(actual)
}
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import kotlinx.serialization.encodeToString

class MisskeyApiClient(var auth: SingleTokenAuth, baseUrl: String, client: HttpClient) :
ApiClient(baseUrl, client.config {
expectSuccess = true
install(WebSockets) {
pingInterval = 20_000
contentConverter = KotlinxWebsocketSerializationConverter(json)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.usbharu.multim.misskey.v12.model

import dev.usbharu.multim.misskey.v12.model.components.MisskeyNeedAuth
import dev.usbharu.multim.misskey.v12.model.components.Note
import kotlinx.serialization.Serializable

Expand All @@ -16,7 +17,7 @@ data class UsersNotesRequest(
val withFile: Boolean? = false,
val fileType: List<String> = emptyList(),
val excludeNsfw: Boolean = false
)
) : MisskeyNeedAuth()


typealias UsersNotesResponse = List<Note>
41 changes: 41 additions & 0 deletions impl/misskey/src/test/kotlin/MisskeyTestUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getError
import dev.usbharu.multim.misskey.v12.common.api.MisskeyApiClient
import dev.usbharu.multim.misskey.v12.model.components.Note
import dev.usbharu.multim.misskey.v12.model.components.UserLite
import dev.usbharu.multim.model.SingleTokenAuth
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.mockk.InternalPlatformDsl.toStr
import kotlinx.datetime.Clock
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
Expand All @@ -21,6 +25,43 @@ object MisskeyTestUtil {

val json = Json { ignoreUnknownKeys = true;isLenient = true }

val baseUrl = "https://localhsot/"

internal fun apiClient(httpClient: HttpClient): MisskeyApiClient {
return MisskeyApiClient(SingleTokenAuth("cdgj2h71"), baseUrl, httpClient)
}

inline fun <reified T> createMockHttpClient(
content: T,
checkAuth: Boolean,
url: String? = null,
serializer:SerializationStrategy<T>,
statusCode: HttpStatusCode = HttpStatusCode.OK,
): HttpClient {
return HttpClient(MockEngine {
if (!checkAuth || "i" in json.parseToJsonElement(it.body.toByteArray().decodeToString()).jsonObject) {
//ok
} else {
Fail.fail("No auth.")
}
if (url == null || it.url.toStr() == url) {
//ok
}else {
Fail.fail("Illegal URL expected: $url actual: ${it.url.toStr()}")
}

respond(
content = json.encodeToString(serializer,content),
status = statusCode,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}) {
install(ContentNegotiation) {
json(json)
}
}
}

fun createMockHttpClient(
content: String = "",
contentType: String = "application/json",
Expand Down
Loading