Test #455
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.member.auth
|
|||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
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.Member
|
||||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
import kr.co.vividnext.sodalive.useraction.ActionType
|
||||||
@@ -34,7 +35,11 @@ class AuthController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val authResponse = service.authenticate(authenticateData, member.id!!)
|
val authResponse = service.authenticate(authenticateData, member.id!!)
|
||||||
memberContentPreferenceService.markAdultVisibleAfterAuthVerify(member.id!!)
|
memberContentPreferenceService.markAdultVisibleAfterAuthVerify(
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdultContentVisible = request.isAdultContentVisible ?: true,
|
||||||
|
contentType = request.contentType ?: ContentType.ALL
|
||||||
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
userActionService.recordAction(
|
userActionService.recordAction(
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
package kr.co.vividnext.sodalive.member.auth
|
package kr.co.vividnext.sodalive.member.auth
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
|
|
||||||
data class AuthVerifyRequest(
|
data class AuthVerifyRequest(
|
||||||
@JsonProperty("receipt_id")
|
@JsonProperty("receipt_id")
|
||||||
val receiptId: String,
|
val receiptId: String,
|
||||||
@JsonProperty("version")
|
@JsonProperty("version")
|
||||||
val version: String?
|
val version: String?,
|
||||||
|
@JsonProperty("isAdultContentVisible")
|
||||||
|
val isAdultContentVisible: Boolean? = null,
|
||||||
|
@JsonProperty("contentType")
|
||||||
|
val contentType: ContentType? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class AuthVerifyCertificate(
|
data class AuthVerifyCertificate(
|
||||||
|
|||||||
+18
-4
@@ -129,14 +129,28 @@ class MemberContentPreferenceService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun markAdultVisibleAfterAuthVerify(memberId: Long) {
|
fun markAdultVisibleAfterAuthVerify(
|
||||||
|
memberId: Long,
|
||||||
|
isAdultContentVisible: Boolean = true,
|
||||||
|
contentType: ContentType = ContentType.ALL
|
||||||
|
) {
|
||||||
val member = memberRepository.findByIdOrNull(memberId)
|
val member = memberRepository.findByIdOrNull(memberId)
|
||||||
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
?: throw SodaException(messageKey = "common.error.bad_credentials")
|
||||||
|
|
||||||
val preference = initializeDefaultPreference(member)
|
val preference = initializeDefaultPreference(member)
|
||||||
if (!preference.isAdultContentVisible) {
|
val now = LocalDateTime.now()
|
||||||
preference.isAdultContentVisible = true
|
var hasChanged = false
|
||||||
preference.adultContentVisibilityChangedAt = LocalDateTime.now()
|
if (preference.isAdultContentVisible != isAdultContentVisible) {
|
||||||
|
preference.isAdultContentVisible = isAdultContentVisible
|
||||||
|
preference.adultContentVisibilityChangedAt = now
|
||||||
|
hasChanged = true
|
||||||
|
}
|
||||||
|
if (preference.contentType != contentType) {
|
||||||
|
preference.contentType = contentType
|
||||||
|
preference.contentTypeChangedAt = now
|
||||||
|
hasChanged = true
|
||||||
|
}
|
||||||
|
if (hasChanged) {
|
||||||
evictRecommendLiveCacheAfterCommit(memberId)
|
evictRecommendLiveCacheAfterCommit(memberId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
package kr.co.vividnext.sodalive.member.auth
|
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.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
import kr.co.vividnext.sodalive.member.Member
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService
|
||||||
import kr.co.vividnext.sodalive.useraction.ActionType
|
import kr.co.vividnext.sodalive.useraction.ActionType
|
||||||
import kr.co.vividnext.sodalive.useraction.UserActionService
|
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.assertThrows
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.Test
|
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
|
import org.mockito.Mockito
|
||||||
|
|
||||||
class AuthControllerTest {
|
class AuthControllerTest {
|
||||||
@@ -31,7 +40,7 @@ class AuthControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("authVerify 성공 시 성인노출 true 저장을 호출한다")
|
@DisplayName("authVerify 성공 시 입력이 없으면 true/ALL 저장을 호출한다")
|
||||||
fun shouldSaveAdultPreferenceWhenAuthVerifySucceeds() {
|
fun shouldSaveAdultPreferenceWhenAuthVerifySucceeds() {
|
||||||
val member = createMember(id = 10L)
|
val member = createMember(id = 10L)
|
||||||
val request = AuthVerifyRequest(receiptId = "receipt-1", version = "v1")
|
val request = AuthVerifyRequest(receiptId = "receipt-1", version = "v1")
|
||||||
@@ -47,9 +56,13 @@ class AuthControllerTest {
|
|||||||
Mockito.`when`(authService.isBlockAuth(certificate)).thenReturn(false)
|
Mockito.`when`(authService.isBlockAuth(certificate)).thenReturn(false)
|
||||||
Mockito.`when`(authService.authenticate(certificate, 10L)).thenReturn(AuthResponse(gender = 1))
|
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(
|
Mockito.verify(userActionService).recordAction(
|
||||||
memberId = 10L,
|
memberId = 10L,
|
||||||
isAuth = true,
|
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
|
@Test
|
||||||
@DisplayName("차단 정책으로 authVerify가 실패하면 저장을 호출하지 않는다")
|
@DisplayName("차단 정책으로 authVerify가 실패하면 저장을 호출하지 않는다")
|
||||||
fun shouldNotSaveAdultPreferenceWhenAuthIsBlocked() {
|
fun shouldNotSaveAdultPreferenceWhenAuthIsBlocked() {
|
||||||
val member = createMember(id = 20L)
|
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(
|
val certificate = AuthVerifyCertificate(
|
||||||
name = "홍길동",
|
name = "홍길동",
|
||||||
birth = "19900101",
|
birth = "19900101",
|
||||||
@@ -73,12 +153,14 @@ class AuthControllerTest {
|
|||||||
Mockito.`when`(authService.certificate(request, memberId = 20L)).thenReturn(certificate)
|
Mockito.`when`(authService.certificate(request, memberId = 20L)).thenReturn(certificate)
|
||||||
Mockito.`when`(authService.isBlockAuth(certificate)).thenReturn(true)
|
Mockito.`when`(authService.isBlockAuth(certificate)).thenReturn(true)
|
||||||
|
|
||||||
assertThrows(SodaException::class.java) {
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
controller.authVerify(request, member)
|
controller.authVerify(request, member)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertEquals("member.auth.blocked_policy", exception.messageKey)
|
||||||
Mockito.verify(authService).signOut(20L)
|
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
|
@Test
|
||||||
@@ -86,11 +168,12 @@ class AuthControllerTest {
|
|||||||
fun shouldThrowWhenMemberIsNull() {
|
fun shouldThrowWhenMemberIsNull() {
|
||||||
val request = AuthVerifyRequest(receiptId = "receipt-3", version = null)
|
val request = AuthVerifyRequest(receiptId = "receipt-3", version = null)
|
||||||
|
|
||||||
assertThrows(SodaException::class.java) {
|
val exception = assertThrows(SodaException::class.java) {
|
||||||
controller.authVerify(request, null)
|
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 {
|
private fun createMember(id: Long): Member {
|
||||||
|
|||||||
+39
-3
@@ -14,6 +14,8 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
|||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.Test
|
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.beans.factory.annotation.Autowired
|
||||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager
|
import org.springframework.cache.concurrent.ConcurrentMapCacheManager
|
||||||
@@ -166,16 +168,50 @@ class MemberContentPreferenceIntegrationTest @Autowired constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("authVerify 성공 후 markAdultVisibleAfterAuthVerify를 호출하면 저장값이 true로 반영된다")
|
@DisplayName("인증 후 입력이 없으면 기존 false/FEMALE 행이 true/ALL로 저장된다")
|
||||||
fun shouldMarkAdultVisibleAfterAuthVerify() {
|
fun shouldMarkAdultVisibleAfterAuthVerify() {
|
||||||
val member = saveNonForcedMember("auth-verified-user")
|
val member = saveNonForcedMember("auth-verified-user")
|
||||||
countryContext.setCountryCode("US")
|
countryContext.setCountryCode("US")
|
||||||
|
|
||||||
service.updatePreference(member, isAdultContentVisible = false, contentType = ContentType.ALL)
|
service.updatePreference(member, isAdultContentVisible = false, contentType = ContentType.FEMALE)
|
||||||
service.markAdultVisibleAfterAuthVerify(member.id!!)
|
service.markAdultVisibleAfterAuthVerify(member.id!!)
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
|
||||||
val stored = service.getStoredPreference(member)
|
val stored = preferenceRepository.findByMemberId(member.id!!)!!
|
||||||
assertTrue(stored.isAdultContentVisible)
|
assertTrue(stored.isAdultContentVisible)
|
||||||
|
assertEquals(ContentType.ALL, stored.contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"ALL, KR, true", "MALE, KR, true", "FEMALE, KR, true",
|
||||||
|
"ALL, US, true", "MALE, US, true", "FEMALE, US, true",
|
||||||
|
"ALL, KR, false", "MALE, KR, false", "FEMALE, KR, false",
|
||||||
|
"ALL, US, false", "MALE, US, false", "FEMALE, US, false"
|
||||||
|
)
|
||||||
|
@DisplayName("인증 후 false와 모든 콘텐츠 유형은 KR과 해외의 기존 행 및 새 행에 영속화된다")
|
||||||
|
fun shouldPersistExplicitAuthPreferenceAfterReload(
|
||||||
|
contentType: ContentType,
|
||||||
|
countryCode: String,
|
||||||
|
existingRow: Boolean
|
||||||
|
) {
|
||||||
|
val member = saveNonForcedMember("auth-input")
|
||||||
|
countryContext.setCountryCode(countryCode)
|
||||||
|
saveAuth(member)
|
||||||
|
val reloadedMember = memberRepository.findById(member.id!!).orElseThrow()
|
||||||
|
if (existingRow) {
|
||||||
|
service.initializeDefaultPreference(reloadedMember)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(existingRow, preferenceRepository.findByMemberId(member.id!!) != null)
|
||||||
|
service.markAdultVisibleAfterAuthVerify(member.id!!, false, contentType)
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
|
||||||
|
val stored = preferenceRepository.findByMemberId(member.id!!)!!
|
||||||
|
assertFalse(stored.isAdultContentVisible)
|
||||||
|
assertEquals(contentType, stored.contentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+108
@@ -13,10 +13,16 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
|||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.Test
|
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
|
import org.mockito.Mockito
|
||||||
import org.springframework.cache.Cache
|
import org.springframework.cache.Cache
|
||||||
import org.springframework.cache.CacheManager
|
import org.springframework.cache.CacheManager
|
||||||
|
import org.springframework.cache.concurrent.ConcurrentMapCacheManager
|
||||||
import org.springframework.dao.DataIntegrityViolationException
|
import org.springframework.dao.DataIntegrityViolationException
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronization
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationManager
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
@@ -405,6 +411,108 @@ class MemberContentPreferenceServiceTest {
|
|||||||
verifyRecommendLiveCacheEvicted(32L)
|
verifyRecommendLiveCacheEvicted(32L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("인증 후 입력이 없으면 기존 false/FEMALE 설정을 true/ALL로 변경한다")
|
||||||
|
fun shouldResetExistingPreferenceToTrueAndAllAfterAuthVerifyWithoutInputs() {
|
||||||
|
val member = createMember(id = 33L)
|
||||||
|
val preference = createPreference(member).apply { contentType = ContentType.FEMALE }
|
||||||
|
val baselineTime = preference.contentTypeChangedAt
|
||||||
|
Mockito.`when`(memberRepository.findById(33L)).thenReturn(Optional.of(member))
|
||||||
|
Mockito.`when`(repository.findByMemberId(33L)).thenReturn(preference)
|
||||||
|
|
||||||
|
service.markAdultVisibleAfterAuthVerify(33L)
|
||||||
|
|
||||||
|
assertTrue(preference.isAdultContentVisible)
|
||||||
|
assertEquals(ContentType.ALL, preference.contentType)
|
||||||
|
assertTrue(preference.adultContentVisibilityChangedAt.isAfter(baselineTime))
|
||||||
|
assertTrue(preference.contentTypeChangedAt.isAfter(baselineTime))
|
||||||
|
verifyRecommendLiveCacheEvicted(33L)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"false, FEMALE, false, false",
|
||||||
|
"true, FEMALE, true, false",
|
||||||
|
"false, MALE, false, true",
|
||||||
|
"false, ALL, false, true",
|
||||||
|
"true, MALE, true, true",
|
||||||
|
"true, ALL, true, true"
|
||||||
|
)
|
||||||
|
@DisplayName("본인인증 후 설정 저장 메서드는 국가·인증 상태를 재검사하지 않고 요청값을 반영한다")
|
||||||
|
fun shouldApplyAuthInputsAndUpdateOnlyChangedFields(
|
||||||
|
isAdultContentVisible: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
adultVisibilityChanged: Boolean,
|
||||||
|
contentTypeChanged: Boolean
|
||||||
|
) {
|
||||||
|
val member = createMember(id = 34L)
|
||||||
|
countryContext.setCountryCode("KR")
|
||||||
|
val baselineTime = LocalDateTime.of(2025, 1, 1, 0, 0)
|
||||||
|
val preference = MemberContentPreference(false, ContentType.FEMALE, baselineTime, baselineTime)
|
||||||
|
preference.member = member
|
||||||
|
Mockito.`when`(memberRepository.findById(34L)).thenReturn(Optional.of(member))
|
||||||
|
Mockito.`when`(repository.findByMemberId(34L)).thenReturn(preference)
|
||||||
|
|
||||||
|
service.markAdultVisibleAfterAuthVerify(34L, isAdultContentVisible, contentType)
|
||||||
|
|
||||||
|
assertEquals(isAdultContentVisible, preference.isAdultContentVisible)
|
||||||
|
assertEquals(contentType, preference.contentType)
|
||||||
|
if (adultVisibilityChanged) {
|
||||||
|
assertTrue(preference.adultContentVisibilityChangedAt.isAfter(baselineTime))
|
||||||
|
} else {
|
||||||
|
assertEquals(baselineTime, preference.adultContentVisibilityChangedAt)
|
||||||
|
}
|
||||||
|
if (contentTypeChanged) {
|
||||||
|
assertTrue(preference.contentTypeChangedAt.isAfter(baselineTime))
|
||||||
|
} else {
|
||||||
|
assertEquals(baselineTime, preference.contentTypeChangedAt)
|
||||||
|
}
|
||||||
|
if (adultVisibilityChanged || contentTypeChanged) {
|
||||||
|
verifyRecommendLiveCacheEvicted(34L)
|
||||||
|
} else {
|
||||||
|
verifyRecommendLiveCacheNotEvicted(34L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(booleans = [true, false])
|
||||||
|
@DisplayName("인증 선호값 변경은 커밋 후에만 캐시를 제거하고 롤백 시 캐시를 유지한다")
|
||||||
|
fun shouldEvictAuthPreferenceCacheOnlyAfterCommit(committed: Boolean) {
|
||||||
|
val member = createMember(id = 35L)
|
||||||
|
val preference = createPreference(member)
|
||||||
|
Mockito.`when`(memberRepository.findById(35L)).thenReturn(Optional.of(member))
|
||||||
|
Mockito.`when`(repository.findByMemberId(35L)).thenReturn(preference)
|
||||||
|
val realCacheManager = ConcurrentMapCacheManager("cache_ttl_3_hours")
|
||||||
|
val cache = realCacheManager.getCache("cache_ttl_3_hours")!!
|
||||||
|
val keys = listOf("getRecommendLive:35:false", "getRecommendLive:35:true", "getRecommendLive:35")
|
||||||
|
keys.forEach { cache.put(it, "cached") }
|
||||||
|
service = MemberContentPreferenceService(repository, memberRepository, countryContext, realCacheManager)
|
||||||
|
|
||||||
|
TransactionSynchronizationManager.initSynchronization()
|
||||||
|
try {
|
||||||
|
service.markAdultVisibleAfterAuthVerify(35L, false, ContentType.MALE)
|
||||||
|
|
||||||
|
assertFalse(preference.isAdultContentVisible)
|
||||||
|
assertEquals(ContentType.MALE, preference.contentType)
|
||||||
|
keys.forEach { assertEquals("cached", cache.get(it)?.get()) }
|
||||||
|
val synchronizations = TransactionSynchronizationManager.getSynchronizations()
|
||||||
|
assertEquals(1, synchronizations.size)
|
||||||
|
if (committed) {
|
||||||
|
synchronizations.forEach { it.afterCommit() }
|
||||||
|
}
|
||||||
|
val status = if (committed) {
|
||||||
|
TransactionSynchronization.STATUS_COMMITTED
|
||||||
|
} else {
|
||||||
|
TransactionSynchronization.STATUS_ROLLED_BACK
|
||||||
|
}
|
||||||
|
synchronizations.forEach { it.afterCompletion(status) }
|
||||||
|
|
||||||
|
keys.forEach { assertEquals(if (committed) null else "cached", cache.get(it)?.get()) }
|
||||||
|
} finally {
|
||||||
|
TransactionSynchronizationManager.clearSynchronization()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("기존 row가 있으면 legacy 조회 파라미터를 무시하고 저장값을 그대로 사용한다")
|
@DisplayName("기존 row가 있으면 legacy 조회 파라미터를 무시하고 저장값을 그대로 사용한다")
|
||||||
fun shouldIgnoreLegacyParamsWhenPreferenceAlreadyExists() {
|
fun shouldIgnoreLegacyParamsWhenPreferenceAlreadyExists() {
|
||||||
|
|||||||
Reference in New Issue
Block a user