package kr.co.vividnext.sodalive.member import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.i18n.LangContext import kr.co.vividnext.sodalive.i18n.SodaMessageSource import kr.co.vividnext.sodalive.marketing.AdTrackingService import kr.co.vividnext.sodalive.member.contentpreference.MemberContentPreferenceService import kr.co.vividnext.sodalive.member.contentpreference.UpdateMemberContentPreferenceRequest import kr.co.vividnext.sodalive.member.contentpreference.ViewerContentPreference import kr.co.vividnext.sodalive.member.social.SocialAuthServiceResolver import kr.co.vividnext.sodalive.useraction.UserActionService import org.junit.jupiter.api.Assertions.assertEquals 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.mockito.Mockito class MemberControllerTest { private lateinit var memberService: MemberService private lateinit var memberContentPreferenceService: MemberContentPreferenceService private lateinit var socialAuthServiceResolver: SocialAuthServiceResolver private lateinit var trackingService: AdTrackingService private lateinit var userActionService: UserActionService private lateinit var controller: MemberController @BeforeEach fun setup() { memberService = mock() memberContentPreferenceService = mock() socialAuthServiceResolver = mock() trackingService = mock() userActionService = mock() controller = MemberController( service = memberService, memberContentPreferenceService = memberContentPreferenceService, socialAuthServiceResolver = socialAuthServiceResolver, trackingService = trackingService, userActionService = userActionService, messageSource = SodaMessageSource(), langContext = LangContext() ) } @Test @DisplayName("PATCH /member/content-preference는 저장된 최신 설정을 응답한다") fun shouldReturnUpdatedPreferenceWhenRequestIsValid() { val member = createMember(1L) val request = UpdateMemberContentPreferenceRequest( isAdultContentVisible = true, contentType = ContentType.FEMALE ) val viewerPreference = ViewerContentPreference( countryCode = "KR", isAdultContentVisible = true, contentType = ContentType.FEMALE, isAdult = true ) Mockito.`when`( memberContentPreferenceService.updatePreference( member = member, isAdultContentVisible = true, contentType = ContentType.FEMALE ) ).thenReturn(viewerPreference) val response = controller.updateContentPreference(request, member) assertTrue(response.success) assertEquals(true, response.data?.isAdultContentVisible) assertEquals(ContentType.FEMALE, response.data?.contentType) } @Test @DisplayName("비로그인 사용자는 PATCH /member/content-preference 호출 시 예외가 발생한다") fun shouldThrowWhenMemberIsNullOnUpdateContentPreference() { val request = UpdateMemberContentPreferenceRequest( isAdultContentVisible = true, contentType = ContentType.ALL ) val exception = assertThrows(SodaException::class.java) { controller.updateContentPreference(request, null) } assertEquals("common.error.bad_credentials", exception.messageKey) Mockito.verifyNoInteractions(memberContentPreferenceService) } @Test @DisplayName("두 필드 모두 누락된 PATCH 요청은 서비스 예외를 그대로 전파한다") fun shouldPropagateServiceExceptionWhenBothFieldsAreMissing() { val member = createMember(2L) val request = UpdateMemberContentPreferenceRequest( isAdultContentVisible = null, contentType = null ) Mockito.`when`( memberContentPreferenceService.updatePreference( member = member, isAdultContentVisible = null, contentType = null ) ).thenThrow(SodaException(messageKey = "common.error.invalid_request")) val exception = assertThrows(SodaException::class.java) { controller.updateContentPreference(request, member) } assertEquals("common.error.invalid_request", exception.messageKey) } private fun createMember(id: Long): Member { val member = Member( email = "member$id@test.com", password = "password", nickname = "member$id" ) member.id = id return member } private inline fun mock(): T { return Mockito.mock(T::class.java) } }