feat(event): v2 일반 이벤트 상세 조회를 추가한다
This commit is contained in:
@@ -188,6 +188,7 @@ class SecurityConfig(
|
|||||||
.antMatchers("/live/recommend/channel").permitAll()
|
.antMatchers("/live/recommend/channel").permitAll()
|
||||||
.antMatchers(HttpMethod.GET, "/live/room").permitAll()
|
.antMatchers(HttpMethod.GET, "/live/room").permitAll()
|
||||||
.antMatchers(HttpMethod.GET, "/event").permitAll()
|
.antMatchers(HttpMethod.GET, "/event").permitAll()
|
||||||
|
.antMatchers(HttpMethod.GET, "/api/v2/events/*").permitAll()
|
||||||
.antMatchers(HttpMethod.GET, "/live/recommend").permitAll()
|
.antMatchers(HttpMethod.GET, "/live/recommend").permitAll()
|
||||||
.antMatchers("/ad-tracking/app-launch").permitAll()
|
.antMatchers("/ad-tracking/app-launch").permitAll()
|
||||||
.antMatchers(HttpMethod.GET, "/notice/latest").permitAll()
|
.antMatchers(HttpMethod.GET, "/notice/latest").permitAll()
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.event.adapter.`in`.web
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.event.application.EventDetailService
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v2/events")
|
||||||
|
class EventDetailController(private val service: EventDetailService) {
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
fun getEvent(
|
||||||
|
@PathVariable id: Long,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = ApiResponse.ok(service.getEvent(id, member))
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.event.application
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.event.EventItem
|
||||||
|
import kr.co.vividnext.sodalive.event.EventRepository
|
||||||
|
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.member.auth.AuthRepository
|
||||||
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.data.repository.findByIdOrNull
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class EventDetailService(
|
||||||
|
private val repository: EventRepository,
|
||||||
|
private val authRepository: AuthRepository,
|
||||||
|
private val memberContentPreferenceService: MemberContentPreferenceService,
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val cloudFrontHost: String
|
||||||
|
) {
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
fun getEvent(id: Long, member: Member?): EventItem {
|
||||||
|
if (id <= 0) throw SodaException(messageKey = "common.error.invalid_request")
|
||||||
|
|
||||||
|
val event = repository.findByIdOrNull(id)
|
||||||
|
?: throw SodaException(messageKey = "common.error.invalid_request")
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
val lang = if (memberContentPreferenceService.resolveCountryCode(member) == "JP") Lang.JA else Lang.KO
|
||||||
|
|
||||||
|
if (!event.isActive || event.startDate > now || event.endDate < now || event.lang != lang) {
|
||||||
|
throw SodaException(messageKey = "common.error.invalid_request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (member?.role != MemberRole.ADMIN) {
|
||||||
|
val isAdult = member?.id?.let { authRepository.getAuthIdByMemberId(it) != null } ?: false
|
||||||
|
if (event.isAdult != null && event.isAdult != isAdult) {
|
||||||
|
throw SodaException(messageKey = "common.error.invalid_request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EventItem(
|
||||||
|
id = id,
|
||||||
|
thumbnailImageUrl = imageUrl(event.thumbnailImage),
|
||||||
|
detailImageUrl = event.detailImage?.let { imageUrl(it) },
|
||||||
|
popupImageUrl = event.popupImage?.let { imageUrl(it) },
|
||||||
|
link = event.link
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun imageUrl(image: String): String = if (image.startsWith("https://")) image else "$cloudFrontHost/$image"
|
||||||
|
}
|
||||||
+350
@@ -0,0 +1,350 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.api.event
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.event.Event
|
||||||
|
import kr.co.vividnext.sodalive.event.EventRepository
|
||||||
|
import kr.co.vividnext.sodalive.i18n.Lang
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberAdapter
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.member.auth.Auth
|
||||||
|
import kr.co.vividnext.sodalive.member.auth.AuthRepository
|
||||||
|
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
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.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource
|
||||||
|
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.boot.test.mock.mockito.MockBean
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate
|
||||||
|
import org.springframework.http.HttpEntity
|
||||||
|
import org.springframework.http.HttpHeaders
|
||||||
|
import org.springframework.http.HttpMethod
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication
|
||||||
|
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.MvcResult
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||||
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@SpringBootTest(
|
||||||
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
|
properties = [
|
||||||
|
"cloud.aws.cloud-front.host=https://cdn.test",
|
||||||
|
"cloud.aws.s3.bucket=test-bucket",
|
||||||
|
"spring.datasource.url=jdbc:h2:mem:event-detail-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_CLASS)
|
||||||
|
class EventDetailControllerIntegrationTest @Autowired constructor(
|
||||||
|
private val restTemplate: TestRestTemplate,
|
||||||
|
private val mockMvc: MockMvc,
|
||||||
|
private val eventRepository: EventRepository,
|
||||||
|
private val memberRepository: MemberRepository,
|
||||||
|
private val authRepository: AuthRepository,
|
||||||
|
private val objectMapper: ObjectMapper
|
||||||
|
) {
|
||||||
|
@MockBean
|
||||||
|
private lateinit var s3Uploader: S3Uploader
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
@AfterEach
|
||||||
|
fun cleanUp() {
|
||||||
|
eventRepository.deleteAllInBatch()
|
||||||
|
authRepository.deleteAllInBatch()
|
||||||
|
memberRepository.deleteAllInBatch()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("익명 상세는 기존 목록과 같은 5개 필드와 외부 링크를 반환하고 엔티티를 변경하지 않는다")
|
||||||
|
fun shouldReturnAnonymousEventDetailWithLegacyFieldsWithoutMutatingTheEntity() {
|
||||||
|
val event = saveEvent(
|
||||||
|
activeEvent().apply {
|
||||||
|
detailImage = null
|
||||||
|
popupImage = null
|
||||||
|
link = "https://external.test/promotion?from=soda&target=event#detail"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val detail = httpGet("/api/v2/events/${event.id}")
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
objectMapper.readTree(
|
||||||
|
"""{"success":true,"message":null,"data":{
|
||||||
|
"id":${event.id},"thumbnailImageUrl":"https://cdn.test/thumbnail.png",
|
||||||
|
"detailImageUrl":null,"popupImageUrl":null,
|
||||||
|
"link":"https://external.test/promotion?from=soda&target=event#detail"
|
||||||
|
},"errorProperty":null}"""
|
||||||
|
),
|
||||||
|
detail
|
||||||
|
)
|
||||||
|
assertEquals(httpGet("/event")["data"]["eventList"].single(), detail["data"])
|
||||||
|
val persisted = eventRepository.findById(requireNotNull(event.id)).orElseThrow()
|
||||||
|
assertEquals("thumbnail.png", persisted.thumbnailImage)
|
||||||
|
assertEquals(null, persisted.detailImage)
|
||||||
|
assertEquals(null, persisted.popupImage)
|
||||||
|
assertEquals(event.link, persisted.link)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"https://images.test/image.png, https://images.test/image.png",
|
||||||
|
"relative/image.png, https://cdn.test/relative/image.png",
|
||||||
|
"http://images.test/image.png, https://cdn.test/http://images.test/image.png"
|
||||||
|
)
|
||||||
|
@DisplayName("세 이미지 필드는 HTTPS만 보존하고 상대 경로와 HTTP는 기존 CDN 규칙을 따른다")
|
||||||
|
fun shouldMapAllImageFieldsLikeTheLegacyList(image: String, expectedImage: String) {
|
||||||
|
val event = saveEvent(
|
||||||
|
activeEvent().apply {
|
||||||
|
thumbnailImage = image
|
||||||
|
detailImage = image
|
||||||
|
popupImage = image
|
||||||
|
link = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val detail = httpGet("/api/v2/events/${event.id}")["data"]
|
||||||
|
|
||||||
|
assertEquals(5, detail.size())
|
||||||
|
assertEquals(event.id, detail["id"].asLong())
|
||||||
|
assertEquals(expectedImage, detail["thumbnailImageUrl"].asText())
|
||||||
|
assertEquals(expectedImage, detail["detailImageUrl"].asText())
|
||||||
|
assertEquals(expectedImage, detail["popupImageUrl"].asText())
|
||||||
|
assertEquals(true, detail["link"].isNull)
|
||||||
|
assertEquals(httpGet("/event")["data"]["eventList"].single(), detail)
|
||||||
|
val persisted = eventRepository.findById(requireNotNull(event.id)).orElseThrow()
|
||||||
|
assertEquals(image, persisted.thumbnailImage)
|
||||||
|
assertEquals(image, persisted.detailImage)
|
||||||
|
assertEquals(image, persisted.popupImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"DEFAULT, KO",
|
||||||
|
"KR, KO",
|
||||||
|
"JP, JA",
|
||||||
|
"US, KO",
|
||||||
|
nullValues = ["DEFAULT"]
|
||||||
|
)
|
||||||
|
@DisplayName("익명 상세의 국가별 언어와 비노출 응답은 기존 목록과 일치한다")
|
||||||
|
fun shouldApplyTheSameCountryLanguageAsTheLegacyList(country: String?, expectedLang: Lang) {
|
||||||
|
val events = listOf(Lang.KO, Lang.JA, Lang.EN).map { lang ->
|
||||||
|
saveEvent(activeEvent().apply { this.lang = lang })
|
||||||
|
}
|
||||||
|
val expected = events.single { it.lang == expectedLang }
|
||||||
|
val legacyItems = httpGet("/event", country)["data"]["eventList"]
|
||||||
|
assertEquals(listOf(expected.id), legacyItems.map { it["id"].asLong() })
|
||||||
|
|
||||||
|
events.forEach { event ->
|
||||||
|
val detail = httpGet("/api/v2/events/${event.id}", country)
|
||||||
|
if (event.id == expected.id) {
|
||||||
|
assertEquals(true, detail["success"].asBoolean())
|
||||||
|
assertEquals(legacyItems.single(), detail["data"])
|
||||||
|
} else {
|
||||||
|
assertInvalidRequest(detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("성인 null false true의 익명 미인증 인증 관리자 노출은 KO와 JA 모두 기존 목록과 같다")
|
||||||
|
fun shouldMatchTheLegacyAdultMatrixForAnonymousUnverifiedVerifiedAndAdminViewers() {
|
||||||
|
val unverified = saveMember("unverified")
|
||||||
|
val verified = saveMember("verified", verified = true)
|
||||||
|
val admin = saveMember("admin", role = MemberRole.ADMIN)
|
||||||
|
assertEquals(null, authRepository.getAuthIdByMemberId(requireNotNull(unverified.id)))
|
||||||
|
assertEquals(1L, authRepository.count())
|
||||||
|
|
||||||
|
listOf("KR" to Lang.KO, "JP" to Lang.JA).forEach { (country, lang) ->
|
||||||
|
val general = saveEvent(activeEvent().apply { this.lang = lang })
|
||||||
|
val nonAdult = saveEvent(
|
||||||
|
activeEvent().apply {
|
||||||
|
this.lang = lang
|
||||||
|
isAdult = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val adult = saveEvent(
|
||||||
|
activeEvent().apply {
|
||||||
|
this.lang = lang
|
||||||
|
isAdult = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val events = listOf(general, nonAdult, adult)
|
||||||
|
val viewers = listOf(
|
||||||
|
null to setOf(general.id, nonAdult.id),
|
||||||
|
unverified to setOf(general.id, nonAdult.id),
|
||||||
|
verified to setOf(general.id, adult.id),
|
||||||
|
admin to setOf(general.id, nonAdult.id, adult.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
viewers.forEach { (member, expectedIds) ->
|
||||||
|
val legacyItems = viewerGet("/event", member, country)["data"]["eventList"]
|
||||||
|
assertEquals(expectedIds, legacyItems.map { it["id"].asLong() }.toSet())
|
||||||
|
events.forEach { event ->
|
||||||
|
val detail = viewerGet("/api/v2/events/${event.id}", member, country)
|
||||||
|
if (event.id in expectedIds) {
|
||||||
|
assertEquals(true, detail["success"].asBoolean())
|
||||||
|
assertEquals(legacyItems.single { it["id"].asLong() == event.id }, detail["data"])
|
||||||
|
} else {
|
||||||
|
assertInvalidRequest(detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("관리자도 활성 기간 언어 조건을 우회하지 못하고 비노출 상세는 미존재와 같다")
|
||||||
|
fun shouldRejectAllHiddenStatesEvenForAdminWhileMatchingTheLegacyList() {
|
||||||
|
val visible = saveEvent(activeEvent())
|
||||||
|
val hidden = listOf(
|
||||||
|
activeEvent().apply { isActive = false },
|
||||||
|
activeEvent().apply {
|
||||||
|
startDate = LocalDateTime.now().plusDays(1)
|
||||||
|
endDate = startDate.plusDays(1)
|
||||||
|
},
|
||||||
|
activeEvent().apply {
|
||||||
|
endDate = LocalDateTime.now().minusDays(1)
|
||||||
|
startDate = endDate.minusDays(1)
|
||||||
|
},
|
||||||
|
activeEvent().apply { lang = Lang.JA },
|
||||||
|
activeEvent().apply { lang = Lang.EN }
|
||||||
|
).map { saveEvent(it) }
|
||||||
|
val unverified = saveMember("hidden-unverified")
|
||||||
|
val verified = saveMember("hidden-verified", verified = true)
|
||||||
|
val admin = saveMember("hidden-admin", role = MemberRole.ADMIN)
|
||||||
|
val missing = httpGet("/api/v2/events/${Long.MAX_VALUE}")
|
||||||
|
assertInvalidRequest(missing)
|
||||||
|
|
||||||
|
listOf(null, unverified, verified, admin).forEach { member ->
|
||||||
|
val legacyItems = viewerGet("/event", member)["data"]["eventList"]
|
||||||
|
assertEquals(listOf(visible.id), legacyItems.map { it["id"].asLong() })
|
||||||
|
hidden.forEach { event ->
|
||||||
|
assertEquals(missing, viewerGet("/api/v2/events/${event.id}", member))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource("0", "-1", "9223372036854775807")
|
||||||
|
@DisplayName("양수가 아니거나 미존재인 ID는 HTTP 200 invalid_request 실패 응답을 반환한다")
|
||||||
|
fun shouldRejectNonpositiveAndMissingIdsWithTheCommonInvalidRequest(id: String) {
|
||||||
|
assertInvalidRequest(httpGet("/api/v2/events/$id"))
|
||||||
|
val result = mockGet("/api/v2/events/$id")
|
||||||
|
assertEquals("common.error.invalid_request", (result.resolvedException as SodaException).messageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource("not-a-number", "9223372036854775808")
|
||||||
|
@DisplayName("숫자가 아니거나 Long 범위를 넘는 ID는 기존 바인딩과 공통 예외 응답을 유지한다")
|
||||||
|
fun shouldKeepTheGlobalBindingErrorResponseForMalformedIds(id: String) {
|
||||||
|
val response = httpGet("/api/v2/events/$id")
|
||||||
|
assertEquals(
|
||||||
|
objectMapper.readTree(
|
||||||
|
"""{"success":false,"message":"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.",
|
||||||
|
"data":null,"errorProperty":null}"""
|
||||||
|
),
|
||||||
|
response
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
MethodArgumentTypeMismatchException::class.java,
|
||||||
|
mockGet("/api/v2/events/$id").resolvedException?.javaClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"POST, /api/v2/events/1", "PUT, /api/v2/events/1", "DELETE, /api/v2/events/1",
|
||||||
|
"GET, /api/v2/events", "POST, /api/v2/events", "GET, /api/v2/events/1/extra",
|
||||||
|
"GET, /api/v2/events/1/extra/deeper", "GET, /event/popup", "GET, /admin/event/banner",
|
||||||
|
"POST, /event", "PUT, /event", "DELETE, /event/1", "GET, /admin/event/banner/1"
|
||||||
|
)
|
||||||
|
@DisplayName("익명 쓰기 collection 하위 경로 기존 팝업 관리자 API는 계속 인증이 필요하다")
|
||||||
|
fun shouldKeepAnonymousRequestsOutsideTheDetailGetProtected(method: HttpMethod, path: String) {
|
||||||
|
val response = restTemplate.exchange(path, method, HttpEntity.EMPTY, String::class.java)
|
||||||
|
println("HTTP $method $path -> ${response.statusCodeValue} ${response.body}")
|
||||||
|
assertEquals(HttpStatus.UNAUTHORIZED, response.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun httpGet(path: String, country: String? = null): JsonNode {
|
||||||
|
val headers = HttpHeaders().apply {
|
||||||
|
country?.let { set("CloudFront-Viewer-Country", it) }
|
||||||
|
}
|
||||||
|
val response = restTemplate.exchange(path, HttpMethod.GET, HttpEntity<Void>(headers), String::class.java)
|
||||||
|
println("HTTP GET $path country=$country -> ${response.statusCodeValue} ${response.body}")
|
||||||
|
assertEquals(HttpStatus.OK, response.statusCode)
|
||||||
|
return objectMapper.readTree(response.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun viewerGet(path: String, member: Member?, country: String? = null): JsonNode {
|
||||||
|
if (member == null) return httpGet(path, country)
|
||||||
|
return objectMapper.readTree(mockGet(path, member, country).response.contentAsByteArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mockGet(path: String, member: Member? = null, country: String? = null): MvcResult {
|
||||||
|
val request = get(path).apply {
|
||||||
|
country?.let { header("CloudFront-Viewer-Country", it) }
|
||||||
|
member?.let {
|
||||||
|
val adapter = MemberAdapter(it)
|
||||||
|
with(authentication(UsernamePasswordAuthenticationToken(adapter, null, adapter.authorities)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val result = mockMvc.perform(request).andReturn()
|
||||||
|
println(
|
||||||
|
"MockMvc GET $path role=${member?.role} country=$country -> ${result.response.status} " +
|
||||||
|
result.response.getContentAsString(Charsets.UTF_8)
|
||||||
|
)
|
||||||
|
assertEquals(200, result.response.status)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertInvalidRequest(response: JsonNode) {
|
||||||
|
assertEquals(
|
||||||
|
objectMapper.readTree(
|
||||||
|
"""{"success":false,"message":"잘못된 요청입니다.","data":null,"errorProperty":null}"""
|
||||||
|
),
|
||||||
|
response
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveMember(seed: String, role: MemberRole = MemberRole.USER, verified: Boolean = false): Member {
|
||||||
|
val member = memberRepository.saveAndFlush(Member(password = "test-password", nickname = seed, role = role))
|
||||||
|
if (verified) {
|
||||||
|
val auth = authRepository.saveAndFlush(
|
||||||
|
Auth("test-member", "19900101", "test-ci-$seed", "test-di-$seed", 1).apply { this.member = member }
|
||||||
|
)
|
||||||
|
assertEquals(auth.id, authRepository.getAuthIdByMemberId(requireNotNull(member.id)))
|
||||||
|
}
|
||||||
|
return member
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveEvent(event: Event): Event = eventRepository.saveAndFlush(event)
|
||||||
|
|
||||||
|
private fun activeEvent(): Event = Event(
|
||||||
|
thumbnailImage = "thumbnail.png",
|
||||||
|
detailImage = "detail.png",
|
||||||
|
popupImage = "popup.png",
|
||||||
|
link = "https://external.test/event",
|
||||||
|
title = "not-returned-title",
|
||||||
|
isAdult = null,
|
||||||
|
isPopup = false,
|
||||||
|
startDate = LocalDateTime.now().minusDays(1),
|
||||||
|
endDate = LocalDateTime.now().plusDays(1)
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user