diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/main/chat/dm/DmChatRemovedEndpointSourceTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/main/chat/dm/DmChatRemovedEndpointSourceTest.kt new file mode 100644 index 00000000..4db55b12 --- /dev/null +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/main/chat/dm/DmChatRemovedEndpointSourceTest.kt @@ -0,0 +1,70 @@ +package kr.co.vividnext.sodalive.v2.main.chat.dm + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import java.io.File + +class DmChatRemovedEndpointSourceTest { + + @Test + fun `active DM 채팅 경로에는 제거된 SSE와 text REST endpoint가 남지 않는다`() { + val activeSources = listOf( + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/DmChatRoomActivity.kt", + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/DmChatRoomViewModel.kt", + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatApi.kt", + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatRepository.kt", + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatSocketClient.kt" + ) + + activeSources.forEach { path -> + val source = projectFile(path).readText() + assertFalse("$path must not use removed SSE events endpoint", source.contains("/events")) + assertFalse("$path must not use removed disconnect endpoint", source.contains("events/disconnect")) + assertFalse("$path must not use removed text REST endpoint", source.contains("messages/text")) + assertFalse("$path must not use SSE accept header", source.contains("text/event-stream")) + assertFalse("$path must not use EventSource", source.contains("EventSource")) + } + } + + @Test + fun `텍스트 전송과 lifecycle 종료는 WebSocket envelope를 사용한다`() { + val viewModel = projectFile( + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/DmChatRoomViewModel.kt" + ).readText() + val repository = projectFile( + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatRepository.kt" + ).readText() + + assertTrue(viewModel.contains("repository.sendSocketText(")) + assertTrue(viewModel.contains("repository.sendLeaveRoom(roomId)")) + assertTrue(viewModel.contains("repository.closeSocket()")) + assertTrue(repository.contains("fun sendSocketText")) + assertTrue(repository.contains("fun sendLeaveRoom")) + assertFalse(repository.contains("disconnectRealtime")) + } + + @Test + fun `음성 메시지는 전송 API 추가 없이 DTO 필드 보존 범위로 유지한다`() { + val api = projectFile( + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatApi.kt" + ).readText() + val models = projectFile( + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatModels.kt" + ).readText() + val repository = projectFile( + "app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/dm/data/DmChatRepository.kt" + ).readText() + + assertTrue(models.contains("@SerializedName(\"voiceMessageUrl\") val voiceMessageUrl: String?")) + assertFalse(api.contains("messages/voice")) + assertFalse(api.contains("Multipart")) + assertFalse(repository.contains("sendDmVoiceMessage")) + } + + private fun projectFile(relativePath: String): File { + val candidates = listOf(File(relativePath), File("../$relativePath")) + return candidates.firstOrNull { it.exists() } + ?: error("Project file not found: $relativePath") + } +}