test #426

Merged
klaus merged 415 commits from test into main 2026-06-27 00:35:30 +00:00
Showing only changes of commit 8b24e89465 - Show all commits

View File

@@ -157,7 +157,7 @@ data class ContentOverviewPage(
val page: Int,
val size: Int
) {
val offset: Int = page * size
val offset: Long = page.toLong() * size
}
class ContentOverviewQueryPolicy {
@@ -270,7 +270,7 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
### Phase 1: 콘텐츠 전체보기 응답/요청 정책 작성
- [ ] **Task 1.1: ContentOverview DTO 직렬화 테스트 작성**
- [x] **Task 1.1: ContentOverview DTO 직렬화 테스트 작성**
- Files:
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/content/overview/dto/ContentOverviewPageResponseTest.kt`
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/content/overview/dto/ContentOverviewPageResponse.kt`
@@ -329,8 +329,12 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
- GREEN: 위 DTO 초안을 추가하고 테스트를 통과시킨다.
- 통과 확인 명령: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.content.overview.dto.ContentOverviewPageResponseTest`
- REFACTOR: import 정리 후 같은 테스트를 재실행한다.
- 검증 기록:
- RED: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.content.overview.dto.ContentOverviewPageResponseTest` 실행 시 `ContentOverviewPageResponse`, `ContentOverviewType`, `ContentOverviewItemResponse` 미구현으로 `compileTestKotlin` 실패.
- GREEN: DTO 구현 후 같은 명령 재실행, `BUILD SUCCESSFUL`.
- REVIEW 보완: `fromFirstAudioContent(...)`가 성인/오리지널 플래그를 전달하는 테스트를 추가했다. 보완 RED는 `isAdult`, `isOriginalSeries` 파라미터 미존재로 `compileTestKotlin` 실패했고, 시그니처 보강 후 같은 DTO 테스트가 `BUILD SUCCESSFUL`.
- [ ] **Task 1.2: ContentOverviewQueryPolicy 테스트와 구현 작성**
- [x] **Task 1.2: ContentOverviewQueryPolicy 테스트와 구현 작성**
- Files:
- Create: `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/content/overview/application/ContentOverviewQueryPolicyTest.kt`
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/content/overview/application/ContentOverviewQueryPolicy.kt`
@@ -369,6 +373,15 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
- GREEN: 위 policy 초안을 추가하고 테스트를 통과시킨다.
- 통과 확인 명령: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.content.overview.application.ContentOverviewQueryPolicyTest`
- REFACTOR: `ContentOverviewType.from(...)`와 page 보정 로직이 DTO/Facade에 중복되지 않게 유지하고 같은 테스트를 재실행한다.
- 검증 기록:
- RED: `./gradlew test --tests kr.co.vividnext.sodalive.v2.api.content.overview.application.ContentOverviewQueryPolicyTest` 실행 시 `ContentOverviewQueryPolicy`, `ContentOverviewPage` 미구현으로 `compileTestKotlin` 실패.
- GREEN: policy 구현 후 같은 명령 재실행, `BUILD SUCCESSFUL`.
- REVIEW 보완: `size = 19`가 기본 size `20`으로 보정되는 테스트를 추가하고, `MIN_SIZE = 20` 정책을 반영했다. 보완 후 같은 policy 테스트가 `BUILD SUCCESSFUL`.
- REVIEW 보완: 큰 `page` 입력에서 `offset`이 Int overflow 되지 않도록 `offset: Long = page.toLong() * size`로 변경했다. 보완 RED는 `Int.MAX_VALUE, size = 50` offset assertion 실패였고, 수정 후 같은 policy 테스트가 `BUILD SUCCESSFUL`.
- REVIEW 보완: 후속 Phase에서 `ContentOverviewPage.offset`을 그대로 넘길 수 있도록 `RecommendationSnapshotPort`, `HomeRecommendationQueryPort`, 관련 service/adapter/repository offset 계약과 문서 예시를 `Long`으로 정렬했다.
- Phase 1 묶음: `./gradlew test --tests 'kr.co.vividnext.sodalive.v2.api.content.overview.*'` 실행, `BUILD SUCCESSFUL`.
- Lint: `./gradlew ktlintCheck` 실행, `BUILD SUCCESSFUL`.
- 참고: `./gradlew test` 전체 실행은 다수 테스트의 XML 결과 파일 write 실패로 중단되어 Phase 1 로직 실패로 보지 않는다.
---
@@ -463,14 +476,14 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
)
Mockito.doReturn(true).`when`(memberContentPreferenceService).canViewAdultContent(member)
Mockito.doReturn(nowSnapshots).`when`(snapshotPort)
.findLatestSnapshots(RecommendedSectionType.NEW_AND_HOT_AUDIO_ALL, offset = 20, limit = 21)
.findLatestSnapshots(RecommendedSectionType.NEW_AND_HOT_AUDIO_ALL, offset = 20L, limit = 21)
Mockito.doReturn(listOf(audioCard(3L), audioCard(4L), audioCard(5L))).`when`(queryPort)
.findAudioCardsByIds(listOf(3L, 4L, 5L), member.id, true, anyLocalDateTime())
val result = queryService.findNewAndHotAudios(member, offset = 20, limit = 21)
val result = queryService.findNewAndHotAudios(member, offset = 20L, limit = 21)
assertEquals(listOf(3L, 4L, 5L), result.map { it.audioContentId })
Mockito.verify(snapshotPort).findLatestSnapshots(RecommendedSectionType.NEW_AND_HOT_AUDIO_ALL, offset = 20, limit = 21)
Mockito.verify(snapshotPort).findLatestSnapshots(RecommendedSectionType.NEW_AND_HOT_AUDIO_ALL, offset = 20L, limit = 21)
}
```
- 실패 확인 명령: `./gradlew test --tests kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationQueryServiceTest`
@@ -478,7 +491,7 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
- GREEN: `AudioRecommendationQueryService.findNewAndHotAudios(member, offset, limit)`를 추가한다.
- 구현 기준:
```kotlin
fun findNewAndHotAudios(member: Member, offset: Int, limit: Int): List<AudioCard> {
fun findNewAndHotAudios(member: Member, offset: Long, limit: Int): List<AudioCard> {
val now = LocalDateTime.now()
val canViewAdultContent = canViewAdultContent(member)
val visibility = if (canViewAdultContent) AudioRecommendationVisibility.ALL else AudioRecommendationVisibility.SAFE
@@ -525,7 +538,7 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
fun shouldReturnNewAndHotPage() {
val member = member(id = 10L)
Mockito.doReturn(listOf(audioCard(1L), audioCard(2L), audioCard(3L))).`when`(audioRecommendationQueryService)
.findNewAndHotAudios(member, offset = 0, limit = 3)
.findNewAndHotAudios(member, offset = 0L, limit = 3)
val response = facade.getContents("NEW_AND_HOT_AUDIO", page = 0, size = 2, member = member)
@@ -540,7 +553,7 @@ private fun emptyResponse(type: ContentOverviewType): ContentOverviewPageRespons
val member = member(id = 10L)
Mockito.doReturn(true).`when`(memberContentPreferenceService).canViewAdultContent(member)
Mockito.doReturn(listOf(firstAudio(1L), firstAudio(2L))).`when`(homeRecommendationQueryService)
.findFirstAudioContents(anyLocalDateTime(), offset = 20, limit = 21, memberId = member.id, includeAdultContents = true)
.findFirstAudioContents(anyLocalDateTime(), offset = 20L, limit = 21, memberId = member.id, includeAdultContents = true)
val response = facade.getContents("FIRST_AUDIO_CONTENT", page = 1, size = 20, member = member)