diff --git a/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java b/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java index b5e956b..4370dea 100644 --- a/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java +++ b/src/main/java/com/techfork/domain/activity/repository/ReadPostRepository.java @@ -33,6 +33,12 @@ public interface ReadPostRepository extends JpaRepository { JOIN rp.post p JOIN p.techBlog t WHERE rp.user.id = :userId + AND rp.id IN ( + SELECT MAX(rp2.id) + FROM ReadPost rp2 + WHERE rp2.user.id = :userId + GROUP BY rp2.post.id + ) AND (:lastReadPostId IS NULL OR rp.id < :lastReadPostId) ORDER BY rp.id DESC """) diff --git a/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java b/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java index 27a48d1..df6effd 100644 --- a/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java +++ b/src/test/java/com/techfork/domain/activity/controller/ActivityControllerIntegrationTest.java @@ -510,6 +510,29 @@ void getReadPosts_Success_Multiple() throws Exception { .andExpect(jsonPath("$.data.readPosts[0].readAt").exists()); } + @Test + @DisplayName("읽은 게시글 목록 조회 성공 - 동일 포스트 중복 제거 확인") + void getReadPosts_Success_Deduplicated() throws Exception { + // Given - 동일한 포스트(testPost1)를 두 번 읽음 + ReadPost readPost1 = ReadPost.create(testUser, testPost1, LocalDateTime.now().minusHours(2), 300); + ReadPost readPost2 = ReadPost.create(testUser, testPost1, LocalDateTime.now().minusHours(1), 400); + // 다른 포스트(testPost2)를 읽음 + ReadPost readPost3 = ReadPost.create(testUser, testPost2, LocalDateTime.now(), 150); + + readPostRepository.saveAll(List.of(readPost1, readPost2, readPost3)); + + // When & Then + mockMvc.perform(get("/api/v1/activities/read-posts") + .header("Authorization", "Bearer " + accessToken) + .param("size", "20")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.readPosts.length()").value(2)) // 3개가 아닌 2개여야 함 + .andExpect(jsonPath("$.data.readPosts[0].postId").value(testPost2.getId())) + .andExpect(jsonPath("$.data.readPosts[1].postId").value(testPost1.getId())) + .andExpect(jsonPath("$.data.readPosts[1].readPostId").value(readPost2.getId())); // 더 최신 ID + } + @Test @DisplayName("읽은 게시글 목록 조회 성공 - 북마크 상태 포함") void getReadPosts_Success_WithBookmarks() throws Exception { diff --git a/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java b/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java index 5b87ce5..0396de0 100644 --- a/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java +++ b/src/test/java/com/techfork/domain/activity/repository/ReadPostRepositoryTest.java @@ -124,4 +124,35 @@ void saveDuplicateReadPost_Success() { assertThat(all).allMatch(rp -> rp.getPost().getId().equals(testPost1.getId())); assertThat(all).allMatch(rp -> rp.getUser().getId().equals(testUser.getId())); } + + @Test + @DisplayName("읽은 게시글 목록 조회 - 동일 포스트 중복 제거 및 최신순 정렬 확인") + void findReadPostsWithCursor_DeduplicateByPostId_Success() { + // Given + // 동일한 포스트(testPost1)를 두 번 읽음 + ReadPost read1 = ReadPost.create(testUser, testPost1, LocalDateTime.now().minusHours(2), 100); + ReadPost read2 = ReadPost.create(testUser, testPost1, LocalDateTime.now().minusHours(1), 100); + // 다른 포스트(testPost2)를 읽음 + ReadPost read3 = ReadPost.create(testUser, testPost2, LocalDateTime.now(), 100); + + readPostRepository.saveAll(List.of(read1, read2, read3)); + + PageRequest pageRequest = PageRequest.of(0, 10); + + // When + List result = readPostRepository.findReadPostsWithCursor(testUser.getId(), null, pageRequest); + + // Then + // 1. 중복 제거 확인: 총 3개의 데이터 중 포스트 종류는 2개이므로 결과는 2개여야 함 + assertThat(result).hasSize(2); + + // 2. 최신 데이터 확인: testPost1에 대해서는 나중에 읽은 read2의 ID가 포함되어야 함 + assertThat(result).extracting(com.techfork.domain.activity.dto.ReadPostDto::postId) + .containsExactlyInAnyOrder(testPost1.getId(), testPost2.getId()); + + // ID 기준으로 내림차순 정렬되었는지 확인 (read3 -> read2 순서) + // saveAll 후 ID를 가져오기 위해 리포지토리에서 다시 조회하거나, 순서만 확인 + assertThat(result.get(0).postId()).isEqualTo(testPost2.getId()); // read3 + assertThat(result.get(1).postId()).isEqualTo(testPost1.getId()); // read2 + } }