diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerIntegrationTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerIntegrationTest.kt new file mode 100644 index 00000000..39aebdc7 --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerIntegrationTest.kt @@ -0,0 +1,113 @@ +package kr.co.vividnext.sodalive.admin.content.series.banner + +import kr.co.vividnext.sodalive.admin.content.series.genre.SeriesGenre +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.member.Member +import kr.co.vividnext.sodalive.member.MemberRole +import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer +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.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user +import org.springframework.test.annotation.DirtiesContext +import org.springframework.test.context.ContextConfiguration +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.transaction.support.TransactionTemplate +import javax.persistence.EntityManager + +@SpringBootTest( + properties = [ + "cloud.aws.cloud-front.host=https://cdn.test", + "spring.datasource.url=jdbc:h2:mem:admin-series-banner-controller-integration;" + + "MODE=MySQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=VALUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" + ] +) +@AutoConfigureMockMvc +@ContextConfiguration(initializers = [EmbeddedRedisInitializer::class]) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class AdminContentSeriesBannerControllerIntegrationTest @Autowired constructor( + private val mockMvc: MockMvc, + private val entityManager: EntityManager, + private val transactionTemplate: TransactionTemplate +) { + @Test + @DisplayName("관리자 시리즈 배너 목록 API는 OSIV off 환경에서 lazy 초기화 예외 없이 응답한다") + fun shouldReturnAdminSeriesBannerListWhenOpenInViewIsDisabled() { + createBannerFixture() + + mockMvc.perform( + get("/admin/audio-content/series/banner/list") + .param("page", "0") + .param("size", "20") + .with(user("admin").roles("ADMIN")) + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.data.totalCount").value(1)) + .andExpect(jsonPath("$.data.content[0].seriesTitle").value("series-admin-banner (일본어)")) + .andExpect(jsonPath("$.data.content[0].imagePath").value("https://cdn.test/banner/jp.png")) + } + + @Test + @DisplayName("관리자 시리즈 배너 상세 API는 OSIV off 환경에서 lazy 초기화 예외 없이 응답한다") + fun shouldReturnAdminSeriesBannerDetailWhenOpenInViewIsDisabled() { + val bannerId = createBannerFixture() + + mockMvc.perform( + get("/admin/audio-content/series/banner/{bannerId}", bannerId) + .with(user("admin").roles("ADMIN")) + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.data.id").value(bannerId)) + .andExpect(jsonPath("$.data.seriesTitle").value("series-admin-banner")) + .andExpect(jsonPath("$.data.imagePath").value("https://cdn.test/banner/jp.png")) + } + + private fun createBannerFixture(): Long { + return transactionTemplate.execute { + val creator = Member( + email = "admin-series-banner-controller@test.com", + password = "password", + nickname = "admin-series-banner-creator", + role = MemberRole.CREATOR + ) + entityManager.persist(creator) + + val genre = SeriesGenre(genre = "admin-series-banner-genre") + entityManager.persist(genre) + + val series = Series( + title = "series-admin-banner", + introduction = "introduction", + languageCode = "ko" + ).apply { + member = creator + this.genre = genre + } + entityManager.persist(series) + + val banner = SeriesBanner( + imagePath = "banner/jp.png", + series = series, + sortOrder = 1, + lang = Lang.JA + ) + entityManager.persist( + banner + ) + + entityManager.flush() + val bannerId = banner.id!! + entityManager.clear() + bannerId + }!! + } +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerTest.kt index dc46b303..95ee6fc6 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/admin/content/series/banner/AdminContentSeriesBannerControllerTest.kt @@ -2,6 +2,8 @@ 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.admin.content.series.banner.dto.SeriesBannerListPageResponse +import kr.co.vividnext.sodalive.admin.content.series.banner.dto.SeriesBannerResponse 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 @@ -13,7 +15,6 @@ 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 @@ -35,7 +36,12 @@ class AdminContentSeriesBannerControllerTest { 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 = "") + val updatedResponse = SeriesBannerResponse( + id = 10L, + imagePath = "https://cdn.test/series_banner/10/banner.png", + seriesId = 10L, + seriesTitle = "series-10" + ) Mockito.`when`(amazonS3Client.getUrl(Mockito.eq("test-bucket"), Mockito.anyString())) .thenAnswer { URL("https://cdn.test/${it.arguments[1]}") } @@ -47,11 +53,14 @@ class AdminContentSeriesBannerControllerTest { 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()) + Mockito.`when`( + bannerService.updateBannerResponse( + bannerId = eqLong(10L), + imagePath = anyStringValue(), + seriesId = nullLongValue(), + imageHost = eqString("https://cdn.test") + ) + ).thenReturn(updatedResponse) val response = controller.registerBanner( image = image, @@ -60,8 +69,52 @@ class AdminContentSeriesBannerControllerTest { assertTrue(response.success) assertEquals(10L, response.data?.id) - assertTrue(response.data?.imagePath?.startsWith("https://cdn.test/series_banner/10/") == true) + assertEquals("https://cdn.test/series_banner/10/banner.png", response.data?.imagePath) Mockito.verify(bannerService).registerBanner(1L, "", Lang.JA) + Mockito.verify(bannerService).updateBannerResponse( + bannerId = eqLong(10L), + imagePath = anyStringValue(), + seriesId = nullLongValue(), + imageHost = eqString("https://cdn.test") + ) + } + + @Test + fun shouldDelegateUpdateBannerResponseToService() { + val image = MockMultipartFile("image", "banner.png", "image/png", "image".toByteArray()) + val serviceResponse = SeriesBannerResponse( + id = 10L, + imagePath = "https://cdn.test/series_banner/10/banner.png", + seriesId = 20L, + seriesTitle = "series-20" + ) + + Mockito.`when`(amazonS3Client.getUrl(Mockito.eq("test-bucket"), Mockito.anyString())) + .thenAnswer { URL("https://cdn.test/${it.arguments[1]}") } + Mockito.`when`(bannerService.getBannerById(10L)).thenReturn(createBanner(id = 10L, lang = Lang.JA, imagePath = "old.png")) + Mockito.`when`( + bannerService.updateBannerResponse( + bannerId = eqLong(10L), + imagePath = anyStringValue(), + seriesId = eqLong(20L), + imageHost = eqString("https://cdn.test") + ) + ).thenReturn(serviceResponse) + + val response = controller.updateBanner( + image = image, + requestString = "{\"bannerId\":10,\"seriesId\":20}" + ) + + assertTrue(response.success) + assertEquals("series-20", response.data?.seriesTitle) + Mockito.verify(bannerService).getBannerById(10L) + Mockito.verify(bannerService).updateBannerResponse( + bannerId = eqLong(10L), + imagePath = anyStringValue(), + seriesId = eqLong(20L), + imageHost = eqString("https://cdn.test") + ) } @Test @@ -77,15 +130,45 @@ class AdminContentSeriesBannerControllerTest { @Test fun shouldAppendBannerLanguageToSeriesTitleInBannerList() { val pageable = PageRequest.of(0, 20) - val japaneseBanner = createBanner(id = 12L, lang = Lang.JA, imagePath = "banner/jp.png") + val serviceResponse = SeriesBannerListPageResponse( + totalCount = 1, + content = listOf( + SeriesBannerResponse( + id = 12L, + imagePath = "https://cdn.test/banner/jp.png", + seriesId = 12L, + seriesTitle = "series-12 (일본어)" + ) + ) + ) - Mockito.`when`(bannerService.getActiveBanners(pageable)) - .thenReturn(PageImpl(listOf(japaneseBanner), pageable, 1)) + Mockito.`when`(bannerService.getActiveBanners(pageable, "https://cdn.test")) + .thenReturn(serviceResponse) val response = controller.getBannerList(page = 0, size = 20) assertTrue(response.success) assertEquals("series-12 (일본어)", response.data?.content?.first()?.seriesTitle) + Mockito.verify(bannerService).getActiveBanners(pageable, "https://cdn.test") + } + + @Test + fun shouldDelegateBannerDetailResponseToService() { + val serviceResponse = SeriesBannerResponse( + id = 12L, + imagePath = "https://cdn.test/banner/jp.png", + seriesId = 12L, + seriesTitle = "series-12" + ) + + Mockito.`when`(bannerService.getBannerDetailResponse(12L, "https://cdn.test")) + .thenReturn(serviceResponse) + + val response = controller.getBannerDetail(bannerId = 12L) + + assertTrue(response.success) + assertEquals("series-12", response.data?.seriesTitle) + Mockito.verify(bannerService).getBannerDetailResponse(12L, "https://cdn.test") } private fun createBanner(id: Long, lang: Lang, imagePath: String): SeriesBanner { @@ -105,4 +188,12 @@ class AdminContentSeriesBannerControllerTest { it.id = id } } + + private fun anyStringValue(): String = Mockito.anyString() ?: "" + + private fun eqLong(value: Long): Long = Mockito.eq(value) + + private fun eqString(value: String): String = Mockito.eq(value) ?: value + + private fun nullLongValue(): Long? = Mockito.isNull() } diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/content/series/main/SeriesMainControllerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/content/series/main/SeriesMainControllerTest.kt index a560232b..ec494527 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/content/series/main/SeriesMainControllerTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/content/series/main/SeriesMainControllerTest.kt @@ -1,11 +1,10 @@ package kr.co.vividnext.sodalive.content.series.main +import kr.co.vividnext.sodalive.admin.content.series.banner.dto.SeriesBannerResponse 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 @@ -15,7 +14,6 @@ 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 { @@ -41,18 +39,16 @@ class SeriesMainControllerTest { 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 - } + val japaneseBanner = SeriesBannerResponse( + id = 100L, + imagePath = "https://cdn.test/banner/jp.png", + seriesId = 10L, + seriesTitle = "series-10" + ) Mockito.`when`(memberContentPreferenceService.resolveForQuery(member)).thenReturn(preference) - Mockito.`when`(bannerService.getDisplayBanners(pageable, Lang.JA)) - .thenReturn(PageImpl(listOf(japaneseBanner), pageable, 1)) + Mockito.`when`(bannerService.getDisplayBannerResponses(pageable, Lang.JA, "https://cdn.test")) + .thenReturn(listOf(japaneseBanner)) Mockito.`when`( contentSeriesService.getSeriesList( null, @@ -79,8 +75,8 @@ class SeriesMainControllerTest { 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) + Mockito.verify(bannerService).getDisplayBannerResponses(pageable, Lang.JA, "https://cdn.test") + Mockito.verify(bannerService, Mockito.never()).getActiveBanners(pageable, "https://cdn.test") } private fun createMember(id: Long): Member { @@ -92,14 +88,4 @@ class SeriesMainControllerTest { it.id = id } } - - private fun createSeries(id: Long): Series { - return Series( - title = "series-$id", - introduction = "introduction-$id", - languageCode = "ja" - ).also { - it.id = id - } - } }