test #435
@@ -20,10 +20,10 @@
|
|||||||
- 기본 노출 수:
|
- 기본 노출 수:
|
||||||
- `banners`: 메인 홈 추천 배너와 동일
|
- `banners`: 메인 홈 추천 배너와 동일
|
||||||
- `originalSeries`: 최신순 12개
|
- `originalSeries`: 최신순 12개
|
||||||
- `latestAudios`: 최신순 12개
|
- `latestAudios`: 최신순 15개
|
||||||
- `newAndHotAudios`: 최대 12개
|
- `newAndHotAudios`: 최대 12개
|
||||||
- `freeAudios`: 최대 10개 랜덤
|
- `freeAudios`: 최대 15개 랜덤
|
||||||
- `pointAudios`: 최대 10개 랜덤
|
- `pointAudios`: 최대 15개 랜덤
|
||||||
- `mostCommentedAudios`: 최대 5개
|
- `mostCommentedAudios`: 최대 5개
|
||||||
- `recommendedAudios`: 최대 10개
|
- `recommendedAudios`: 최대 10개
|
||||||
- 공개 오디오 공통 조건: `AudioContent.isActive == true`, `AudioContent.duration != null`, `AudioContent.releaseDate != null`, `AudioContent.releaseDate <= now`, 크리에이터 회원 활성.
|
- 공개 오디오 공통 조건: `AudioContent.isActive == true`, `AudioContent.duration != null`, `AudioContent.releaseDate != null`, `AudioContent.releaseDate <= now`, 크리에이터 회원 활성.
|
||||||
@@ -346,7 +346,7 @@ interface AudioRecommendationQueryPort {
|
|||||||
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/AudioRecommendationQueryRepository.kt`
|
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/AudioRecommendationQueryRepository.kt`
|
||||||
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt`
|
- Create: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt`
|
||||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepositoryTest.kt`
|
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepositoryTest.kt`
|
||||||
- RED: 배너는 기존 홈 추천 배너와 동일 필드/활성/차단 정책을 적용하고, 오리지널 시리즈는 `isOriginal = true` 최신순 12개, 최신 오디오는 `releaseDate desc`, `audioContentId desc` 12개를 반환하는 repository 테스트를 작성한다.
|
- RED: 배너는 기존 홈 추천 배너와 동일 필드/활성/차단 정책을 적용하고, 오리지널 시리즈는 `isOriginal = true` 최신순 12개, 최신 오디오는 `releaseDate desc`, `audioContentId desc` 15개를 반환하는 repository 테스트를 작성한다.
|
||||||
- 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`
|
- 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`
|
||||||
- GREEN: QueryDSL로 `findBanners`, `findOriginalSeries`, `findLatestAudios`를 구현한다. 이미지 경로는 `toCdnUrl(cloudFrontHost)`를 사용한다.
|
- GREEN: QueryDSL로 `findBanners`, `findOriginalSeries`, `findLatestAudios`를 구현한다. 이미지 경로는 `toCdnUrl(cloudFrontHost)`를 사용한다.
|
||||||
- REFACTOR: 공개 오디오 조건, 성인 콘텐츠 조건, 차단 관계 조건을 private 조건 함수로 분리한다.
|
- REFACTOR: 공개 오디오 조건, 성인 콘텐츠 조건, 차단 관계 조건을 private 조건 함수로 분리한다.
|
||||||
@@ -356,7 +356,7 @@ interface AudioRecommendationQueryPort {
|
|||||||
- Files:
|
- Files:
|
||||||
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt`
|
- Modify: `src/main/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepository.kt`
|
||||||
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepositoryTest.kt`
|
- Test: `src/test/kotlin/kr/co/vividnext/sodalive/v2/audio/recommendation/adapter/out/persistence/DefaultAudioRecommendationQueryRepositoryTest.kt`
|
||||||
- RED: 무료 오디오는 `price = 0` 공개 오디오 중 최대 10개, 포인트 오디오는 `isPointAvailable = true` 공개 오디오 중 최대 10개를 반환하고 두 섹션 간 중복을 제거하지 않는 테스트를 작성한다.
|
- RED: 무료 오디오는 `price = 0` 공개 오디오 중 최대 15개, 포인트 오디오는 `isPointAvailable = true` 공개 오디오 중 최대 15개를 반환하고 두 섹션 간 중복을 제거하지 않는 테스트를 작성한다.
|
||||||
- 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`
|
- 실패 확인: `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`
|
||||||
- GREEN: `findFreeAudios`, `findPointAudios`를 구현하고 DB 랜덤 정렬은 기존 repository 관례에 맞춰 `Expressions.numberTemplate(Double::class.java, "function('rand')")` 또는 동일 프로젝트에서 쓰는 랜덤 정렬 방식을 사용한다.
|
- GREEN: `findFreeAudios`, `findPointAudios`를 구현하고 DB 랜덤 정렬은 기존 repository 관례에 맞춰 `Expressions.numberTemplate(Double::class.java, "function('rand')")` 또는 동일 프로젝트에서 쓰는 랜덤 정렬 방식을 사용한다.
|
||||||
- REFACTOR: 무료/포인트 조회가 같은 공통 projection 함수를 사용하게 정리한다.
|
- REFACTOR: 무료/포인트 조회가 같은 공통 projection 함수를 사용하게 정리한다.
|
||||||
@@ -680,3 +680,4 @@ interface AudioRecommendationQueryPort {
|
|||||||
- 2026-06-23 리뷰 보정 검증: repository focused test는 병렬 Gradle 실행 중 `kaptGenerateStubsTestKotlin` 출력 디렉터리 충돌로 1회 실패해 단독 재실행했다. H2 `MODE=MySQL`의 `TIMESTAMPDIFF` 경계 동작이 운영 MySQL 공식 기준과 달라 신규 repository 경계 테스트는 제거하고 Kotlin 정책 테스트로 24시간 경계를 고정했다. 최종 `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`: `BUILD SUCCESSFUL`.
|
- 2026-06-23 리뷰 보정 검증: repository focused test는 병렬 Gradle 실행 중 `kaptGenerateStubsTestKotlin` 출력 디렉터리 충돌로 1회 실패해 단독 재실행했다. H2 `MODE=MySQL`의 `TIMESTAMPDIFF` 경계 동작이 운영 MySQL 공식 기준과 달라 신규 repository 경계 테스트는 제거하고 Kotlin 정책 테스트로 24시간 경계를 고정했다. 최종 `./gradlew test --tests kr.co.vividnext.sodalive.v2.audio.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest`: `BUILD SUCCESSFUL`.
|
||||||
- 2026-06-23 리뷰 보정 검증: `./gradlew ktlintCheck`: `BUILD SUCCESSFUL`, `git diff --check`: 출력 없음.
|
- 2026-06-23 리뷰 보정 검증: `./gradlew ktlintCheck`: `BUILD SUCCESSFUL`, `git diff --check`: 출력 없음.
|
||||||
- 2026-06-25 후속 보정: `DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility`의 score 비교 실패 원인은 repository native SQL의 `timestampdiff(day, c.release_date, :snapshotAt)` 최신성 계산이 DB 날짜 경계 기준에 의존해 `AudioRecommendationScorePolicy`의 24시간 경과 기준 `ChronoUnit.DAYS` 계산과 어긋날 수 있는 점으로 확인했다. `DefaultAudioRecommendationQueryRepository`의 New & Hot/추천 오디오 공개일 최신성 계산을 `floor(timestampdiff(hour, c.release_date, :snapshotAt) / 24)`로 변경해 Kotlin 정책과 일치시켰고, `SAFE` 성인 콘텐츠 제외 조건은 기존 `(:includeAdult = true or c.is_adult = false)` 구현이 올바른 것으로 확인했다. 검증은 `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility'`, `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.domain.AudioRecommendationScorePolicyTest'`, `./gradlew ktlintCheck` 모두 `BUILD SUCCESSFUL`로 완료했다.
|
- 2026-06-25 후속 보정: `DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility`의 score 비교 실패 원인은 repository native SQL의 `timestampdiff(day, c.release_date, :snapshotAt)` 최신성 계산이 DB 날짜 경계 기준에 의존해 `AudioRecommendationScorePolicy`의 24시간 경과 기준 `ChronoUnit.DAYS` 계산과 어긋날 수 있는 점으로 확인했다. `DefaultAudioRecommendationQueryRepository`의 New & Hot/추천 오디오 공개일 최신성 계산을 `floor(timestampdiff(hour, c.release_date, :snapshotAt) / 24)`로 변경해 Kotlin 정책과 일치시켰고, `SAFE` 성인 콘텐츠 제외 조건은 기존 `(:includeAdult = true or c.is_adult = false)` 구현이 올바른 것으로 확인했다. 검증은 `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.adapter.out.persistence.DefaultAudioRecommendationQueryRepositoryTest.shouldFindNewAndHotSnapshotsWithVisibility'`, `./gradlew test --rerun-tasks --tests 'kr.co.vividnext.sodalive.v2.content.recommendation.domain.AudioRecommendationScorePolicyTest'`, `./gradlew ktlintCheck` 모두 `BUILD SUCCESSFUL`로 완료했다.
|
||||||
|
- 2026-07-12 후속 정책 변경: 콘텐츠 추천 탭의 `latestAudios`, `freeAudios`, `pointAudios` 기본 노출 수를 각각 15개로 확정했다. PRD/plan-task의 섹션별 기본 노출 수와 관련 task 설명을 15개 정책으로 갱신했고, service 테스트는 `AudioRecommendationQueryService` 상수 기준으로 limit 전달을 검증하도록 정리했다. 검증은 `./gradlew --no-daemon test --tests kr.co.vividnext.sodalive.v2.content.recommendation.application.AudioRecommendationQueryServiceTest`, `./gradlew --no-daemon ktlintCheck`, `./gradlew --no-daemon tasks --all` 모두 `BUILD SUCCESSFUL`로 완료했다. 단, `tasks --all`은 일반 sandbox에서 `~/.gradle` wrapper lock 파일 접근 제한으로 실패해 승인 후 재실행했다.
|
||||||
|
|||||||
@@ -65,10 +65,10 @@
|
|||||||
- 섹션별 기본 노출 수는 아래와 같다.
|
- 섹션별 기본 노출 수는 아래와 같다.
|
||||||
- `banners`: 메인 홈 추천 배너와 동일
|
- `banners`: 메인 홈 추천 배너와 동일
|
||||||
- `originalSeries`: 최신순 12개
|
- `originalSeries`: 최신순 12개
|
||||||
- `latestAudios`: 최신순 12개
|
- `latestAudios`: 최신순 15개
|
||||||
- `newAndHotAudios`: 최대 12개
|
- `newAndHotAudios`: 최대 12개
|
||||||
- `freeAudios`: 최대 10개 랜덤
|
- `freeAudios`: 최대 15개 랜덤
|
||||||
- `pointAudios`: 최대 10개 랜덤
|
- `pointAudios`: 최대 15개 랜덤
|
||||||
- `mostCommentedAudios`: 최대 5개
|
- `mostCommentedAudios`: 최대 5개
|
||||||
- `recommendedAudios`: 최대 10개
|
- `recommendedAudios`: 최대 10개
|
||||||
- 특정 섹션 데이터가 부족하면 가능한 개수만 내려주고 전체 API는 성공 처리한다.
|
- 특정 섹션 데이터가 부족하면 가능한 개수만 내려주고 전체 API는 성공 처리한다.
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
### Feature D. 새로 올라온 오디오
|
### Feature D. 새로 올라온 오디오
|
||||||
|
|
||||||
#### Requirements
|
#### Requirements
|
||||||
- 공개된 오디오 콘텐츠를 최신순으로 12개 조회한다.
|
- 공개된 오디오 콘텐츠를 최신순으로 15개 조회한다.
|
||||||
- 최신순 기준은 `releaseDate desc`, 동률이면 `audioContentId desc`로 한다.
|
- 최신순 기준은 `releaseDate desc`, 동률이면 `audioContentId desc`로 한다.
|
||||||
- Response는 공통 오디오 카드 응답을 사용한다.
|
- Response는 공통 오디오 카드 응답을 사용한다.
|
||||||
|
|
||||||
@@ -126,14 +126,14 @@
|
|||||||
### Feature F. 무료 오디오
|
### Feature F. 무료 오디오
|
||||||
|
|
||||||
#### Requirements
|
#### Requirements
|
||||||
- 무료 오디오 중 랜덤으로 최대 10개 조회한다.
|
- 무료 오디오 중 랜덤으로 최대 15개 조회한다.
|
||||||
- 무료 오디오는 `price = 0`인 공개 오디오로 정의한다.
|
- 무료 오디오는 `price = 0`인 공개 오디오로 정의한다.
|
||||||
- Response는 공통 오디오 카드 응답을 사용한다.
|
- Response는 공통 오디오 카드 응답을 사용한다.
|
||||||
|
|
||||||
### Feature G. 포인트 오디오
|
### Feature G. 포인트 오디오
|
||||||
|
|
||||||
#### Requirements
|
#### Requirements
|
||||||
- 포인트 사용 가능 오디오 중 랜덤으로 최대 10개 조회한다.
|
- 포인트 사용 가능 오디오 중 랜덤으로 최대 15개 조회한다.
|
||||||
- 포인트 오디오는 `isPointAvailable = true`인 공개 오디오로 정의한다.
|
- 포인트 오디오는 `isPointAvailable = true`인 공개 오디오로 정의한다.
|
||||||
- Response는 공통 오디오 카드 응답을 사용한다.
|
- Response는 공통 오디오 카드 응답을 사용한다.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user