feat(auth): 본인인증 요청의 콘텐츠 선호값을 반영한다

This commit is contained in:
2026-10-12 13:28:14 +09:00
parent 6115792dcd
commit ed3d2ff320
3 changed files with 103 additions and 10 deletions
@@ -1,14 +1,23 @@
package kr.co.vividnext.sodalive.member.auth
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
import kr.co.vividnext.sodalive.useraction.ActionType
import kr.co.vividnext.sodalive.useraction.UserActionService
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertSame
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
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.junit.jupiter.params.provider.ValueSource
import org.mockito.Mockito
class AuthControllerTest {
@@ -31,7 +40,7 @@ class AuthControllerTest {
}
@Test
@DisplayName("authVerify 성공 시 성인노출 true 저장을 호출한다")
@DisplayName("authVerify 성공 시 입력이 없으면 true/ALL 저장을 호출한다")
fun shouldSaveAdultPreferenceWhenAuthVerifySucceeds() {
val member = createMember(id = 10L)
val request = AuthVerifyRequest(receiptId = "receipt-1", version = "v1")
@@ -47,9 +56,13 @@ class AuthControllerTest {
Mockito.`when`(authService.isBlockAuth(certificate)).thenReturn(false)
Mockito.`when`(authService.authenticate(certificate, 10L)).thenReturn(AuthResponse(gender = 1))
controller.authVerify(request, member)
val response = controller.authVerify(request, member)
Mockito.verify(memberContentPreferenceService).markAdultVisibleAfterAuthVerify(10L)
assertTrue(response.success)
assertEquals(1, response.data?.gender)
val inOrder = Mockito.inOrder(authService, memberContentPreferenceService)
inOrder.verify(authService).authenticate(certificate, 10L)
inOrder.verify(memberContentPreferenceService).markAdultVisibleAfterAuthVerify(10L, true, ContentType.ALL)
Mockito.verify(userActionService).recordAction(
memberId = 10L,
isAuth = true,
@@ -57,11 +70,78 @@ class AuthControllerTest {
)
}
@ParameterizedTest
@CsvSource(
value = [
"{\"receipt_id\":\"receipt-json\",\"version\":\"v1\"}|true|ALL",
"{\"receipt_id\":\"receipt-json\",\"version\":null}|true|ALL",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":null,\"contentType\":null}|true|ALL",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":false}|false|ALL",
"{\"receipt_id\":\"receipt-json\",\"contentType\":\"ALL\"}|true|ALL",
"{\"receipt_id\":\"receipt-json\",\"contentType\":\"MALE\"}|true|MALE",
"{\"receipt_id\":\"receipt-json\",\"contentType\":\"FEMALE\"}|true|FEMALE",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":null,\"contentType\":\"MALE\"}|true|MALE",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":false,\"contentType\":null}|false|ALL",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":false,\"contentType\":\"ALL\"}|false|ALL",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":false,\"contentType\":\"MALE\"}|false|MALE",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":false,\"contentType\":\"FEMALE\"}|false|FEMALE",
"{\"receipt_id\":\"receipt-json\",\"isAdultContentVisible\":true,\"contentType\":\"FEMALE\"}|true|FEMALE"
],
delimiter = '|'
)
@DisplayName("인증 JSON의 camelCase 선호값을 전달하고 생략과 null은 필드별 true/ALL로 해석한다")
fun shouldApplyJsonPreferenceInputsAfterAuthentication(
json: String,
expectedAdultContentVisible: Boolean,
expectedContentType: ContentType
) {
val member = createMember(id = 11L)
val request = jacksonObjectMapper().readValue<AuthVerifyRequest>(json)
val certificate = AuthVerifyCertificate("홍길동", "19900101", "unique-json", "di-json", 1)
Mockito.`when`(authService.certificate(request, 11L)).thenReturn(certificate)
Mockito.`when`(authService.authenticate(certificate, 11L)).thenReturn(AuthResponse(gender = 1))
val response = controller.authVerify(request, member)
assertTrue(response.success)
assertEquals(1, response.data?.gender)
val inOrder = Mockito.inOrder(authService, memberContentPreferenceService)
inOrder.verify(authService).authenticate(certificate, 11L)
inOrder.verify(memberContentPreferenceService).markAdultVisibleAfterAuthVerify(
11L,
expectedAdultContentVisible,
expectedContentType
)
}
@ParameterizedTest
@ValueSource(strings = ["certificate", "authenticate"])
@DisplayName("인증서 검증 또는 authenticate 실패 시 선호 설정을 쓰지 않는다")
fun shouldNotWritePreferenceWhenAuthenticationFails(failingStep: String) {
val member = createMember(id = 12L)
val request = AuthVerifyRequest("receipt-failure", null, false, ContentType.FEMALE)
val certificate = AuthVerifyCertificate("홍길동", "19900101", "unique-failure", "di-failure", 1)
val failure = SodaException(messageKey = "member.auth.certificate_invalid_retry")
if (failingStep == "certificate") {
Mockito.`when`(authService.certificate(request, 12L)).thenThrow(failure)
} else {
Mockito.`when`(authService.certificate(request, 12L)).thenReturn(certificate)
Mockito.`when`(authService.authenticate(certificate, 12L)).thenThrow(failure)
}
val exception = assertThrows(SodaException::class.java) {
controller.authVerify(request, member)
}
assertSame(failure, exception)
Mockito.verifyNoInteractions(memberContentPreferenceService, userActionService)
}
@Test
@DisplayName("차단 정책으로 authVerify가 실패하면 저장을 호출하지 않는다")
fun shouldNotSaveAdultPreferenceWhenAuthIsBlocked() {
val member = createMember(id = 20L)
val request = AuthVerifyRequest(receiptId = "receipt-2", version = null)
val request = AuthVerifyRequest("receipt-2", null, false, ContentType.MALE)
val certificate = AuthVerifyCertificate(
name = "홍길동",
birth = "19900101",
@@ -73,12 +153,14 @@ class AuthControllerTest {
Mockito.`when`(authService.certificate(request, memberId = 20L)).thenReturn(certificate)
Mockito.`when`(authService.isBlockAuth(certificate)).thenReturn(true)
assertThrows(SodaException::class.java) {
val exception = assertThrows(SodaException::class.java) {
controller.authVerify(request, member)
}
assertEquals("member.auth.blocked_policy", exception.messageKey)
Mockito.verify(authService).signOut(20L)
Mockito.verify(memberContentPreferenceService, Mockito.never()).markAdultVisibleAfterAuthVerify(Mockito.anyLong())
Mockito.verify(authService, Mockito.never()).authenticate(certificate, 20L)
Mockito.verifyNoInteractions(memberContentPreferenceService, userActionService)
}
@Test
@@ -86,11 +168,12 @@ class AuthControllerTest {
fun shouldThrowWhenMemberIsNull() {
val request = AuthVerifyRequest(receiptId = "receipt-3", version = null)
assertThrows(SodaException::class.java) {
val exception = assertThrows(SodaException::class.java) {
controller.authVerify(request, null)
}
Mockito.verifyNoInteractions(memberContentPreferenceService)
assertEquals("common.error.bad_credentials", exception.messageKey)
Mockito.verifyNoInteractions(authService, memberContentPreferenceService, userActionService)
}
private fun createMember(id: Long): Member {