feat(series-banner): 시리즈 배너를 언어별로 등록하고 노출한다

This commit is contained in:
2026-04-02 16:59:31 +09:00
parent d3b1f4bcd4
commit 3c32559c5d
10 changed files with 371 additions and 8 deletions

View File

@@ -0,0 +1,108 @@
package kr.co.vividnext.sodalive.admin.content.series.banner
import com.amazonaws.services.s3.AmazonS3Client
import com.fasterxml.jackson.databind.ObjectMapper
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.content.series.main.banner.ContentSeriesBannerService
import kr.co.vividnext.sodalive.content.series.main.banner.SeriesBanner
import kr.co.vividnext.sodalive.creator.admin.content.series.Series
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import org.springframework.mock.web.MockMultipartFile
import java.net.URL
class AdminContentSeriesBannerControllerTest {
private val bannerService = Mockito.mock(ContentSeriesBannerService::class.java)
private val amazonS3Client = Mockito.mock(AmazonS3Client::class.java)
private val s3Uploader = S3Uploader(amazonS3Client)
private val controller = AdminContentSeriesBannerController(
bannerService = bannerService,
s3Uploader = s3Uploader,
langContext = LangContext(),
messageSource = SodaMessageSource(),
s3Bucket = "test-bucket",
imageHost = "https://cdn.test"
)
@Test
fun shouldRegisterJapaneseBannerThroughAdminApi() {
val image = MockMultipartFile("image", "banner.png", "image/png", "image".toByteArray())
val registeredBanner = createBanner(id = 10L, lang = Lang.JA, imagePath = "")
val updatedBanner = createBanner(id = 10L, lang = Lang.JA, imagePath = "")
Mockito.`when`(amazonS3Client.getUrl(Mockito.eq("test-bucket"), Mockito.anyString()))
.thenAnswer { URL("https://cdn.test/${it.arguments[1]}") }
Mockito.`when`(
bannerService.registerBanner(
seriesId = 1L,
imagePath = "",
lang = Lang.JA
)
).thenReturn(registeredBanner)
Mockito.doAnswer {
updatedBanner.apply {
imagePath = it.arguments[1] as String
}
}.`when`(bannerService).updateBanner(Mockito.eq(10L), Mockito.anyString(), Mockito.isNull())
val response = controller.registerBanner(
image = image,
requestString = "{\"seriesId\":1,\"lang\":\"ja\"}"
)
assertTrue(response.success)
assertEquals(10L, response.data?.id)
assertTrue(response.data?.imagePath?.startsWith("https://cdn.test/series_banner/10/") == true)
Mockito.verify(bannerService).registerBanner(1L, "", Lang.JA)
}
@Test
fun shouldDeserializeIso639LanguageCodeToLangEnum() {
val request = ObjectMapper().readValue(
"{\"seriesId\":1,\"lang\":\"en\"}",
kr.co.vividnext.sodalive.admin.content.series.banner.dto.SeriesBannerRegisterRequest::class.java
)
assertEquals(Lang.EN, request.lang)
}
@Test
fun shouldAppendBannerLanguageToSeriesTitleInBannerList() {
val pageable = PageRequest.of(0, 20)
val japaneseBanner = createBanner(id = 12L, lang = Lang.JA, imagePath = "banner/jp.png")
Mockito.`when`(bannerService.getActiveBanners(pageable))
.thenReturn(PageImpl(listOf(japaneseBanner), pageable, 1))
val response = controller.getBannerList(page = 0, size = 20)
assertTrue(response.success)
assertEquals("series-12 (일본어)", response.data?.content?.first()?.seriesTitle)
}
private fun createBanner(id: Long, lang: Lang, imagePath: String): SeriesBanner {
val series = Series(
title = "series-$id",
introduction = "introduction-$id",
languageCode = "ko"
)
series.id = id
return SeriesBanner(
imagePath = imagePath,
series = series,
sortOrder = 1,
lang = lang
).also {
it.id = id
}
}
}

View File

