Skip to content

Commit

Permalink
test: DeepLTranslatorClient 테스트 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
seokjin8678 committed Dec 25, 2023
1 parent 123307f commit 61b6453
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming
import io.github.oshai.kotlinlogging.KotlinLogging
import java.util.UUID
import kr.galaxyhub.sc.common.exception.BadRequestException
import kr.galaxyhub.sc.common.exception.GalaxyhubException
import kr.galaxyhub.sc.common.exception.InternalServerError
import kr.galaxyhub.sc.news.domain.Content
import kr.galaxyhub.sc.news.domain.Language
Expand All @@ -28,11 +29,12 @@ class DeepLTranslatorClient(
.uri("/v2/translate")
.bodyValue(DeepLRequest(content.language.shortName, targetLanguage.shortName, content.toText()))
.retrieve()
.onStatus({ it.is4xxClientError || it.is5xxServerError }) {
.onStatus({ it.isError }) {
log.info { "DeepL ErrorResponse=${it.bodyToMono<String>().block()}" } // 에러 응답 확인용. 추후 불필요하면 삭제
throw handleError(it)
Mono.error(handleError(it))
}
.bodyToMono<DeepLResponse>()
.onErrorResume { Mono.error(handleConnectError(it)) }
.map { it.toContent(content.newsId, targetLanguage) }
}

Expand All @@ -41,21 +43,33 @@ class DeepLTranslatorClient(
*/
private fun handleError(clientResponse: ClientResponse): Exception {
val statusCode = clientResponse.statusCode()
return when(statusCode.value()) {
return when (statusCode.value()) {
HttpStatus.TOO_MANY_REQUESTS.value() -> {
BadRequestException("단기간에 너무 많은 요청을 보냈습니다.")
}

456 -> {
log.error { "DeepL 할당량이 초과되었습니다." }
InternalServerError("할당량이 초과되었습니다. 관리자에게 문의하세요.")
}

else -> {
log.warn { "DeepL 서버에 일시적 문제가 발생했습니다." }
InternalServerError("번역기 서버에 일시적 문제가 발생했습니다.")
}
}
}

private fun handleConnectError(ex: Throwable): Exception {
return when (ex) {
is GalaxyhubException -> ex
else -> {
log.error(ex) { "DeepL 서버에 연결할 수 없습니다." }
InternalServerError("번역기 서버에 연결할 수 없습니다.")
}
}
}

override fun getProvider(): TranslatorProvider {
return TranslatorProvider.DEEPL
}
Expand All @@ -76,7 +90,7 @@ private data class DeepLRequest(
val text: List<String>,
)

private data class DeepLResponse(
internal data class DeepLResponse(
val translations: List<DeepLSentenceResponse>,
) {

Expand All @@ -102,7 +116,7 @@ private data class DeepLResponse(
}

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
private data class DeepLSentenceResponse(
internal data class DeepLSentenceResponse(
val detectedSourceLanguage: String,
val text: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ object ContentFixture {
),
language: Language = Language.ENGLISH,
content: String = "blah blah",
newsId: UUID,
newsId: UUID = UUID.randomUUID(),
): Content {
return Content(
newsInformation = newsInformation,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package kr.galaxyhub.sc.translation.infra

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.throwable.shouldHaveMessage
import kr.galaxyhub.sc.common.exception.BadRequestException
import kr.galaxyhub.sc.common.exception.InternalServerError
import kr.galaxyhub.sc.common.support.enqueue
import kr.galaxyhub.sc.news.domain.Language
import kr.galaxyhub.sc.news.domain.NewsInformation
import kr.galaxyhub.sc.news.fixture.ContentFixture
import okhttp3.mockwebserver.MockWebServer
import org.springframework.web.reactive.function.client.WebClient

class DeepLTranslatorClientTest : DescribeSpec({

isolationMode = IsolationMode.InstancePerLeaf

val objectMapper = jacksonObjectMapper()
val mockWebServer = MockWebServer()
val deepLTranslatorClient = DeepLTranslatorClient(
webClient = WebClient.builder()
.baseUrl("${mockWebServer.url("/")}")
.build()
)

describe("requestTranslate") {
context("외부 서버가 200 응답을 반환하면") {
val response = DeepLResponse(
listOf(
DeepLSentenceResponse(
detectedSourceLanguage = "EN",
text = "제목입니다."
),
DeepLSentenceResponse(
detectedSourceLanguage = "EN",
text = "발췌입니다."
),
DeepLSentenceResponse(
detectedSourceLanguage = "EN",
text = "내용입니다."
)
)
)
mockWebServer.enqueue {
statusCode(200)
body(objectMapper.writeValueAsString(response))
}

val expect = deepLTranslatorClient.requestTranslate(ContentFixture.create(), Language.KOREAN).block()!!

it("번역된 응답이 반환된다.") {
assertSoftly {
expect.newsInformation shouldBe NewsInformation("제목입니다.", "발췌입니다.")
expect.content shouldBe "내용입니다."
}
}
}

context("외부 서버가 429 응답을 반환하면") {
mockWebServer.enqueue {
statusCode(429)
}

val expect = deepLTranslatorClient.requestTranslate(ContentFixture.create(), Language.KOREAN)

it("BadRequestException 예외를 던진다.") {
val ex = shouldThrow<BadRequestException> { expect.block() }
ex shouldHaveMessage "단기간에 너무 많은 요청을 보냈습니다."
}
}

context("외부 서버가 456 응답을 반환하면") {
mockWebServer.enqueue {
statusCode(456)
}

val expect = deepLTranslatorClient.requestTranslate(ContentFixture.create(), Language.KOREAN)

it("InternalServerError 예외를 던진다.") {
val ex = shouldThrow<InternalServerError> { expect.block() }
ex shouldHaveMessage "할당량이 초과되었습니다. 관리자에게 문의하세요."
}
}

context("외부 서버가 그 외 응답을 반환하면") {
mockWebServer.enqueue {
statusCode(500)
}

val expect = deepLTranslatorClient.requestTranslate(ContentFixture.create(), Language.KOREAN)

it("InternalServerError 예외를 던진다.") {
val ex = shouldThrow<InternalServerError> { expect.block() }
ex shouldHaveMessage "번역기 서버에 일시적 문제가 발생했습니다."
}
}

context("외부 서버에 연결할 수 없으면") {
mockWebServer.shutdown()

val expect = deepLTranslatorClient.requestTranslate(ContentFixture.create(), Language.KOREAN)

it("InternalServerError 예외를 던진다.") {
val ex = shouldThrow<InternalServerError> { expect.block() }
ex shouldHaveMessage "번역기 서버에 연결할 수 없습니다."
}
}
}
})

0 comments on commit 61b6453

Please sign in to comment.