diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt index 445b4f75..2c89e06c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderOrder.kt @@ -1,6 +1,6 @@ package kr.co.vividnext.sodalive.i18n.translation object LanguageProviderOrder { - val detectionOrder = listOf(LanguageProviderId.PAPAGO) - val translationOrder = listOf(LanguageProviderId.PAPAGO) + val detectionOrder = listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS) + val translationOrder = listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS) } diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectTest.kt index 662c6ce4..acfe3c16 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectTest.kt @@ -90,7 +90,6 @@ import javax.persistence.EntityManager TranslationSourceExtractor::class, TranslationReadModelMaterializer::class, TranslationCoordinator::class, - LanguageProviderProperties::class, PapagoTranslationService::class, ResourceTranslationJobScheduler::class, TranslationJobScheduler::class, @@ -678,6 +677,12 @@ class CreatorCommunityLanguageDetectTest @Autowired constructor( @TestConfiguration @EnableAsync class AsyncConfiguration { + @Bean + fun languageProviderProperties() = LanguageProviderProperties( + listOf(LanguageProviderId.PAPAGO), + listOf(LanguageProviderId.PAPAGO) + ) + @Bean(name = ["taskExecutor"]) fun taskExecutor(): DetailPipelineTaskExecutor { return DetailPipelineTaskExecutor() @@ -734,7 +739,7 @@ class ControlledLanguageDetectionCacheService( override val providerId = LanguageProviderId.PAPAGO override fun detect(query: String): String? = null }), - LanguageProviderProperties() + LanguageProviderProperties(listOf(LanguageProviderId.PAPAGO), listOf(LanguageProviderId.PAPAGO)) ) { val detections = ConcurrentHashMap() val queries = ConcurrentLinkedQueue() diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectionCacheConcurrencyTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectionCacheConcurrencyTest.kt index 8382c191..9f239128 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectionCacheConcurrencyTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/content/CreatorCommunityLanguageDetectionCacheConcurrencyTest.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.configs.QueryDslConfig import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityRepository +import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderId.PAPAGO import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderProperties import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType import kr.co.vividnext.sodalive.i18n.translation.PapagoLanguageDetectionProvider @@ -29,6 +30,8 @@ import org.springframework.aop.support.AopUtils import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Import import org.springframework.http.HttpHeaders import org.springframework.http.HttpMethod @@ -71,7 +74,7 @@ import java.util.concurrent.atomic.AtomicInteger QueryDslConfig::class, AudioContentThemeQueryRepository::class, LanguageDetectionCacheService::class, - LanguageProviderProperties::class, + CreatorCommunityLanguageDetectionCacheConcurrencyTest.PapagoConfiguration::class, PapagoLanguageDetectionProvider::class, PapagoTranslationService::class, TranslationCoordinator::class, @@ -98,6 +101,12 @@ class CreatorCommunityLanguageDetectionCacheConcurrencyTest @Autowired construct } private lateinit var papagoRequests: ControlledPapagoRequestFactory + @TestConfiguration + class PapagoConfiguration { + @Bean + fun languageProviderProperties() = LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)) + } + @BeforeEach fun setUp() { papagoRequests = ControlledPapagoRequestFactory() diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectionCacheServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectionCacheServiceTest.kt index c33ab3f9..a5abf075 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectionCacheServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/content/LanguageDetectionCacheServiceTest.kt @@ -108,6 +108,10 @@ class LanguageDetectionCacheServiceTest { override val providerId = LanguageProviderId.PAPAGO override fun detect(query: String) = detect() } - return LanguageDetectionCacheService(repository, listOf(provider), LanguageProviderProperties()) + return LanguageDetectionCacheService( + repository, + listOf(provider), + LanguageProviderProperties(listOf(LanguageProviderId.PAPAGO), listOf(LanguageProviderId.PAPAGO)) + ) } } diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderIdTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderIdTest.kt index a384b7a2..e10110af 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderIdTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderIdTest.kt @@ -42,12 +42,24 @@ class LanguageProviderIdTest { } @Test - @DisplayName("production 코드 목록과 실제 Papago adapter는 PAPAGO enum을 사용한다") + @DisplayName("production 기본 순서는 PAPAGO 다음 AWS이며 실제 Papago adapter 식별자는 유지한다") fun shouldUsePapagoEnumForProductionDefaultsAndAdapters() { - assertEquals(listOf(LanguageProviderId.PAPAGO), LanguageProviderOrder.detectionOrder) - assertEquals(listOf(LanguageProviderId.PAPAGO), LanguageProviderOrder.translationOrder) - assertEquals(listOf(LanguageProviderId.PAPAGO), LanguageProviderProperties().detectionOrder) - assertEquals(listOf(LanguageProviderId.PAPAGO), LanguageProviderProperties().translationOrder) + assertAll( + { assertEquals(listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS), LanguageProviderOrder.detectionOrder) }, + { assertEquals(listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS), LanguageProviderOrder.translationOrder) }, + { + assertEquals( + listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS), + LanguageProviderProperties().detectionOrder + ) + }, + { + assertEquals( + listOf(LanguageProviderId.PAPAGO, LanguageProviderId.AWS), + LanguageProviderProperties().translationOrder + ) + } + ) assertEquals(LanguageProviderId.PAPAGO, PapagoLanguageDetectionProvider("test-id", "test-secret").providerId) val translator = PapagoTranslationService("test-id", "test-secret") assertEquals(LanguageProviderId.PAPAGO, translator.providerId) diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderSpringConfigurationTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderSpringConfigurationTest.kt index fb11dcfc..c652ac43 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderSpringConfigurationTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/LanguageProviderSpringConfigurationTest.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.content.LanguageDetectionCacheService import kr.co.vividnext.sodalive.content.LanguageDetectionResultRepository import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderId.AWS import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderId.PAPAGO +import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertNull @@ -15,12 +16,20 @@ 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 software.amazon.awssdk.services.comprehend.ComprehendClient +import software.amazon.awssdk.services.comprehend.model.DetectDominantLanguageRequest +import software.amazon.awssdk.services.comprehend.model.DetectDominantLanguageResponse +import software.amazon.awssdk.services.comprehend.model.DominantLanguage +import software.amazon.awssdk.services.translate.TranslateClient +import software.amazon.awssdk.services.translate.model.TranslateTextRequest +import software.amazon.awssdk.services.translate.model.TranslateTextResponse import java.util.function.Supplier class LanguageProviderSpringConfigurationTest { private val papagoRunner = ApplicationContextRunner().withUserConfiguration(Providers::class.java) private val runner = papagoRunner.withUserConfiguration(AwsProviders::class.java) private val defaultRunner = runner.withUserConfiguration(LanguageProviderProperties::class.java) + private val actualRunner = papagoRunner.withUserConfiguration(ActualAwsProviders::class.java) @Test @DisplayName("과거 Spring 우선순위 property가 있어도 production 기본 순서는 바뀌지 않는다") @@ -31,8 +40,18 @@ class LanguageProviderSpringConfigurationTest { ).run { context -> assertNull(context.startupFailure) val properties = context.getBean(LanguageProviderProperties::class.java) - assertEquals(listOf(PAPAGO), properties.detectionOrder) - assertEquals(listOf(PAPAGO), properties.translationOrder) + assertAll( + { assertEquals(listOf(PAPAGO, AWS), properties.detectionOrder) }, + { assertEquals(listOf(PAPAGO, AWS), properties.translationOrder) } + ) + assertEquals(PAPAGO, context.getBean(LanguageDetectionCacheService::class.java).detectWithCache("source")?.providerId) + assertEquals( + PAPAGO, + context.getBean(TranslationCoordinator::class.java) + .translate(TranslateRequest(listOf("source"), "ko", "en")).providerId + ) + assertEquals(0, context.getBean("secondaryDetection", RecordingDetectionProvider::class.java).queries.size) + assertEquals(0, context.getBean("secondaryTranslation", RecordingTranslationProvider::class.java).requests.size) } } @@ -57,8 +76,18 @@ class LanguageProviderSpringConfigurationTest { 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) + assertAll( + { assertEquals(listOf(PAPAGO, AWS), properties.detectionOrder) }, + { assertEquals(listOf(PAPAGO, AWS), properties.translationOrder) } + ) + assertEquals(PAPAGO, context.getBean(LanguageDetectionCacheService::class.java).detectWithCache("source")?.providerId) + assertEquals( + PAPAGO, + context.getBean(TranslationCoordinator::class.java) + .translate(TranslateRequest(listOf("source"), "ko", "en")).providerId + ) + assertEquals(0, context.getBean("secondaryDetection", RecordingDetectionProvider::class.java).queries.size) + assertEquals(0, context.getBean("secondaryTranslation", RecordingTranslationProvider::class.java).requests.size) } } @@ -104,13 +133,15 @@ class LanguageProviderSpringConfigurationTest { } @Test - @DisplayName("실제 production component의 Spring 기본 생성자는 두 기능 모두 파파고를 사용한다") + @DisplayName("Spring 기본 생성자는 두 기능 모두 PAPAGO 다음 AWS를 등록하고 첫 PAPAGO 성공 뒤 멈춘다") 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) + assertAll( + { assertEquals(listOf(PAPAGO, AWS), properties.detectionOrder) }, + { assertEquals(listOf(PAPAGO, AWS), properties.translationOrder) } + ) assertEquals( PAPAGO, context.getBean(LanguageDetectionCacheService::class.java).detectWithCache("source")?.providerId @@ -120,6 +151,72 @@ class LanguageProviderSpringConfigurationTest { context.getBean(TranslationCoordinator::class.java) .translate(TranslateRequest(listOf("source"), "ko", "en")).providerId ) + assertEquals(listOf("source"), context.getBean("papagoDetection", RecordingDetectionProvider::class.java).queries) + assertEquals(1, context.getBean("papagoTranslation", RecordingTranslationProvider::class.java).requests.size) + assertEquals(0, context.getBean("secondaryDetection", RecordingDetectionProvider::class.java).queries.size) + assertEquals(0, context.getBean("secondaryTranslation", RecordingTranslationProvider::class.java).requests.size) + } + } + + @Test + @DisplayName("실제 AWS adapter와 SDK client만 등록한 context도 기본 순서에서 PAPAGO 첫 성공 뒤 SDK를 호출하지 않는다") + fun shouldWireActualAwsAdaptersWithoutDuplicateIds() { + actualRunner.withUserConfiguration(LanguageProviderProperties::class.java).run { context -> + assertNull(context.startupFailure) + assertEquals(2, context.getBeansOfType(LanguageDetectionProvider::class.java).size) + assertEquals(2, context.getBeansOfType(TranslationProvider::class.java).size) + assertEquals(AWS, context.getBean(AwsLanguageDetectionProvider::class.java).providerId) + val translator = context.getBean(AwsTranslationService::class.java) + assertEquals(AWS, translator.providerId) + assertEquals("translate-2017-07-01", translator.providerVersion) + val properties = context.getBean(LanguageProviderProperties::class.java) + assertAll( + { assertEquals(listOf(PAPAGO, AWS), properties.detectionOrder) }, + { assertEquals(listOf(PAPAGO, AWS), properties.translationOrder) } + ) + assertEquals(PAPAGO, context.getBean(LanguageDetectionCacheService::class.java).detectWithCache("source")?.providerId) + assertEquals( + PAPAGO, + context.getBean(TranslationCoordinator::class.java) + .translate(TranslateRequest(listOf("source"), "ko", "en")).providerId + ) + Mockito.verifyNoInteractions( + context.getBean(ComprehendClient::class.java), + context.getBean(TranslateClient::class.java) + ) + } + } + + @Test + @DisplayName("실제 AWS adapter는 별도 context의 명시 AWS 우선 순서로 SDK 응답을 반환한다") + fun shouldInvokeActualAwsAdaptersThroughSpringCoordinators() { + actualRunner.withBean( + LanguageProviderProperties::class.java, + Supplier { LanguageProviderProperties(listOf(AWS, PAPAGO), listOf(AWS, PAPAGO)) } + ).run { context -> + assertNull(context.startupFailure) + val detectionRequest = DetectDominantLanguageRequest.builder().text("source").build() + val translationRequest = TranslateTextRequest.builder() + .text("source").sourceLanguageCode("ko").targetLanguageCode("en").build() + val detector = context.getBean(ComprehendClient::class.java) + val translator = context.getBean(TranslateClient::class.java) + Mockito.`when`(detector.detectDominantLanguage(detectionRequest)).thenReturn( + DetectDominantLanguageResponse.builder() + .languages(DominantLanguage.builder().languageCode("ko").build()).build() + ) + Mockito.`when`(translator.translateText(translationRequest)) + .thenReturn(TranslateTextResponse.builder().translatedText("translated").build()) + + assertEquals(AWS, context.getBean(LanguageDetectionCacheService::class.java).detectWithCache("source")?.providerId) + val result = context.getBean(TranslationCoordinator::class.java) + .translate(TranslateRequest(listOf("source"), "ko", "en")) + assertEquals(AWS, result.providerId) + assertEquals("translate-2017-07-01", result.providerVersion) + assertEquals(listOf("translated"), result.result.translatedText) + Mockito.verify(detector).detectDominantLanguage(detectionRequest) + Mockito.verify(translator).translateText(translationRequest) + assertEquals(0, context.getBean("papagoDetection", RecordingDetectionProvider::class.java).queries.size) + assertEquals(0, context.getBean("papagoTranslation", RecordingTranslationProvider::class.java).requests.size) } } @@ -169,4 +266,14 @@ class LanguageProviderSpringConfigurationTest { @Bean fun secondaryTranslation() = RecordingTranslationProvider(AWS) } + + @TestConfiguration + @Import(AwsLanguageDetectionProvider::class, AwsTranslationService::class) + class ActualAwsProviders { + @Bean + fun comprehendClient(): ComprehendClient = Mockito.mock(ComprehendClient::class.java) + + @Bean + fun translateClient(): TranslateClient = Mockito.mock(TranslateClient::class.java) + } } diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationJobWorkerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationJobWorkerTest.kt index d98e1f72..f5bb3916 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationJobWorkerTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/i18n/translation/TranslationJobWorkerTest.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.i18n.translation +import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderId.PAPAGO import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -30,7 +31,11 @@ class TranslationJobWorkerTest { val worker = TranslationJobWorker( translationJobRepository = jobRepository, translationMemoryRepository = memoryRepository, - translationCoordinator = TranslationCoordinator(listOf(provider), LanguageProviderProperties(), memoryRepository), + translationCoordinator = TranslationCoordinator( + listOf(provider), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), + memoryRepository + ), materializer = materializer, transactionManager = TestTransactionManager() ) @@ -54,7 +59,11 @@ class TranslationJobWorkerTest { val worker = TranslationJobWorker( translationJobRepository = jobRepository, translationMemoryRepository = memoryRepository, - translationCoordinator = TranslationCoordinator(listOf(provider), LanguageProviderProperties(), memoryRepository), + translationCoordinator = TranslationCoordinator( + listOf(provider), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), + memoryRepository + ), materializer = materializer, transactionManager = TestTransactionManager() ) @@ -84,7 +93,11 @@ class TranslationJobWorkerTest { val worker = TranslationJobWorker( translationJobRepository = jobRepository, translationMemoryRepository = memoryRepository, - translationCoordinator = TranslationCoordinator(listOf(provider), LanguageProviderProperties(), memoryRepository), + translationCoordinator = TranslationCoordinator( + listOf(provider), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), + memoryRepository + ), materializer = materializer, transactionManager = TestTransactionManager() ) @@ -129,7 +142,11 @@ class TranslationJobWorkerTest { val worker = TranslationJobWorker( translationJobRepository = jobRepository, translationMemoryRepository = memoryRepository, - translationCoordinator = TranslationCoordinator(listOf(provider), LanguageProviderProperties(), memoryRepository), + translationCoordinator = TranslationCoordinator( + listOf(provider), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), + memoryRepository + ), materializer = materializer, transactionManager = TestTransactionManager() ) @@ -165,7 +182,11 @@ class TranslationJobWorkerTest { val worker = TranslationJobWorker( translationJobRepository = jobRepository, translationMemoryRepository = memoryRepository, - translationCoordinator = TranslationCoordinator(listOf(provider), LanguageProviderProperties(), memoryRepository), + translationCoordinator = TranslationCoordinator( + listOf(provider), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), + memoryRepository + ), materializer = materializer, transactionManager = TestTransactionManager() ) @@ -198,7 +219,11 @@ class TranslationJobWorkerTest { val worker = TranslationJobWorker( translationJobRepository = jobRepository, translationMemoryRepository = memoryRepository, - translationCoordinator = TranslationCoordinator(listOf(provider), LanguageProviderProperties(), memoryRepository), + translationCoordinator = TranslationCoordinator( + listOf(provider), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), + memoryRepository + ), materializer = materializer, transactionManager = TestTransactionManager() ) diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/translation/CreatorCommunityTranslationServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/translation/CreatorCommunityTranslationServiceTest.kt index 33d29e76..77856bae 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/translation/CreatorCommunityTranslationServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/creator/channel/community/translation/CreatorCommunityTranslationServiceTest.kt @@ -19,6 +19,7 @@ import kr.co.vividnext.sodalive.content.theme.translation.ContentThemeTranslatio import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityRepository +import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderId.PAPAGO import kr.co.vividnext.sodalive.i18n.translation.LanguageProviderProperties import kr.co.vividnext.sodalive.i18n.translation.LanguageTranslationTargetType import kr.co.vividnext.sodalive.i18n.translation.PapagoTranslationService @@ -673,7 +674,7 @@ class CreatorCommunityTranslationServiceTest @Autowired constructor( sourceExtractor = sourceExtractor, translationCoordinator = TranslationCoordinator( listOf(PapagoTranslationService("test-id", "test-secret")), - LanguageProviderProperties(), + LanguageProviderProperties(listOf(PAPAGO), listOf(PAPAGO)), memoryRepository ), contentTranslationRepository = Mockito.mock(ContentTranslationRepository::class.java),