From a85bc67f7a99e51ea75d985c25b47679cde32752 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 27 Feb 2026 13:57:04 +0900 Subject: [PATCH] =?UTF-8?q?fix(channel-donation):=20=EC=B1=84=EB=84=90=20?= =?UTF-8?q?=ED=9B=84=EC=9B=90=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EA=B0=84?= =?UTF-8?q?=EC=9D=84=20=EC=9B=94=20=EA=B2=BD=EA=B3=84=20=EA=B8=B0=EC=A4=80?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=86=B5=EC=9D=BC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._크리에이터프로필채널후원조회월범위수정.md | 45 ++++++++++++++ .../sodalive/explorer/ExplorerService.kt | 1 + .../ChannelDonationMessageRepository.kt | 22 ++++--- .../channelDonation/ChannelDonationService.kt | 14 +++-- .../ChannelDonationMessageRepositoryTest.kt | 50 ++++++++++++---- .../ChannelDonationServiceTest.kt | 58 ++++++++++++++++++- 6 files changed, 165 insertions(+), 25 deletions(-) create mode 100644 docs/20260227_크리에이터프로필채널후원조회월범위수정.md diff --git a/docs/20260227_크리에이터프로필채널후원조회월범위수정.md b/docs/20260227_크리에이터프로필채널후원조회월범위수정.md new file mode 100644 index 00000000..deccbd7f --- /dev/null +++ b/docs/20260227_크리에이터프로필채널후원조회월범위수정.md @@ -0,0 +1,45 @@ +- [x] `getCreatorProfile`의 채널 후원 리스트 조회 경로 식별 (`ExplorerService` -> `ChannelDonationService` -> `ChannelDonationMessageRepository`) +- [x] 프로필 채널 후원 조회 시 조회 월의 1일~말일 범위만 조회되도록 기간 조건 반영 +- [x] 기존 일반 채널 후원 목록 API 동작 영향 없는지 확인 +- [x] 수정 파일 기준 정적 진단/테스트/빌드 검증 수행 + +## 검증 기록 + +### 1차 구현 +- 무엇을: 크리에이터 프로필의 채널 후원 리스트 조회 기간을 월 단위로 제한 +- 왜: 기존 기간 계산(`now - 1 month`)은 월 경계 기준 요구사항(해당 월 1일~말일)과 다름 +- 어떻게: + - `lsp_diagnostics`(수정 파일 2개) 실행 시 `.kt` 확장자용 LSP 서버 미구성으로 도구 진단 불가(환경 제약 확인) + - `./gradlew test --tests "kr.co.vividnext.sodalive.explorer.profile.channelDonation.*"` 실행: 성공 + - `./gradlew build` 실행: 성공 (ktlint 포함 전체 빌드 통과) + +### 2차 수정 +- 무엇을: 프로필 집계 응답뿐 아니라 전체 채널 후원 리스트 API도 월 단위(1일~말일) 조회로 통일 +- 왜: 요구사항이 프로필 전용이 아닌 전체 채널 후원 리스트 대상까지 확장됨 +- 어떻게: + - `lsp_diagnostics`(수정 파일) 실행 시 `.kt` 확장자용 LSP 서버 미구성으로 도구 진단 불가(환경 제약 확인) + - `./gradlew test --tests "kr.co.vividnext.sodalive.explorer.profile.channelDonation.*"` 실행: 성공 + - `./gradlew build` 실행: 성공 (ktlint 포함 전체 빌드 통과) + +### 3차 수정 +- 무엇을: `endDateTime` nullable 분기와 중복 메서드를 제거하고 기존 조회 메서드 시그니처에 `endDateTime`을 포함해 단일 로직으로 정리 +- 왜: `endDateTime`이 항상 존재하는 현재 요구사항에서 null 분기 로직은 불필요하며 유지보수 복잡도만 증가시킴 +- 어떻게: + - `lsp_diagnostics`(수정 파일) 실행 시 `.kt` 확장자용 LSP 서버 미구성으로 도구 진단 불가(환경 제약 확인) + - `./gradlew test --tests "kr.co.vividnext.sodalive.explorer.profile.channelDonation.*"` 실행: 성공 + - `./gradlew build` 실행: 성공 (ktlint 포함 전체 빌드 통과) + +### 4차 수정 +- 무엇을: `endDateTime` 도입 이후 테스트 의미를 월 경계 의도에 맞게 보강 (`Service`는 월 시작/종료 전달 검증, `Repository`는 월 범위 기반 필터 검증) +- 왜: 기존 테스트 일부는 단순 파라미터 통과 확인 수준이어서 월 경계 요구사항을 직접 담지 못함 +- 어떻게: + - `lsp_diagnostics`(수정 테스트 파일) 실행 시 `.kt` 확장자용 LSP 서버 미구성으로 도구 진단 불가(환경 제약 확인) + - `./gradlew test --tests "kr.co.vividnext.sodalive.explorer.profile.channelDonation.*"` 실행: 성공 + - `./gradlew build` 실행: 성공 (ktlint 포함 전체 빌드 통과) + +### 5차 수정 +- 무엇을: 채널 후원 테스트 2개 파일의 가독성 개선을 위해 `@DisplayName`(한글)과 BDD(`given/when/then`) 단락 설명을 추가 +- 왜: 테스트 코드 길이가 길어지며 의도 파악이 어려워져, 시나리오/준비/실행/검증 흐름을 빠르게 읽을 수 있도록 개선 필요 +- 어떻게: + - `lsp_diagnostics`(수정 테스트 파일) 실행 시 `.kt` 확장자용 LSP 서버 미구성으로 도구 진단 불가(환경 제약 확인) + - `./gradlew test --tests "kr.co.vividnext.sodalive.explorer.profile.channelDonation.*" && ./gradlew build` 실행: 성공 diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt index e8e4453a..f5d39792 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt @@ -395,6 +395,7 @@ class ExplorerService( limit = 4 ) + // 채널 후원 val channelDonationList = if (isCreator && !isBlock) { channelDonationService.getChannelDonationListForProfile( creatorId = creatorId, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepository.kt index dcc5f886..6938c582 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepository.kt @@ -17,14 +17,16 @@ interface ChannelDonationMessageQueryRepository { isCreator: Boolean, offset: Long, limit: Long, - startDateTime: LocalDateTime + startDateTime: LocalDateTime, + endDateTime: LocalDateTime ): List fun getChannelDonationMessageTotalCount( creatorId: Long, memberId: Long, isCreator: Boolean, - startDateTime: LocalDateTime + startDateTime: LocalDateTime, + endDateTime: LocalDateTime ): Int } @@ -37,13 +39,15 @@ class ChannelDonationMessageQueryRepositoryImpl( isCreator: Boolean, offset: Long, limit: Long, - startDateTime: LocalDateTime + startDateTime: LocalDateTime, + endDateTime: LocalDateTime ): List { val where = whereCondition( creatorId = creatorId, memberId = memberId, isCreator = isCreator, - startDateTime = startDateTime + startDateTime = startDateTime, + endDateTime = endDateTime ) return queryFactory @@ -62,13 +66,15 @@ class ChannelDonationMessageQueryRepositoryImpl( creatorId: Long, memberId: Long, isCreator: Boolean, - startDateTime: LocalDateTime + startDateTime: LocalDateTime, + endDateTime: LocalDateTime ): Int { val where = whereCondition( creatorId = creatorId, memberId = memberId, isCreator = isCreator, - startDateTime = startDateTime + startDateTime = startDateTime, + endDateTime = endDateTime ) return queryFactory @@ -83,9 +89,11 @@ class ChannelDonationMessageQueryRepositoryImpl( creatorId: Long, memberId: Long, isCreator: Boolean, - startDateTime: LocalDateTime + startDateTime: LocalDateTime, + endDateTime: LocalDateTime ) = channelDonationMessage.creator.id.eq(creatorId) .and(channelDonationMessage.createdAt.goe(startDateTime)) + .and(channelDonationMessage.createdAt.lt(endDateTime)) .let { if (isCreator) { it diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationService.kt index d1006170..041ed9b2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationService.kt @@ -11,8 +11,9 @@ import kr.co.vividnext.sodalive.member.MemberRole import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import java.time.LocalDateTime +import java.time.LocalDate import java.time.format.DateTimeFormatter +import java.time.temporal.TemporalAdjusters @Service class ChannelDonationService( @@ -61,14 +62,18 @@ class ChannelDonationService( memberRepository.findCreatorByIdOrNull(creatorId) ?: throw SodaException(messageKey = "member.validation.creator_not_found") - val startDateTime = LocalDateTime.now().minusMonths(1) + val startDateTime = LocalDate.now() + .with(TemporalAdjusters.firstDayOfMonth()) + .atStartOfDay() + val endDateTime = startDateTime.plusMonths(1) val isCreator = member.role == MemberRole.CREATOR && creatorId == member.id val totalCount = channelDonationMessageRepository.getChannelDonationMessageTotalCount( creatorId = creatorId, memberId = member.id!!, isCreator = isCreator, - startDateTime = startDateTime + startDateTime = startDateTime, + endDateTime = endDateTime ) val items = channelDonationMessageRepository.getChannelDonationMessageList( @@ -77,7 +82,8 @@ class ChannelDonationService( isCreator = isCreator, offset = offset, limit = limit, - startDateTime = startDateTime + startDateTime = startDateTime, + endDateTime = endDateTime ).map { GetChannelDonationListItem( id = it.id!!, diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepositoryTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepositoryTest.kt index d1738bc0..42502116 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepositoryTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationMessageRepositoryTest.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.member.MemberRole import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -20,44 +21,56 @@ class ChannelDonationMessageRepositoryTest @Autowired constructor( private val entityManager: EntityManager ) { @Test + @DisplayName("일반 사용자 조회 시 월 범위와 공개/비공개 규칙을 적용하고 최신순으로 정렬한다") fun shouldFilterByDateAndSortByCreatedAtAndIdDescForViewer() { + // given: 크리에이터/조회자/타 사용자 데이터를 준비한다. val creator = saveMember(nickname = "creator", role = MemberRole.CREATOR) val viewer = saveMember(nickname = "viewer", role = MemberRole.USER) val otherUser = saveMember(nickname = "other", role = MemberRole.USER) + // given: 조회 기준 월의 시작/종료 시점을 계산한다. val now = LocalDateTime.now() - val tieTime = now.minusDays(2) + val monthStart = now.withDayOfMonth(1).toLocalDate().atStartOfDay() + val nextMonthStart = monthStart.plusMonths(1) + val tieTime = monthStart.plusDays(2) + // given: 공개/비공개 및 월 범위 경계 확인용 메시지를 저장한다. val oldPublic = saveMessage(member = viewer, creator = creator, can = 1, isSecret = false) val publicTieFirst = saveMessage(member = otherUser, creator = creator, can = 2, isSecret = false) val publicTieSecond = saveMessage(member = viewer, creator = creator, can = 3, isSecret = false) val secretMine = saveMessage(member = viewer, creator = creator, can = 4, isSecret = true) val secretOther = saveMessage(member = otherUser, creator = creator, can = 5, isSecret = true) - updateCreatedAt(oldPublic.id!!, now.minusMonths(2)) + // given: createdAt을 직접 조정해 정렬/필터 조건을 명확히 만든다. + updateCreatedAt(oldPublic.id!!, monthStart.minusDays(1)) updateCreatedAt(publicTieFirst.id!!, tieTime) updateCreatedAt(publicTieSecond.id!!, tieTime) - updateCreatedAt(secretMine.id!!, now.minusDays(1)) - updateCreatedAt(secretOther.id!!, now.minusHours(12)) + updateCreatedAt(secretMine.id!!, monthStart.plusDays(4)) + updateCreatedAt(secretOther.id!!, monthStart.plusDays(5)) entityManager.flush() entityManager.clear() + // when: 일반 사용자 기준으로 목록/총건수 조회를 실행한다. val list = channelDonationMessageRepository.getChannelDonationMessageList( creatorId = creator.id!!, memberId = viewer.id!!, isCreator = false, offset = 0, limit = 10, - startDateTime = now.minusMonths(1) + startDateTime = monthStart, + endDateTime = nextMonthStart ) + // when: 같은 조건으로 총 건수 조회를 실행한다. val totalCount = channelDonationMessageRepository.getChannelDonationMessageTotalCount( creatorId = creator.id!!, memberId = viewer.id!!, isCreator = false, - startDateTime = now.minusMonths(1) + startDateTime = monthStart, + endDateTime = nextMonthStart ) + // then: 비공개 타인 메시지는 제외되고, createdAt desc/id desc 정렬이 적용돼야 한다. assertEquals(3, list.size) assertEquals(secretMine.id, list[0].id) assertEquals(publicTieSecond.id, list[1].id) @@ -66,40 +79,53 @@ class ChannelDonationMessageRepositoryTest @Autowired constructor( } @Test + @DisplayName("크리에이터 본인 조회 시 월 범위 내 비공개 메시지를 모두 조회한다") fun shouldIncludeAllRecentSecretMessagesForCreator() { + // given: 크리에이터/조회자/타 사용자 데이터를 준비한다. val creator = saveMember(nickname = "creator2", role = MemberRole.CREATOR) val viewer = saveMember(nickname = "viewer2", role = MemberRole.USER) val otherUser = saveMember(nickname = "other2", role = MemberRole.USER) + // given: 조회 기준 월의 시작/종료 시점을 계산한다. val now = LocalDateTime.now() + val monthStart = now.withDayOfMonth(1).toLocalDate().atStartOfDay() + val nextMonthStart = monthStart.plusMonths(1) + + // given: 월 경계/비공개 노출 검증용 메시지를 저장한다. val oldPublic = saveMessage(member = viewer, creator = creator, can = 1, isSecret = false) val recentPublic = saveMessage(member = viewer, creator = creator, can = 2, isSecret = false) val recentSecretMine = saveMessage(member = viewer, creator = creator, can = 3, isSecret = true) val recentSecretOther = saveMessage(member = otherUser, creator = creator, can = 4, isSecret = true) - updateCreatedAt(oldPublic.id!!, now.minusMonths(2)) - updateCreatedAt(recentPublic.id!!, now.minusDays(3)) - updateCreatedAt(recentSecretMine.id!!, now.minusDays(2)) - updateCreatedAt(recentSecretOther.id!!, now.minusDays(1)) + // given: createdAt을 직접 조정해 월 범위 안/밖 데이터를 분리한다. + updateCreatedAt(oldPublic.id!!, monthStart.minusDays(1)) + updateCreatedAt(recentPublic.id!!, monthStart.plusDays(2)) + updateCreatedAt(recentSecretMine.id!!, monthStart.plusDays(3)) + updateCreatedAt(recentSecretOther.id!!, monthStart.plusDays(4)) entityManager.flush() entityManager.clear() + // when: 크리에이터 본인 기준으로 목록/총건수 조회를 실행한다. val list = channelDonationMessageRepository.getChannelDonationMessageList( creatorId = creator.id!!, memberId = creator.id!!, isCreator = true, offset = 0, limit = 10, - startDateTime = now.minusMonths(1) + startDateTime = monthStart, + endDateTime = nextMonthStart ) + // when: 같은 조건으로 총 건수 조회를 실행한다. val totalCount = channelDonationMessageRepository.getChannelDonationMessageTotalCount( creatorId = creator.id!!, memberId = creator.id!!, isCreator = true, - startDateTime = now.minusMonths(1) + startDateTime = monthStart, + endDateTime = nextMonthStart ) + // then: 본인 조회는 비공개 메시지를 포함하고 최신순으로 반환해야 한다. assertEquals(3, list.size) assertEquals(recentSecretOther.id, list[0].id) assertEquals(recentSecretMine.id, list[1].id) diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationServiceTest.kt index d9705cb0..0748a811 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/explorer/profile/channelDonation/ChannelDonationServiceTest.kt @@ -10,9 +10,11 @@ import kr.co.vividnext.sodalive.member.MemberRole import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.mockito.Mockito import java.time.LocalDateTime +import java.time.LocalTime class ChannelDonationServiceTest { private lateinit var canPaymentService: CanPaymentService @@ -36,7 +38,9 @@ class ChannelDonationServiceTest { } @Test + @DisplayName("후원 캔 수가 1 미만이면 예외를 던진다") fun shouldThrowWhenDonateCanIsLessThanOne() { + // given: 유효하지 않은 후원 요청(캔 0)을 준비한다. val member = createMember(id = 10L, role = MemberRole.USER, nickname = "viewer") val request = PostChannelDonationRequest( creatorId = 1L, @@ -46,15 +50,19 @@ class ChannelDonationServiceTest { container = "aos" ) + // when: 후원 로직을 실행한다. val exception = assertThrows(SodaException::class.java) { service.donate(request, member) } + // then: 최소 캔 수 검증 메시지 키를 반환해야 한다. assertEquals("content.donation.error.minimum_can", exception.messageKey) } @Test + @DisplayName("일반 사용자 조회 시 비공개 노출 규칙과 월 범위를 적용한다") fun shouldPassUserVisibilityFlagToRepositoryWhenRequesterIsNotCreator() { + // given: 크리에이터/조회자/후원 메시지 데이터를 준비한다. val creator = createMember(id = 1L, role = MemberRole.CREATOR, nickname = "creator") val viewer = createMember(id = 2L, role = MemberRole.USER, nickname = "viewer") val message = ChannelDonationMessage(can = 3, isSecret = true, additionalMessage = "응원합니다") @@ -62,16 +70,24 @@ class ChannelDonationServiceTest { message.member = viewer message.creator = creator message.createdAt = LocalDateTime.of(2026, 2, 20, 12, 0, 0) + var capturedStartDateTime: LocalDateTime? = null + var capturedEndDateTime: LocalDateTime? = null + // given: repository 응답과 전달 파라미터 캡처를 설정한다. Mockito.`when`(memberRepository.findCreatorByIdOrNull(creator.id!!)).thenReturn(creator) Mockito.`when`( channelDonationMessageRepository.getChannelDonationMessageTotalCount( Mockito.eq(creator.id!!), Mockito.eq(viewer.id!!), Mockito.eq(false), + anyLocalDateTime(), anyLocalDateTime() ) - ).thenReturn(1) + ).thenAnswer { + capturedStartDateTime = it.getArgument(3) + capturedEndDateTime = it.getArgument(4) + 1 + } Mockito.`when`( channelDonationMessageRepository.getChannelDonationMessageList( Mockito.eq(creator.id!!), @@ -79,10 +95,12 @@ class ChannelDonationServiceTest { Mockito.eq(false), Mockito.eq(0L), Mockito.eq(5L), + anyLocalDateTime(), anyLocalDateTime() ) ).thenReturn(listOf(message)) + // when: 채널 후원 목록 조회를 실행한다. val result = service.getChannelDonationList( creatorId = creator.id!!, member = viewer, @@ -90,14 +108,17 @@ class ChannelDonationServiceTest { limit = 5 ) + // then: 응답 총 건수/메시지 포맷이 기대값과 일치해야 한다. assertEquals(1, result.totalCount) assertEquals(1, result.items.size) assertEquals("3캔을 비밀후원하셨습니다.\n\"응원합니다\"", result.items[0].message) + // then: 일반 사용자 조회(false) 조건으로 repository를 호출해야 한다. Mockito.verify(channelDonationMessageRepository).getChannelDonationMessageTotalCount( Mockito.eq(creator.id!!), Mockito.eq(viewer.id!!), Mockito.eq(false), + anyLocalDateTime(), anyLocalDateTime() ) Mockito.verify(channelDonationMessageRepository).getChannelDonationMessageList( @@ -106,12 +127,20 @@ class ChannelDonationServiceTest { Mockito.eq(false), Mockito.eq(0L), Mockito.eq(5L), + anyLocalDateTime(), anyLocalDateTime() ) + + // then: 월 시작/다음 달 시작 범위를 사용해야 한다. + assertEquals(1, capturedStartDateTime!!.dayOfMonth) + assertEquals(LocalTime.MIDNIGHT, capturedStartDateTime!!.toLocalTime()) + assertEquals(capturedStartDateTime!!.plusMonths(1), capturedEndDateTime) } @Test + @DisplayName("후원 캔 수는 천 단위 콤마가 포함된 메시지로 포맷된다") fun shouldFormatCanWithCommaInDonationMessage() { + // given: 1,000캔 후원 메시지 데이터를 준비한다. val creator = createMember(id = 1L, role = MemberRole.CREATOR, nickname = "creator") val viewer = createMember(id = 2L, role = MemberRole.USER, nickname = "viewer") val message = ChannelDonationMessage(can = 1000, isSecret = true, additionalMessage = "응원합니다") @@ -120,12 +149,14 @@ class ChannelDonationServiceTest { message.creator = creator message.createdAt = LocalDateTime.of(2026, 2, 20, 12, 0, 0) + // given: repository 응답을 설정한다. Mockito.`when`(memberRepository.findCreatorByIdOrNull(creator.id!!)).thenReturn(creator) Mockito.`when`( channelDonationMessageRepository.getChannelDonationMessageTotalCount( Mockito.eq(creator.id!!), Mockito.eq(viewer.id!!), Mockito.eq(false), + anyLocalDateTime(), anyLocalDateTime() ) ).thenReturn(1) @@ -136,10 +167,12 @@ class ChannelDonationServiceTest { Mockito.eq(false), Mockito.eq(0L), Mockito.eq(5L), + anyLocalDateTime(), anyLocalDateTime() ) ).thenReturn(listOf(message)) + // when: 목록 조회를 실행한다. val result = service.getChannelDonationList( creatorId = creator.id!!, member = viewer, @@ -147,22 +180,33 @@ class ChannelDonationServiceTest { limit = 5 ) + // then: 후원 메시지에 천 단위 콤마가 포함되어야 한다. assertEquals("1,000캔을 비밀후원하셨습니다.\n\"응원합니다\"", result.items[0].message) } @Test + @DisplayName("크리에이터 본인 조회 시 creator 플래그와 월 범위를 적용한다") fun shouldPassCreatorVisibilityFlagToRepositoryWhenRequesterIsCreatorSelf() { + // given: 조회자와 크리에이터가 동일한 상황을 준비한다. val creator = createMember(id = 1L, role = MemberRole.CREATOR, nickname = "creator") + var capturedStartDateTime: LocalDateTime? = null + var capturedEndDateTime: LocalDateTime? = null + // given: repository 응답과 전달 파라미터 캡처를 설정한다. Mockito.`when`(memberRepository.findCreatorByIdOrNull(creator.id!!)).thenReturn(creator) Mockito.`when`( channelDonationMessageRepository.getChannelDonationMessageTotalCount( Mockito.eq(creator.id!!), Mockito.eq(creator.id!!), Mockito.eq(true), + anyLocalDateTime(), anyLocalDateTime() ) - ).thenReturn(0) + ).thenAnswer { + capturedStartDateTime = it.getArgument(3) + capturedEndDateTime = it.getArgument(4) + 0 + } Mockito.`when`( channelDonationMessageRepository.getChannelDonationMessageList( Mockito.eq(creator.id!!), @@ -170,10 +214,12 @@ class ChannelDonationServiceTest { Mockito.eq(true), Mockito.eq(0L), Mockito.eq(5L), + anyLocalDateTime(), anyLocalDateTime() ) ).thenReturn(emptyList()) + // when: 크리에이터 본인으로 목록 조회를 실행한다. service.getChannelDonationList( creatorId = creator.id!!, member = creator, @@ -181,10 +227,12 @@ class ChannelDonationServiceTest { limit = 5 ) + // then: creator(true) 조건으로 repository를 호출해야 한다. Mockito.verify(channelDonationMessageRepository).getChannelDonationMessageTotalCount( Mockito.eq(creator.id!!), Mockito.eq(creator.id!!), Mockito.eq(true), + anyLocalDateTime(), anyLocalDateTime() ) Mockito.verify(channelDonationMessageRepository).getChannelDonationMessageList( @@ -193,8 +241,14 @@ class ChannelDonationServiceTest { Mockito.eq(true), Mockito.eq(0L), Mockito.eq(5L), + anyLocalDateTime(), anyLocalDateTime() ) + + // then: 월 시작/다음 달 시작 범위를 사용해야 한다. + assertEquals(1, capturedStartDateTime!!.dayOfMonth) + assertEquals(LocalTime.MIDNIGHT, capturedStartDateTime!!.toLocalTime()) + assertEquals(capturedStartDateTime!!.plusMonths(1), capturedEndDateTime) } private fun createMember(id: Long, role: MemberRole, nickname: String): Member {