@@ -0,0 +1,105 @@
package kr.co.vividnext.sodalive.content.series.main
import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.series.ContentSeriesService
import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.content.series.main.banner.ContentSeriesBannerService
import kr.co.vividnext.sodalive.content.series.main.banner.SeriesBanner
import kr.co.vividnext.sodalive.creator.admin.content.series.Series
import kr.co.vividnext.sodalive.i18n.Lang
import kr.co.vividnext.sodalive.i18n.LangContext
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
import kr.co.vividnext.sodalive.member.contentpreference.ViewerContentPreference
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
class SeriesMainControllerTest {
private val contentSeriesService = Mockito.mock(ContentSeriesService::class.java)
private val bannerService = Mockito.mock(ContentSeriesBannerService::class.java)
private val langContext = LangContext().apply { setLang(Lang.JA) }
private val memberContentPreferenceService = Mockito.mock(MemberContentPreferenceService::class.java)
private val controller = SeriesMainController(
contentSeriesService = contentSeriesService,
bannerService = bannerService,
langContext = langContext,
memberContentPreferenceService = memberContentPreferenceService,
imageHost = "https://cdn.test"
)
@Test
fun shouldFetchOnlyRequestedLanguageBanners() {
val member = createMember(id = 1L)
val preference = ViewerContentPreference(
countryCode = "KR",
isAdultContentVisible = true,
contentType = ContentType.ALL,
isAdult = true
)
val pageable = PageRequest.of(0, 10)
val japaneseBanner = SeriesBanner(
imagePath = "banner/jp.png",
series = createSeries(id = 10L),
sortOrder = 1,
lang = Lang.JA
).also {
it.id = 100L
}
Mockito.`when`(memberContentPreferenceService.resolveForQuery(member)).thenReturn(preference)
Mockito.`when`(bannerService.getDisplayBanners(pageable, Lang.JA))
.thenReturn(PageImpl(listOf(japaneseBanner), pageable, 1))
Mockito.`when`(
contentSeriesService.getSeriesList(
null,
false,
true,
true,
true,
ContentType.ALL,
member,
0,
20
)
).thenReturn(GetSeriesListResponse(totalCount = 0, items = emptyList()))
Mockito.`when`(
contentSeriesService.getRecommendSeriesList(
true,
ContentType.ALL,
member
)
).thenReturn(emptyList())
val response = controller.fetchData(member)
assertTrue(response.success)
assertEquals(1, response.data?.banners?.size)
assertEquals("series-10", response.data?.banners?.first()?.seriesTitle)
Mockito.verify(bannerService).getDisplayBanners(pageable, Lang.JA)
Mockito.verify(bannerService, Mockito.never()).getActiveBanners(pageable)
}
private fun createMember(id: Long): Member {
return Member(
email = "member-$id@test.com",
password = "password",
nickname = "member-$id"
).also {
it.id = id
}
}
private fun createSeries(id: Long): Series {
return Series(
title = "series-$id",
introduction = "introduction-$id",
languageCode = "ja"
).also {
it.id = id
}
}
}

View File

@@ -0,0 +1,100 @@
package kr.co.vividnext.sodalive.content.series.main.banner
import kr.co.vividnext.sodalive.admin.content.series.AdminContentSeriesRepository
import kr.co.vividnext.sodalive.creator.admin.content.series.Series
import kr.co.vividnext.sodalive.i18n.Lang
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.Mockito
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
class ContentSeriesBannerServiceTest {
private lateinit var bannerRepository: SeriesBannerRepository
private lateinit var seriesRepository: AdminContentSeriesRepository
private lateinit var service: ContentSeriesBannerService
@BeforeEach
fun setUp() {
bannerRepository = Mockito.mock(SeriesBannerRepository::class.java)
seriesRepository = Mockito.mock(AdminContentSeriesRepository::class.java)
service = ContentSeriesBannerService(
bannerRepository = bannerRepository,
seriesRepository = seriesRepository
)
}
@Test
@DisplayName("일본어 배너 등록 요청은 JA 언어값으로 저장한다")
fun shouldRegisterJapaneseBanner() {
val series = createSeries(id = 1L)
Mockito.`when`(seriesRepository.findByIdAndActiveTrue(1L)).thenReturn(series)
Mockito.`when`(bannerRepository.findMaxSortOrder()).thenReturn(2)
Mockito.`when`(bannerRepository.save(Mockito.any(SeriesBanner::class.java))).thenAnswer { it.arguments[0] }
val banner = service.registerBanner(
seriesId = 1L,
imagePath = "banner/jp.png",
lang = Lang.JA
)
assertEquals(Lang.JA, banner.lang)
assertEquals(3, banner.sortOrder)
assertEquals("banner/jp.png", banner.imagePath)
}
@Test
@DisplayName("언어가 없는 배너 등록 요청은 KO로 저장한다")
fun shouldRegisterKoreanBannerWhenLangIsMissing() {
val series = createSeries(id = 2L)
Mockito.`when`(seriesRepository.findByIdAndActiveTrue(2L)).thenReturn(series)
Mockito.`when`(bannerRepository.findMaxSortOrder()).thenReturn(null)
Mockito.`when`(bannerRepository.save(Mockito.any(SeriesBanner::class.java))).thenAnswer { it.arguments[0] }
val banner = service.registerBanner(
seriesId = 2L,
imagePath = "banner/default.png",
lang = null
)
assertEquals(Lang.KO, banner.lang)
assertEquals(1, banner.sortOrder)
}
@Test
@DisplayName("일본어 사용자는 일본어 배너만 조회한다")
fun shouldReturnJapaneseBannersForJapaneseUser() {
val pageable = PageRequest.of(0, 10)
val japaneseBanner = SeriesBanner(
imagePath = "banner/jp.png",
series = createSeries(id = 3L),
sortOrder = 1,
lang = Lang.JA
)
val expectedPage = PageImpl(listOf(japaneseBanner), pageable, 1)
Mockito.`when`(bannerRepository.findByIsActiveTrueAndLangOrderBySortOrderAsc(Lang.JA, pageable))
.thenReturn(expectedPage)
val actual = service.getDisplayBanners(pageable, Lang.JA)
assertEquals(expectedPage, actual)
Mockito.verify(bannerRepository).findByIsActiveTrueAndLangOrderBySortOrderAsc(Lang.JA, pageable)
Mockito.verify(bannerRepository, Mockito.never())
.findByIsActiveTrueOrderBySortOrderAsc(pageable)
}
private fun createSeries(id: Long): Series {
return Series(
title = "series-$id",
introduction = "introduction-$id",
languageCode = "ko"
).also {
it.id = id
}
}
}