revert(ai-character): 기존 관리자 기능 기반을 제거한다
This commit is contained in:
-162
@@ -1,162 +0,0 @@
|
||||
package kr.co.vividnext.sodalive.content
|
||||
|
||||
import kr.co.vividnext.sodalive.common.CountryContext
|
||||
import kr.co.vividnext.sodalive.configs.SecurityConfig
|
||||
import kr.co.vividnext.sodalive.i18n.LangContext
|
||||
import kr.co.vividnext.sodalive.i18n.SodaMessageSource
|
||||
import kr.co.vividnext.sodalive.jwt.JwtAccessDeniedHandler
|
||||
import kr.co.vividnext.sodalive.jwt.JwtAuthenticationEntryPoint
|
||||
import kr.co.vividnext.sodalive.jwt.TokenProvider
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberAdapter
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||
import org.hamcrest.Matchers.anEmptyMap
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.verify
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
|
||||
import org.springframework.boot.test.mock.mockito.MockBean
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||
import org.springframework.web.bind.annotation.PutMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
|
||||
@WebMvcTest(AudioContentController::class)
|
||||
@Import(SecurityConfig::class, JwtAuthenticationEntryPoint::class, JwtAccessDeniedHandler::class)
|
||||
class AudioContentUploadCompletionContractTest @Autowired constructor(
|
||||
private val mockMvc: MockMvc
|
||||
) {
|
||||
@MockBean
|
||||
private lateinit var service: AudioContentService
|
||||
|
||||
@MockBean
|
||||
private lateinit var memberContentPreferenceService: MemberContentPreferenceService
|
||||
|
||||
@MockBean
|
||||
private lateinit var tokenProvider: TokenProvider
|
||||
|
||||
@MockBean
|
||||
private lateinit var countryContext: CountryContext
|
||||
|
||||
@MockBean
|
||||
private lateinit var langContext: LangContext
|
||||
|
||||
@MockBean
|
||||
private lateinit var sodaMessageSource: SodaMessageSource
|
||||
|
||||
@Test
|
||||
@DisplayName("upload-complete callback은 기존 PUT 경로와 ADMIN/BOT 권한 계약을 유지한다")
|
||||
fun shouldKeepUploadCompleteRouteAndRoles() {
|
||||
val classMapping = AudioContentController::class.java.getAnnotation(RequestMapping::class.java)
|
||||
val method = AudioContentController::class.java.getDeclaredMethod(
|
||||
"uploadComplete",
|
||||
UploadCompleteRequest::class.java,
|
||||
kr.co.vividnext.sodalive.member.Member::class.java
|
||||
)
|
||||
val putMapping = method.getAnnotation(PutMapping::class.java)
|
||||
val preAuthorize = method.getAnnotation(PreAuthorize::class.java)
|
||||
|
||||
assertEquals("/audio-content", classMapping.value.single())
|
||||
assertEquals("/upload-complete", putMapping.value.single())
|
||||
assertEquals("hasAnyRole('ADMIN', 'BOT')", preAuthorize.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("upload-complete request는 기존 contentId/contentPath/duration field를 유지한다")
|
||||
fun shouldKeepUploadCompleteRequestFields() {
|
||||
val request = UploadCompleteRequest(contentId = 1L, contentPath = "1/output.mp3", duration = "00:01:00")
|
||||
|
||||
assertEquals(1L, request.contentId)
|
||||
assertEquals("1/output.mp3", request.contentPath)
|
||||
assertEquals("00:01:00", request.duration)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("upload-complete callback은 기존 성공 응답 surface를 유지한다")
|
||||
fun shouldKeepUploadCompleteSuccessResponseSurface() {
|
||||
givenToken("bot-token", MemberRole.BOT)
|
||||
|
||||
mockMvc.perform(
|
||||
put("/audio-content/upload-complete")
|
||||
.header("Authorization", "Bearer bot-token")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""{"contentId":1,"contentPath":"1/output.mp3","duration":"00:01:00"}""")
|
||||
)
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data").value(anEmptyMap<Any, Any>()))
|
||||
.andExpect(content().json(LEGACY_UPLOAD_COMPLETE_SUCCESS_RESPONSE, true))
|
||||
|
||||
verify(service).uploadComplete(1L, "1/output.mp3", "00:01:00")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("upload-complete callback은 ADMIN/BOT JWT만 허용하고 USER/invalid JWT/익명 요청은 거부한다")
|
||||
fun shouldAuthorizeUploadCompleteWithProductionSecurityChain() {
|
||||
givenToken("admin-token", MemberRole.ADMIN)
|
||||
givenToken("bot-token", MemberRole.BOT)
|
||||
givenToken("user-token", MemberRole.USER)
|
||||
Mockito.`when`(tokenProvider.validateToken("invalid-token")).thenReturn(false)
|
||||
|
||||
mockMvc.perform(uploadCompleteRequest("user-token"))
|
||||
.andExpect(status().isForbidden)
|
||||
|
||||
mockMvc.perform(uploadCompleteRequest("invalid-token"))
|
||||
.andExpect(status().isUnauthorized)
|
||||
|
||||
mockMvc.perform(uploadCompleteRequest("admin-token"))
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
|
||||
mockMvc.perform(uploadCompleteRequest("bot-token"))
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
|
||||
mockMvc.perform(
|
||||
put("/audio-content/upload-complete")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPLOAD_COMPLETE_REQUEST)
|
||||
)
|
||||
.andExpect(status().isUnauthorized)
|
||||
}
|
||||
|
||||
private fun uploadCompleteRequest(token: String) = put("/audio-content/upload-complete")
|
||||
.header("Authorization", "Bearer $token")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(UPLOAD_COMPLETE_REQUEST)
|
||||
|
||||
private fun givenToken(token: String, role: MemberRole) {
|
||||
val member = Member(
|
||||
email = "${role.name.lowercase()}@test.com",
|
||||
password = "password",
|
||||
nickname = role.name.lowercase(),
|
||||
role = role
|
||||
).apply { id = role.ordinal.toLong() + 1 }
|
||||
val authentication = UsernamePasswordAuthenticationToken(
|
||||
MemberAdapter(member),
|
||||
token,
|
||||
MemberAdapter(member).authorities
|
||||
)
|
||||
Mockito.`when`(tokenProvider.validateToken(token)).thenReturn(true)
|
||||
Mockito.`when`(tokenProvider.getAuthentication(token)).thenReturn(authentication)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val UPLOAD_COMPLETE_REQUEST =
|
||||
"""{"contentId":1,"contentPath":"1/output.mp3","duration":"00:01:00"}"""
|
||||
|
||||
private const val LEGACY_UPLOAD_COMPLETE_SUCCESS_RESPONSE =
|
||||
"""{"success":true,"message":null,"data":{},"errorProperty":null}"""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user