diff --git a/src/main/java/com/hf/healthfriend/HealthfriendApplication.java b/src/main/java/com/hf/healthfriend/HealthfriendApplication.java index 816f4596..63023463 100644 --- a/src/main/java/com/hf/healthfriend/HealthfriendApplication.java +++ b/src/main/java/com/hf/healthfriend/HealthfriendApplication.java @@ -2,10 +2,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication -@EnableJpaAuditing public class HealthfriendApplication { public static void main(String[] args) { diff --git a/src/main/java/com/hf/healthfriend/domain/chat/repository/ChatMessageRepository.java b/src/main/java/com/hf/healthfriend/domain/chat/repository/ChatMessageRepository.java index b4b87e00..222225b9 100644 --- a/src/main/java/com/hf/healthfriend/domain/chat/repository/ChatMessageRepository.java +++ b/src/main/java/com/hf/healthfriend/domain/chat/repository/ChatMessageRepository.java @@ -2,7 +2,20 @@ import com.hf.healthfriend.domain.chat.entity.chatmessage.ChatMessage; import com.hf.healthfriend.domain.chat.repository.custom.ChatMessageCustomRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface ChatMessageRepository extends JpaRepository, ChatMessageCustomRepository { + + @Query(""" + SELECT cm + FROM ChatMessage cm + INNER JOIN Chatroom cr ON cr.chatroomId = cm.chatroom.chatroomId + WHERE cr.chatroomId = :chatroomId + ORDER BY cm.creationTime DESC + """) + Page findByChatroomId(@Param("chatroomId") Long chatroomId, Pageable page); } diff --git a/src/main/java/com/hf/healthfriend/global/config/JpaAuditingConfig.java b/src/main/java/com/hf/healthfriend/global/config/JpaAuditingConfig.java new file mode 100644 index 00000000..79325aa8 --- /dev/null +++ b/src/main/java/com/hf/healthfriend/global/config/JpaAuditingConfig.java @@ -0,0 +1,9 @@ +package com.hf.healthfriend.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class JpaAuditingConfig { +} diff --git a/src/test/java/com/hf/healthfriend/domain/chat/repository/TestChatMessageRepository.java b/src/test/java/com/hf/healthfriend/domain/chat/repository/TestChatMessageRepository.java index 6e03d2b0..57401400 100644 --- a/src/test/java/com/hf/healthfriend/domain/chat/repository/TestChatMessageRepository.java +++ b/src/test/java/com/hf/healthfriend/domain/chat/repository/TestChatMessageRepository.java @@ -1,8 +1,10 @@ package com.hf.healthfriend.domain.chat.repository; +import com.hf.healthfriend.domain.chat.constant.ChatMessageType; import com.hf.healthfriend.domain.chat.entity.Chatroom; import com.hf.healthfriend.domain.chat.entity.chatmessage.ChatMessage; import com.hf.healthfriend.domain.chat.entity.chatmessage.ImageChatMessage; +import com.hf.healthfriend.domain.chat.entity.chatmessage.TextChatMessage; import com.hf.healthfriend.domain.member.entity.Member; import com.hf.healthfriend.domain.member.repository.MemberRepository; import com.hf.healthfriend.testutil.MysqlTestcontainerConfig; @@ -12,12 +14,19 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.util.ReflectionTestUtils; -import java.util.Optional; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; @@ -66,4 +75,96 @@ void saveImageMessage_success() { assertThat(chatMessage instanceof ImageChatMessage).isTrue(); assertThat(((ImageChatMessage) chatMessage).getImageUrl()).isEqualTo(message.getImageUrl()); } + + @ParameterizedTest + @CsvSource(value = { + "1:1", + "1:10", + "1:100", + "2:1", + "2:10", + "2:100", + "3:100", + "3:25", + "3:47" + }, delimiter = ':') + @DisplayName("findByChatroomId() - Pagination을 적용한 채팅 메시지 목록 불러오기 테스트") + void findByChatroomId_success_fetchWithPagination(int page, int pageSize) { + // Given + int index = page - 1; + Member participant1 = SampleEntityGenerator.generateSampleMember("participant1@gmail.com", "part1"); + Member participant2 = SampleEntityGenerator.generateSampleMember("participant2@gmail.com", "part2"); + this.memberRepository.save(participant1); + this.memberRepository.save(participant2); + + Chatroom chatroom = SampleEntityGenerator.generateSampleChatroom(participant1, participant2); + this.chatroomRepository.save(chatroom); + + List messages = inputSampleChatMessages(chatroom, participant1, participant2); + + for (ChatMessage m : messages) { + log.info("chatMessageId={}", m.getChatMessageId()); + log.info("creationTime={}", m.getCreationTime()); + } + + // When + Page result = this.chatMessageRepository.findByChatroomId(chatroom.getChatroomId(), + PageRequest.of(index, pageSize)); + + // Then + List expected = getSubList(messages, index, pageSize); + + log.info("message.size()={}", messages.size()); + log.info("messages.size() / pageSize = {}", messages.size() / pageSize); + assertThat(result.getTotalElements()).isEqualTo(messages.size()); + assertThat(result.getTotalPages()) + .isEqualTo(messages.size() / pageSize + (messages.size() % pageSize > 0 ? 1 : 0)); + + // 순서 보장 + assertThat(expected.stream().map(ChatMessage::getChatMessageId)) + .containsExactly(result.getContent().stream().map(ChatMessage::getChatMessageId).toArray(Long[]::new)); + } + + private List inputSampleChatMessages(Chatroom chatroom, Member participant1, Member participant2) { + final int DUMMY_COUNT = 100; + List chatMessages = new ArrayList<>(); + LocalDateTime now = LocalDateTime.now(); + + for (int i = 0; i < DUMMY_COUNT; i++) { + int num = i + 1; + Random random = new Random(); + int randomValue = random.nextInt(DUMMY_COUNT * 100); + log.info("DUMMY_COUNT * 100 = {}", DUMMY_COUNT * 100); + log.info("randomValue={}", randomValue); + LocalDateTime randomCreationTime = now.minus(randomValue, ChronoUnit.SECONDS); + TextChatMessage message = new TextChatMessage(chatroom, num % 2 == 0 ? participant1 : participant2, "text" + num); + ReflectionTestUtils.setField(message, "creationTime", randomCreationTime); + ReflectionTestUtils.setField(message, "lastModified", randomCreationTime); + chatMessages.add(message); + } + + this.chatMessageRepository.saveAll(chatMessages); + + chatMessages.sort((c1, c2) -> { + if (c1.getCreationTime().isAfter(c2.getCreationTime())) { + return -1; + } else if (c1.getCreationTime().isBefore(c2.getCreationTime())) { + return 1; + } else { + return 0; + } + }); + + return chatMessages; + } + + private List getSubList(List original, int index, int pageSize) { + int fromIndex = index * pageSize; + int toIndex = (index + 1) * pageSize; + if (fromIndex >= original.size()) { + return new ArrayList<>(); + } + + return original.subList(fromIndex, Math.min(toIndex, pageSize)); + } } \ No newline at end of file