From 7f1606a8aad7e9c5889038c1aed582a5a1dc9198 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 2 Apr 2026 17:55:20 +0900 Subject: [PATCH] =?UTF-8?q?feat(content-banner):=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=B0=B0=EB=84=88?= =?UTF-8?q?=EB=A5=BC=20=EC=96=B8=EC=96=B4=EB=B3=84=EB=A1=9C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=98=EA=B3=A0=20=EB=85=B8=EC=B6=9C=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20260402_audio_content_banner_lang_ddl.sql | 41 +++ docs/20260402_오디오콘텐츠배너언어적용.md | 11 + .../banner/AdminContentBannerService.kt | 2 +- .../banner/CreateContentBannerRequest.kt | 2 + .../sodalive/api/home/HomeService.kt | 3 +- .../content/main/banner/AudioContentBanner.kt | 4 + .../banner/AudioContentBannerRepository.kt | 9 +- .../main/banner/AudioContentBannerService.kt | 5 +- .../banner/AdminContentBannerServiceTest.kt | 78 +++++ .../sodalive/api/home/HomeServiceTest.kt | 314 ++++++++++++++++++ .../AudioContentBannerRepositoryTest.kt | 45 +++ 11 files changed, 508 insertions(+), 6 deletions(-) create mode 100644 docs/20260402_audio_content_banner_lang_ddl.sql create mode 100644 docs/20260402_오디오콘텐츠배너언어적용.md create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerServiceTest.kt create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/api/home/HomeServiceTest.kt create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepositoryTest.kt diff --git a/docs/20260402_audio_content_banner_lang_ddl.sql b/docs/20260402_audio_content_banner_lang_ddl.sql new file mode 100644 index 00000000..6e73b150 --- /dev/null +++ b/docs/20260402_audio_content_banner_lang_ddl.sql @@ -0,0 +1,41 @@ +SET @schema_name := DATABASE(); + +SET @lang_column_exists := ( + SELECT COUNT(1) + FROM information_schema.columns + WHERE table_schema = @schema_name + AND table_name = 'content_banner' + AND column_name = 'lang' +); + +SET @add_lang_column_sql := IF( + @lang_column_exists = 0, + 'ALTER TABLE content_banner ADD COLUMN lang VARCHAR(10) NULL COMMENT ''배너 노출 언어'' AFTER type', + 'SELECT ''content_banner.lang already exists'' AS message' +); + +PREPARE add_lang_column_stmt FROM @add_lang_column_sql; +EXECUTE add_lang_column_stmt; +DEALLOCATE PREPARE add_lang_column_stmt; + +UPDATE content_banner +SET lang = 'KO' +WHERE lang IS NULL; + +SET @lang_column_nullable := ( + SELECT IS_NULLABLE + FROM information_schema.columns + WHERE table_schema = @schema_name + AND table_name = 'content_banner' + AND column_name = 'lang' +); + +SET @alter_lang_column_sql := IF( + @lang_column_nullable = 'YES', + 'ALTER TABLE content_banner MODIFY COLUMN lang VARCHAR(10) NOT NULL DEFAULT ''KO'' COMMENT ''배너 노출 언어 (KO 기본, EN/JA 추가 가능)''', + 'SELECT ''content_banner.lang already normalized'' AS message' +); + +PREPARE alter_lang_column_stmt FROM @alter_lang_column_sql; +EXECUTE alter_lang_column_stmt; +DEALLOCATE PREPARE alter_lang_column_stmt; diff --git a/docs/20260402_오디오콘텐츠배너언어적용.md b/docs/20260402_오디오콘텐츠배너언어적용.md new file mode 100644 index 00000000..81b459bf --- /dev/null +++ b/docs/20260402_오디오콘텐츠배너언어적용.md @@ -0,0 +1,11 @@ +- [x] 오디오 콘텐츠 배너 등록·조회 경로와 언어 처리 기준을 확인한다. +- [x] 배너 등록 API에 `lang` 파라미터를 추가하고 지원 언어를 `Lang` 기준으로 저장하도록 수정한다. +- [x] 관리자 배너 목록은 전체 언어 배너를 유지하고, HomeService `fetchData`는 사용자 언어와 일치하는 배너만 조회하도록 수정한다. +- [x] 변경 파일 기준으로 검증을 수행하고 결과를 기록한다. + +## 검증 기록 + +### 1차 구현 +- 무엇을: 오디오 콘텐츠 배너 엔티티와 등록 요청에 `lang`을 추가하고, 홈 `fetchData`에서 현재 사용자 언어를 넘겨 해당 언어 배너만 조회하도록 수정했다. 운영 반영용으로 `content_banner.lang` 컬럼 DDL도 추가했다. +- 왜: 관리자 등록 시 언어별 배너를 구분해 저장해야 하고, 홈에서는 사용자 언어와 맞는 배너만 노출되어야 하기 때문이다. 관리자 목록 API는 기존처럼 언어 전체 배너를 그대로 조회해야 한다. +- 어떻게: Kotlin LSP가 없어 정적 진단은 Gradle 컴파일/테스트로 대체했고, `./gradlew test --tests "kr.co.vividnext.sodalive.admin.content.banner.AdminContentBannerServiceTest" --tests "kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerRepositoryTest" --tests "kr.co.vividnext.sodalive.api.home.HomeServiceTest"`로 등록 언어 저장, 언어별 배너 조회, 홈 언어 전달을 검증했다. 이어서 `./gradlew ktlintCheck`를 실행해 스타일 검증까지 확인했고 두 명령 모두 `BUILD SUCCESSFUL`이다. diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt index a6ecbf47..bcaeadcf 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerService.kt @@ -72,7 +72,7 @@ class AdminContentBannerService( null } - val audioContentBanner = AudioContentBanner(type = request.type) + val audioContentBanner = AudioContentBanner(type = request.type, lang = request.lang) audioContentBanner.link = request.link audioContentBanner.isAdult = request.isAdult audioContentBanner.event = event diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt index e564206a..a5a81fbf 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/content/banner/CreateContentBannerRequest.kt @@ -1,9 +1,11 @@ package kr.co.vividnext.sodalive.admin.content.banner import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType +import kr.co.vividnext.sodalive.i18n.Lang data class CreateContentBannerRequest( val type: AudioContentBannerType, + val lang: Lang, val tabId: Long?, val eventId: Long?, val creatorId: Long?, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt index cf7b925f..e93ed984 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt @@ -124,7 +124,8 @@ class HomeService( val bannerList = bannerService.getBannerList( tabId = 1, memberId = member?.id, - isAdult = isAdult + isAdult = isAdult, + lang = langContext.lang ) val originalAudioDramaList = seriesService.getOriginalAudioDramaList( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt index c79f17ef..6c2dce3c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBanner.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTab import kr.co.vividnext.sodalive.creator.admin.content.series.Series import kr.co.vividnext.sodalive.event.Event +import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.member.Member import javax.persistence.Column import javax.persistence.Entity @@ -22,6 +23,9 @@ data class AudioContentBanner( @Enumerated(value = EnumType.STRING) var type: AudioContentBannerType, @Column(nullable = false) + @Enumerated(value = EnumType.STRING) + var lang: Lang = Lang.KO, + @Column(nullable = false) var isAdult: Boolean = false, @Column(nullable = false) var isActive: Boolean = true, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt index 0fd76c68..3f125d97 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepository.kt @@ -4,19 +4,20 @@ import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.content.main.banner.QAudioContentBanner.audioContentBanner import kr.co.vividnext.sodalive.content.main.tab.QAudioContentMainTab.audioContentMainTab import kr.co.vividnext.sodalive.event.QEvent.event +import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.member.QMember.member import org.springframework.data.jpa.repository.JpaRepository interface AudioContentBannerRepository : JpaRepository, AudioContentBannerQueryRepository interface AudioContentBannerQueryRepository { - fun getAudioContentMainBannerList(tabId: Long, isAdult: Boolean): List + fun getAudioContentMainBannerList(tabId: Long, isAdult: Boolean, lang: Lang? = null): List } class AudioContentBannerQueryRepositoryImpl( private val queryFactory: JPAQueryFactory ) : AudioContentBannerQueryRepository { - override fun getAudioContentMainBannerList(tabId: Long, isAdult: Boolean): List { + override fun getAudioContentMainBannerList(tabId: Long, isAdult: Boolean, lang: Lang?): List { var where = audioContentBanner.isActive.isTrue where = if (tabId == 1L) { @@ -29,6 +30,10 @@ class AudioContentBannerQueryRepositoryImpl( where = where.and(audioContentBanner.isAdult.isFalse) } + if (lang != null) { + where = where.and(audioContentBanner.lang.eq(lang)) + } + return queryFactory .selectFrom(audioContentBanner) .leftJoin(audioContentBanner.tab, audioContentMainTab) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt index 4aaaffa7..c8904ca5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerService.kt @@ -1,6 +1,7 @@ package kr.co.vividnext.sodalive.content.main.banner import kr.co.vividnext.sodalive.event.EventItem +import kr.co.vividnext.sodalive.i18n.Lang import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service @@ -13,8 +14,8 @@ class AudioContentBannerService( @Value("\${cloud.aws.cloud-front.host}") private val imageHost: String ) { - fun getBannerList(tabId: Long, memberId: Long?, isAdult: Boolean): List { - return repository.getAudioContentMainBannerList(tabId, isAdult) + fun getBannerList(tabId: Long, memberId: Long?, isAdult: Boolean, lang: Lang? = null): List { + return repository.getAudioContentMainBannerList(tabId, isAdult, lang) .filter { if (it.type == AudioContentBannerType.CREATOR && it.creator != null && memberId != null) { !isBlockedBetweenMembers(memberId = memberId, creatorId = it.creator!!.id!!) diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerServiceTest.kt new file mode 100644 index 00000000..6ce901d5 --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/banner/AdminContentBannerServiceTest.kt @@ -0,0 +1,78 @@ +package kr.co.vividnext.sodalive.admin.content.banner + +import com.amazonaws.services.s3.AmazonS3Client +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository +import kr.co.vividnext.sodalive.admin.content.tab.AdminContentMainTabRepository +import kr.co.vividnext.sodalive.aws.s3.S3Uploader +import kr.co.vividnext.sodalive.content.main.banner.AudioContentBanner +import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType +import kr.co.vividnext.sodalive.event.EventRepository +import kr.co.vividnext.sodalive.i18n.Lang +import kr.co.vividnext.sodalive.member.MemberRepository +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mockito +import org.springframework.mock.web.MockMultipartFile +import java.net.URL + +class AdminContentBannerServiceTest { + private lateinit var amazonS3Client: AmazonS3Client + private lateinit var s3Uploader: S3Uploader + private lateinit var repository: AdminContentBannerRepository + private lateinit var memberRepository: MemberRepository + private lateinit var seriesRepository: AdminContentSeriesRepository + private lateinit var eventRepository: EventRepository + private lateinit var contentMainTabRepository: AdminContentMainTabRepository + private lateinit var service: AdminContentBannerService + + @BeforeEach + fun setUp() { + amazonS3Client = Mockito.mock(AmazonS3Client::class.java) + s3Uploader = S3Uploader(amazonS3Client) + repository = Mockito.mock(AdminContentBannerRepository::class.java) + memberRepository = Mockito.mock(MemberRepository::class.java) + seriesRepository = Mockito.mock(AdminContentSeriesRepository::class.java) + eventRepository = Mockito.mock(EventRepository::class.java) + contentMainTabRepository = Mockito.mock(AdminContentMainTabRepository::class.java) + service = AdminContentBannerService( + s3Uploader = s3Uploader, + repository = repository, + memberRepository = memberRepository, + seriesRepository = seriesRepository, + eventRepository = eventRepository, + contentMainTabRepository = contentMainTabRepository, + objectMapper = jacksonObjectMapper(), + bucket = "test-bucket" + ) + } + + @Test + @DisplayName("배너 등록 요청의 lang 값을 저장한다") + fun shouldSaveRequestedLangWhenCreatingBanner() { + val image = MockMultipartFile("image", "banner.png", "image/png", "image".toByteArray()) + val requestString = """ + {"type":"LINK","lang":"ja","tabId":null,"eventId":null,"creatorId":null,"seriesId":null,"link":"https://example.com","isAdult":false} + """.trimIndent() + val savedBannerCaptor = ArgumentCaptor.forClass(AudioContentBanner::class.java) + + Mockito.`when`(repository.save(Mockito.any(AudioContentBanner::class.java))) + .thenAnswer { + (it.arguments[0] as AudioContentBanner).also { banner -> + banner.id = 1L + } + } + Mockito.doAnswer { URL("https://cdn.test/${it.arguments[1]}") } + .`when`(amazonS3Client) + .getUrl(Mockito.eq("test-bucket"), Mockito.anyString()) + + service.createAudioContentMainBanner(image, requestString) + + Mockito.verify(repository).save(savedBannerCaptor.capture()) + assertEquals(AudioContentBannerType.LINK, savedBannerCaptor.value.type) + assertEquals(Lang.JA, savedBannerCaptor.value.lang) + } +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/api/home/HomeServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/api/home/HomeServiceTest.kt new file mode 100644 index 00000000..245e857f --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/api/home/HomeServiceTest.kt @@ -0,0 +1,314 @@ +package kr.co.vividnext.sodalive.api.home + +import kr.co.vividnext.sodalive.audition.AuditionService +import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService +import kr.co.vividnext.sodalive.content.AudioContentService +import kr.co.vividnext.sodalive.content.ContentType +import kr.co.vividnext.sodalive.content.SortType +import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService +import kr.co.vividnext.sodalive.content.series.ContentSeriesService +import kr.co.vividnext.sodalive.content.theme.AudioContentThemeService +import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek +import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository +import kr.co.vividnext.sodalive.i18n.Lang +import kr.co.vividnext.sodalive.i18n.LangContext +import kr.co.vividnext.sodalive.live.room.LiveRoomService +import kr.co.vividnext.sodalive.live.room.LiveRoomStatus +import kr.co.vividnext.sodalive.query.recommend.RecommendChannelQueryService +import kr.co.vividnext.sodalive.rank.ContentRankingSortType +import kr.co.vividnext.sodalive.rank.RankingRepository +import kr.co.vividnext.sodalive.rank.RankingService +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.mockito.Mockito +import org.springframework.data.domain.Pageable +import java.time.DayOfWeek +import java.time.LocalDateTime +import java.time.ZoneId + +class HomeServiceTest { + private lateinit var liveRoomService: LiveRoomService + private lateinit var auditionService: AuditionService + private lateinit var seriesService: ContentSeriesService + private lateinit var contentService: AudioContentService + private lateinit var bannerService: AudioContentBannerService + private lateinit var contentThemeService: AudioContentThemeService + private lateinit var recommendChannelService: RecommendChannelQueryService + private lateinit var characterService: ChatCharacterService + private lateinit var rankingService: RankingService + private lateinit var rankingRepository: RankingRepository + private lateinit var explorerQueryRepository: ExplorerQueryRepository + private lateinit var service: HomeService + private val timezone = "Asia/Seoul" + + @BeforeEach + fun setUp() { + liveRoomService = Mockito.mock(LiveRoomService::class.java) + auditionService = Mockito.mock(AuditionService::class.java) + seriesService = Mockito.mock(ContentSeriesService::class.java) + contentService = Mockito.mock(AudioContentService::class.java) + bannerService = Mockito.mock(AudioContentBannerService::class.java) + contentThemeService = Mockito.mock(AudioContentThemeService::class.java) + recommendChannelService = Mockito.mock(RecommendChannelQueryService::class.java) + characterService = Mockito.mock(ChatCharacterService::class.java) + rankingService = Mockito.mock(RankingService::class.java) + rankingRepository = Mockito.mock(RankingRepository::class.java) + explorerQueryRepository = Mockito.mock(ExplorerQueryRepository::class.java) + service = HomeService( + liveRoomService = liveRoomService, + auditionService = auditionService, + seriesService = seriesService, + contentService = contentService, + bannerService = bannerService, + contentThemeService = contentThemeService, + recommendChannelService = recommendChannelService, + characterService = characterService, + rankingService = rankingService, + rankingRepository = rankingRepository, + explorerQueryRepository = explorerQueryRepository, + langContext = LangContext().apply { setLang(Lang.JA) }, + memberContentPreferenceService = Mockito.mock( + kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService::class.java + ), + imageHost = "https://cdn.test" + ) + + val systemTime = LocalDateTime.now() + val zonedDateTime = systemTime + .atZone(ZoneId.systemDefault()) + .withZoneSameInstant(ZoneId.of(timezone)) + val expectedDayOfWeek = when (zonedDateTime.dayOfWeek) { + DayOfWeek.MONDAY -> SeriesPublishedDaysOfWeek.MON + DayOfWeek.TUESDAY -> SeriesPublishedDaysOfWeek.TUE + DayOfWeek.WEDNESDAY -> SeriesPublishedDaysOfWeek.WED + DayOfWeek.THURSDAY -> SeriesPublishedDaysOfWeek.THU + DayOfWeek.FRIDAY -> SeriesPublishedDaysOfWeek.FRI + DayOfWeek.SATURDAY -> SeriesPublishedDaysOfWeek.SAT + DayOfWeek.SUNDAY -> SeriesPublishedDaysOfWeek.SUN + null -> SeriesPublishedDaysOfWeek.RANDOM + } + val currentDateTime = LocalDateTime.now() + val rankingStartDate = currentDateTime + .withHour(15) + .withMinute(0) + .withSecond(0) + .minusWeeks(1) + .with(java.time.temporal.TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val rankingEndDate = rankingStartDate.plusDays(6) + + Mockito.doReturn(emptyList()).`when`(liveRoomService) + .getRoomList(null, LiveRoomStatus.NOW, Pageable.ofSize(10), null, timezone) + Mockito.`when`(rankingRepository.getCreatorRankings(null)).thenReturn(emptyList()) + Mockito.`when`( + contentThemeService.getActiveThemeOfContent( + false, + false, + false, + ContentType.ALL, + listOf("다시듣기") + ) + ).thenReturn(emptyList()) + Mockito.`when`( + contentThemeService.getActiveThemeOfContent( + false, + true, + false, + ContentType.ALL, + emptyList() + ) + ).thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 0, + 20, + SortType.NEWEST, + false, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 0, + 50, + SortType.NEWEST, + true, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 50, + 100, + SortType.NEWEST, + true, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 150, + 150, + SortType.NEWEST, + true, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 0, + 50, + SortType.NEWEST, + false, + false, + false, + true, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 50, + 100, + SortType.NEWEST, + false, + false, + false, + true, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 150, + 150, + SortType.NEWEST, + false, + false, + false, + true, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 0, + 50, + SortType.NEWEST, + false, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 50, + 100, + SortType.NEWEST, + false, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`( + contentService.getLatestContentByTheme( + null, + emptyList(), + ContentType.ALL, + 150, + 150, + SortType.NEWEST, + false, + false, + false, + false, + emptyList() + ) + ) + .thenReturn(emptyList()) + Mockito.`when`(bannerService.getBannerList(1L, null, false, Lang.JA)).thenReturn(emptyList()) + Mockito.`when`( + seriesService.getOriginalAudioDramaList(null, false, ContentType.ALL, 0, 20) + ).thenReturn(emptyList()) + Mockito.`when`(auditionService.getInProgressAuditionList(false)).thenReturn(emptyList()) + Mockito.`when`( + seriesService.getDayOfWeekSeriesList(null, false, ContentType.ALL, expectedDayOfWeek, 0, 10) + ).thenReturn(emptyList()) + Mockito.`when`(characterService.getPopularCharacters("ja", 20)).thenReturn(emptyList()) + Mockito.`when`( + rankingService.getContentRanking( + null, + false, + ContentType.ALL, + rankingStartDate.minusDays(1), + rankingEndDate, + 0, + 12, + ContentRankingSortType.REVENUE, + "" + ) + ) + .thenReturn(emptyList()) + Mockito.`when`(recommendChannelService.getRecommendChannel(null, false, ContentType.ALL)).thenReturn(emptyList()) + } + + @Test + @DisplayName("홈 fetchData는 현재 요청 언어를 배너 조회에 전달한다") + fun shouldPassCurrentLangToBannerServiceWhenFetchingHome() { + service.fetchData(timezone = timezone, member = null) + + Mockito.verify(bannerService).getBannerList(1L, null, false, Lang.JA) + } +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepositoryTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepositoryTest.kt new file mode 100644 index 00000000..ddf5792b --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/content/main/banner/AudioContentBannerRepositoryTest.kt @@ -0,0 +1,45 @@ +package kr.co.vividnext.sodalive.content.main.banner + +import kr.co.vividnext.sodalive.configs.QueryDslConfig +import kr.co.vividnext.sodalive.i18n.Lang +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.context.annotation.Import + +@DataJpaTest +@Import(QueryDslConfig::class) +class AudioContentBannerRepositoryTest @Autowired constructor( + private val repository: AudioContentBannerRepository +) { + @Test + @DisplayName("사용자 배너 조회는 요청 언어와 일치하는 배너만 반환한다") + fun shouldReturnOnlyRequestedLanguageBanners() { + repository.saveAndFlush( + AudioContentBanner( + thumbnailImage = "banner/ko.png", + type = AudioContentBannerType.LINK, + lang = Lang.KO + ).apply { + link = "https://ko.example.com" + } + ) + repository.saveAndFlush( + AudioContentBanner( + thumbnailImage = "banner/ja.png", + type = AudioContentBannerType.LINK, + lang = Lang.JA + ).apply { + link = "https://ja.example.com" + } + ) + + val banners = repository.getAudioContentMainBannerList(tabId = 1L, isAdult = false, lang = Lang.JA) + + assertEquals(1, banners.size) + assertEquals(Lang.JA, banners.first().lang) + assertEquals("https://ja.example.com", banners.first().link) + } +}