refactor(i18n): 공급자 우선순위를 코드 목록으로 지정한다
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package kr.co.vividnext.sodalive.i18n.translation
|
||||
|
||||
object LanguageProviderOrder {
|
||||
val detectionOrder = listOf("papago")
|
||||
val translationOrder = listOf("papago")
|
||||
}
|
||||
+5
-7
@@ -1,17 +1,15 @@
|
||||
package kr.co.vividnext.sodalive.i18n.translation
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class LanguageProviderProperties(
|
||||
@Value("\${sodalive.language-providers.detection-order:papago}")
|
||||
detectionOrder: String = "papago",
|
||||
@Value("\${sodalive.language-providers.translation-order:papago}")
|
||||
translationOrder: String = "papago"
|
||||
val detectionOrder: List<String>,
|
||||
val translationOrder: List<String>
|
||||
) {
|
||||
val detectionOrder = detectionOrder.split(',').map { it.trim() }
|
||||
val translationOrder = translationOrder.split(',').map { it.trim() }
|
||||
@Autowired
|
||||
constructor() : this(LanguageProviderOrder.detectionOrder, LanguageProviderOrder.translationOrder)
|
||||
|
||||
fun <T> orderedProviders(order: List<String>, providers: List<T>, name: (T) -> String): List<T> {
|
||||
require(order.isNotEmpty() && order.none { it.isBlank() }) { "language provider order must not be empty" }
|
||||
|
||||
@@ -19,11 +19,6 @@ management:
|
||||
show-details: never
|
||||
show-components: never
|
||||
|
||||
sodalive:
|
||||
language-providers:
|
||||
detection-order: ${LANGUAGE_PROVIDERS_DETECTION_ORDER:papago}
|
||||
translation-order: ${LANGUAGE_PROVIDERS_TRANSLATION_ORDER:papago}
|
||||
|
||||
deployment:
|
||||
artifact-version: ${DEPLOY_ARTIFACT_VERSION:local}
|
||||
config-commit: ${DEPLOY_CONFIG_COMMIT:0000000000000000000000000000000000000000}
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ class LanguageDetectionProviderTest {
|
||||
}
|
||||
|
||||
private fun service(providers: List<LanguageDetectionProvider>, order: List<String> = providers.map { it.providerName }) =
|
||||
LanguageDetectionCacheService(repository, providers, LanguageProviderProperties(detectionOrder = order.joinToString(",")))
|
||||
LanguageDetectionCacheService(repository, providers, LanguageProviderProperties(order, listOf("papago")))
|
||||
}
|
||||
|
||||
class RecordingDetectionProvider(
|
||||
|
||||
+7
-4
@@ -35,16 +35,13 @@ import java.util.UUID
|
||||
@DataJpaTest(
|
||||
properties = [
|
||||
"spring.cache.type=none",
|
||||
"spring.datasource.url=jdbc:h2:mem:language-provider-cache;MODE=MySQL;NON_KEYWORDS=VALUE;DB_CLOSE_ON_EXIT=FALSE",
|
||||
"sodalive.language-providers.detection-order=papago,secondary",
|
||||
"sodalive.language-providers.translation-order=papago,secondary"
|
||||
"spring.datasource.url=jdbc:h2:mem:language-provider-cache;MODE=MySQL;NON_KEYWORDS=VALUE;DB_CLOSE_ON_EXIT=FALSE"
|
||||
]
|
||||
)
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Import(
|
||||
QueryDslConfig::class,
|
||||
AudioContentThemeQueryRepository::class,
|
||||
LanguageProviderProperties::class,
|
||||
LanguageDetectionCacheService::class,
|
||||
TranslationCoordinator::class,
|
||||
TranslationJobWorker::class,
|
||||
@@ -163,6 +160,12 @@ class LanguageProviderCacheEndToEndTest @Autowired constructor(
|
||||
|
||||
@TestConfiguration
|
||||
class Providers {
|
||||
@Bean
|
||||
fun languageProviderProperties() = LanguageProviderProperties(
|
||||
listOf("papago", "secondary"),
|
||||
listOf("papago", "secondary")
|
||||
)
|
||||
|
||||
@Bean
|
||||
fun papagoDetection() = RecordingDetectionProvider("papago") { throw IllegalStateException("sensitive provider body") }
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ class LanguageProviderPropertiesTest {
|
||||
@Test
|
||||
@DisplayName("기능별 목록을 서로 독립된 설정 순서로 정렬한다")
|
||||
fun shouldOrderCapabilitiesIndependently() {
|
||||
val properties = LanguageProviderProperties("second,first", "first,second")
|
||||
val properties = LanguageProviderProperties(listOf("second", "first"), listOf("first", "second"))
|
||||
assertEquals(
|
||||
listOf("second", "first"),
|
||||
properties.orderedProviders(properties.detectionOrder, listOf("first", "second")) { it }
|
||||
|
||||
+80
-14
@@ -12,17 +12,63 @@ import org.springframework.boot.test.context.TestConfiguration
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.core.env.SystemEnvironmentPropertySource
|
||||
import java.util.function.Supplier
|
||||
|
||||
class LanguageProviderSpringConfigurationTest {
|
||||
private val runner = ApplicationContextRunner().withUserConfiguration(Providers::class.java)
|
||||
private val defaultRunner = runner.withUserConfiguration(LanguageProviderProperties::class.java)
|
||||
|
||||
@Test
|
||||
@DisplayName("실제 Spring 설정 바인딩으로 감지만 순서를 바꾸어도 번역 순서는 유지한다")
|
||||
fun shouldBindIndependentCapabilityOrders() {
|
||||
listOf("papago,secondary" to "papago", "secondary,papago" to "secondary").forEach { (order, expected) ->
|
||||
runner.withPropertyValues(
|
||||
"sodalive.language-providers.detection-order=$order",
|
||||
"sodalive.language-providers.translation-order=papago,secondary"
|
||||
@DisplayName("과거 Spring 우선순위 property가 있어도 production 기본 순서는 바뀌지 않는다")
|
||||
fun shouldIgnoreLegacyPropertyOverrides() {
|
||||
defaultRunner.withPropertyValues(
|
||||
"sodalive.language-providers.detection-order=secondary,papago",
|
||||
"sodalive.language-providers.translation-order=secondary,papago"
|
||||
).run { context ->
|
||||
assertNull(context.startupFailure)
|
||||
val properties = context.getBean(LanguageProviderProperties::class.java)
|
||||
assertEquals(listOf("papago"), properties.detectionOrder)
|
||||
assertEquals(listOf("papago"), properties.translationOrder)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("과거 우선순위 환경변수가 property로 연결돼도 production 기본 순서는 바뀌지 않는다")
|
||||
fun shouldIgnoreLegacyEnvironmentOverrides() {
|
||||
defaultRunner.withInitializer { context ->
|
||||
context.environment.propertySources.addFirst(
|
||||
SystemEnvironmentPropertySource(
|
||||
"legacyProviderOrderEnvironment",
|
||||
mapOf(
|
||||
"LANGUAGE_PROVIDERS_DETECTION_ORDER" to "secondary,papago",
|
||||
"LANGUAGE_PROVIDERS_TRANSLATION_ORDER" to "secondary,papago"
|
||||
)
|
||||
)
|
||||
)
|
||||
}.withPropertyValues(
|
||||
"sodalive.language-providers.detection-order=\${LANGUAGE_PROVIDERS_DETECTION_ORDER:papago}",
|
||||
"sodalive.language-providers.translation-order=\${LANGUAGE_PROVIDERS_TRANSLATION_ORDER:papago}"
|
||||
).run { context ->
|
||||
assertNull(context.startupFailure)
|
||||
assertEquals("secondary,papago", context.environment.getProperty("sodalive.language-providers.detection-order"))
|
||||
assertEquals("secondary,papago", context.environment.getProperty("sodalive.language-providers.translation-order"))
|
||||
val properties = context.getBean(LanguageProviderProperties::class.java)
|
||||
assertEquals(listOf("papago"), properties.detectionOrder)
|
||||
assertEquals(listOf("papago"), properties.translationOrder)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Spring 테스트 bean의 감지 목록만 바꾸어도 번역 순서는 유지한다")
|
||||
fun shouldInjectIndependentCapabilityOrders() {
|
||||
listOf(
|
||||
listOf("papago", "secondary") to "papago",
|
||||
listOf("secondary", "papago") to "secondary"
|
||||
).forEach { (order, expected) ->
|
||||
runner.withBean(
|
||||
LanguageProviderProperties::class.java,
|
||||
Supplier { LanguageProviderProperties(order, listOf("papago", "secondary")) }
|
||||
).run { context ->
|
||||
assertNull(context.startupFailure)
|
||||
val detector = context.getBean(LanguageDetectionCacheService::class.java)
|
||||
@@ -37,7 +83,10 @@ class LanguageProviderSpringConfigurationTest {
|
||||
assertEquals(0, context.getBean("secondaryTranslation", RecordingTranslationProvider::class.java).requests.size)
|
||||
}
|
||||
}
|
||||
runner.withPropertyValues("sodalive.language-providers.translation-order=secondary,papago").run { context ->
|
||||
runner.withBean(
|
||||
LanguageProviderProperties::class.java,
|
||||
Supplier { LanguageProviderProperties(listOf("papago", "secondary"), listOf("secondary", "papago")) }
|
||||
).run { context ->
|
||||
assertNull(context.startupFailure)
|
||||
assertEquals(
|
||||
"papago",
|
||||
@@ -52,17 +101,34 @@ class LanguageProviderSpringConfigurationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Spring 기본값은 두 기능 모두 파파고이며 잘못된 설정은 시작 시 실패한다")
|
||||
fun shouldDefaultToPapagoAndRejectInvalidConfigurationAtStartup() {
|
||||
runner.run { context ->
|
||||
@DisplayName("실제 production component의 Spring 기본 생성자는 두 기능 모두 파파고를 사용한다")
|
||||
fun shouldDefaultToPapago() {
|
||||
defaultRunner.run { context ->
|
||||
assertNull(context.startupFailure)
|
||||
val properties = context.getBean(LanguageProviderProperties::class.java)
|
||||
assertEquals(listOf("papago"), properties.detectionOrder)
|
||||
assertEquals(listOf("papago"), properties.translationOrder)
|
||||
assertEquals(
|
||||
"papago",
|
||||
context.getBean(LanguageDetectionCacheService::class.java).detectWithCache("source")?.providerName
|
||||
)
|
||||
assertEquals(
|
||||
"papago",
|
||||
context.getBean(TranslationCoordinator::class.java)
|
||||
.translate(TranslateRequest(listOf("source"), "ko", "en")).providerName
|
||||
)
|
||||
}
|
||||
listOf("detection-order", "translation-order").forEach { key ->
|
||||
listOf("", "papago,papago", "unknown").forEach { value ->
|
||||
runner.withPropertyValues("sodalive.language-providers.$key=$value").run { context ->
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Spring 테스트 bean의 빈 목록과 빈 이름과 중복 순서와 미등록 이름은 시작 시 실패한다")
|
||||
fun shouldRejectInvalidOrdersAtStartup() {
|
||||
listOf(emptyList(), listOf(""), listOf("papago", "papago"), listOf("unknown")).forEach { order ->
|
||||
listOf(
|
||||
LanguageProviderProperties(order, listOf("papago")),
|
||||
LanguageProviderProperties(listOf("papago"), order)
|
||||
).forEach { properties ->
|
||||
runner.withBean(LanguageProviderProperties::class.java, Supplier { properties }).run { context ->
|
||||
assertNotNull(context.startupFailure)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +136,7 @@ class LanguageProviderSpringConfigurationTest {
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
@Import(LanguageProviderProperties::class, TranslationCoordinator::class, LanguageDetectionCacheService::class)
|
||||
@Import(TranslationCoordinator::class, LanguageDetectionCacheService::class)
|
||||
class Providers {
|
||||
@Bean
|
||||
fun memoryRepository(): TranslationMemoryRepository = Mockito.mock(TranslationMemoryRepository::class.java)
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ class TranslationCoordinatorTest {
|
||||
private fun coordinator(
|
||||
providers: List<TranslationProvider>,
|
||||
order: List<String> = providers.map { it.providerName }
|
||||
) = TranslationCoordinator(providers, LanguageProviderProperties(translationOrder = order.joinToString(",")), repository)
|
||||
) = TranslationCoordinator(providers, LanguageProviderProperties(listOf("papago"), order), repository)
|
||||
|
||||
private fun memory(provider: String, text: String) = TranslationMemory(
|
||||
"hash",
|
||||
|
||||
Reference in New Issue
Block a user