feat(chat): 채팅방 리스트 조회 API를 추가한다
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
package kr.co.vividnext.sodalive.v2.chat
|
||||
|
||||
import kr.co.vividnext.sodalive.chat.room.ChatMessageType
|
||||
import kr.co.vividnext.sodalive.chat.room.repository.ChatRoomRepository
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.v2.chat.dto.ChatRoomListQueryDto
|
||||
import kr.co.vividnext.sodalive.v2.chat.service.ChatRoomListService
|
||||
import kr.co.vividnext.sodalive.v2.usercreatorchat.UserCreatorChatMessageType
|
||||
import kr.co.vividnext.sodalive.v2.usercreatorchat.repository.UserCreatorChatRoomRepository
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import java.time.LocalDateTime
|
||||
|
||||
class ChatRoomListServiceTest {
|
||||
private lateinit var aiRoomRepository: ChatRoomRepository
|
||||
private lateinit var dmRoomRepository: UserCreatorChatRoomRepository
|
||||
private lateinit var service: ChatRoomListService
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
aiRoomRepository = Mockito.mock(ChatRoomRepository::class.java)
|
||||
dmRoomRepository = Mockito.mock(UserCreatorChatRoomRepository::class.java)
|
||||
service = ChatRoomListService(
|
||||
aiRoomRepository = aiRoomRepository,
|
||||
dmRoomRepository = dmRoomRepository,
|
||||
cloudFrontHost = "https://cdn.test"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("전체 채팅 리스트는 AI와 DM을 최신순으로 병합하고 30개 페이징한다")
|
||||
fun shouldMergeAiAndDmRoomsByLastMessageAt() {
|
||||
val member = member(1L)
|
||||
val aiLastAt = LocalDateTime.of(2026, 5, 14, 11, 0)
|
||||
val dmLastAt = LocalDateTime.of(2026, 5, 14, 12, 0)
|
||||
Mockito.`when`(aiRoomRepository.findAiChatListRooms(1L, null, null, null, PageRequest.of(0, 31))).thenReturn(
|
||||
listOf(
|
||||
ChatRoomListQueryDto(
|
||||
roomId = 10L,
|
||||
chatType = "AI",
|
||||
targetName = "AI 캐릭터",
|
||||
targetImagePath = "character/a.png",
|
||||
lastMessage = "AI hello",
|
||||
messageType = ChatMessageType.TEXT.name,
|
||||
lastMessageAt = aiLastAt
|
||||
)
|
||||
)
|
||||
)
|
||||
Mockito.`when`(dmRoomRepository.findDmChatListRooms(1L, null, null, null, PageRequest.of(0, 31))).thenReturn(
|
||||
listOf(
|
||||
ChatRoomListQueryDto(
|
||||
roomId = 20L,
|
||||
chatType = "DM",
|
||||
targetName = "creator",
|
||||
targetImagePath = null,
|
||||
lastMessage = "안녕하세요. 문의드립니다. 길게 보냅니다.",
|
||||
messageType = UserCreatorChatMessageType.TEXT.name,
|
||||
lastMessageAt = dmLastAt
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val response = service.getRooms(member, filter = "ALL", cursor = null, limit = 30)
|
||||
|
||||
assertFalse(response.hasMore)
|
||||
assertEquals(listOf("DM", "AI"), response.rooms.map { it.chatType })
|
||||
assertEquals("creator", response.rooms[0].targetName)
|
||||
assertEquals("https://cdn.test/profile/default-profile.png", response.rooms[0].targetImageUrl)
|
||||
assertEquals("안녕하세요. 문의드립니다. ...", response.rooms[0].lastMessage)
|
||||
assertEquals("2026-05-14T12:00:00Z", response.rooms[0].lastMessageAt)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("DM 필터는 DM 방만 조회하고 음성 메시지 요약 문구를 사용한다")
|
||||
fun shouldReturnOnlyDmRoomsWithVoicePreview() {
|
||||
val member = member(1L)
|
||||
Mockito.`when`(dmRoomRepository.findDmChatListRooms(1L, null, null, null, PageRequest.of(0, 31))).thenReturn(
|
||||
listOf(
|
||||
ChatRoomListQueryDto(
|
||||
roomId = 20L,
|
||||
chatType = "DM",
|
||||
targetName = "creator",
|
||||
targetImagePath = "profile/creator.png",
|
||||
lastMessage = null,
|
||||
messageType = UserCreatorChatMessageType.VOICE.name,
|
||||
lastMessageAt = LocalDateTime.of(2026, 5, 14, 12, 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val response = service.getRooms(member, filter = "DM", cursor = null, limit = 30)
|
||||
|
||||
assertEquals(1, response.rooms.size)
|
||||
assertEquals("DM", response.rooms[0].chatType)
|
||||
assertEquals("[음성 메시지]", response.rooms[0].lastMessage)
|
||||
assertEquals("https://cdn.test/profile/creator.png", response.rooms[0].targetImageUrl)
|
||||
Mockito.verifyNoInteractions(aiRoomRepository)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("31번째 항목이 있으면 hasMore와 nextCursor를 반환한다")
|
||||
fun shouldReturnNextCursorWhenMoreThanLimit() {
|
||||
val member = member(1L)
|
||||
val rows = (1L..31L).map { index ->
|
||||
ChatRoomListQueryDto(
|
||||
roomId = index,
|
||||
chatType = "AI",
|
||||
targetName = "AI $index",
|
||||
targetImagePath = null,
|
||||
lastMessage = "message $index",
|
||||
messageType = ChatMessageType.TEXT.name,
|
||||
lastMessageAt = LocalDateTime.of(2026, 5, 14, 12, 0).minusMinutes(index)
|
||||
)
|
||||
}
|
||||
Mockito.`when`(aiRoomRepository.findAiChatListRooms(1L, null, null, null, PageRequest.of(0, 31))).thenReturn(rows)
|
||||
|
||||
val response = service.getRooms(member, filter = "AI", cursor = null, limit = 30)
|
||||
|
||||
assertEquals(30, response.rooms.size)
|
||||
assertTrue(response.hasMore)
|
||||
assertEquals("AI", response.rooms.last().chatType)
|
||||
assertEquals("30", response.nextCursor?.substringAfterLast(":"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("커서는 동일 시간의 다음 정렬 항목을 누락하지 않는다")
|
||||
fun shouldKeepRoomsWithSameTimestampAfterCursor() {
|
||||
val member = member(1L)
|
||||
val cursorAt = LocalDateTime.of(2026, 5, 14, 12, 0)
|
||||
Mockito.`when`(aiRoomRepository.findAiChatListRooms(1L, cursorAt, "DM", 20L, PageRequest.of(0, 31))).thenReturn(
|
||||
listOf(
|
||||
ChatRoomListQueryDto(
|
||||
roomId = 30L,
|
||||
chatType = "AI",
|
||||
targetName = "AI same time",
|
||||
targetImagePath = "",
|
||||
lastMessage = "same time",
|
||||
messageType = ChatMessageType.TEXT.name,
|
||||
lastMessageAt = cursorAt
|
||||
)
|
||||
)
|
||||
)
|
||||
Mockito.`when`(dmRoomRepository.findDmChatListRooms(1L, cursorAt, "DM", 20L, PageRequest.of(0, 31))).thenReturn(
|
||||
listOf(
|
||||
ChatRoomListQueryDto(
|
||||
roomId = 20L,
|
||||
chatType = "DM",
|
||||
targetName = "cursor row",
|
||||
targetImagePath = "profile/cursor.png",
|
||||
lastMessage = "cursor",
|
||||
messageType = UserCreatorChatMessageType.TEXT.name,
|
||||
lastMessageAt = cursorAt
|
||||
),
|
||||
ChatRoomListQueryDto(
|
||||
roomId = 10L,
|
||||
chatType = "DM",
|
||||
targetName = "older",
|
||||
targetImagePath = "profile/older.png",
|
||||
lastMessage = "older",
|
||||
messageType = UserCreatorChatMessageType.TEXT.name,
|
||||
lastMessageAt = cursorAt.minusMinutes(1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val response = service.getRooms(member, filter = "ALL", cursor = "2026-05-14T12:00:00:DM:20", limit = 30)
|
||||
|
||||
assertEquals(listOf(30L, 10L), response.rooms.map { it.roomId })
|
||||
assertEquals("https://cdn.test/profile/default-profile.png", response.rooms[0].targetImageUrl)
|
||||
}
|
||||
|
||||
private fun member(id: Long) = Member(password = "pw", nickname = "user").apply { this.id = id }
|
||||
}
|
||||
Reference in New Issue
Block a user