refactor(dm): 채팅 저장소를 WebSocket 기준으로 전환한다
This commit is contained in:
@@ -5,20 +5,32 @@ import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.CreateDmChatRoomRequest
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.CreateDmChatRoomResponse
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatApi
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatMessageResponse
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatMessagesPageResponse
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatRepository
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatRoomOpenResponse
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.SendDmChatMessageResponse
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.SendDmTextMessageRequest
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatSocketClient
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.data.DmChatSocketEvent
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
import okio.ByteString
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class DmChatRepositoryTest {
|
||||
|
||||
private val api = FakeDmChatApi()
|
||||
private val repository = DmChatRepository(api)
|
||||
private val socketFactory = FakeWebSocketFactory()
|
||||
private val socketClient = DmChatSocketClient(
|
||||
okHttpClient = OkHttpClient(),
|
||||
gson = com.google.gson.Gson(),
|
||||
baseUrl = "https://api.example.com",
|
||||
webSocketFactory = socketFactory::newWebSocket
|
||||
)
|
||||
private val repository = DmChatRepository(api = api, socketClient = socketClient)
|
||||
|
||||
@Test
|
||||
fun `createOrGetRoom은 bearer header와 creatorId를 API에 위임한다`() {
|
||||
@@ -48,27 +60,38 @@ class DmChatRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sendTextMessage는 textMessage request를 API에 위임한다`() {
|
||||
repository.sendTextMessage(token = "test-token", roomId = 12L, textMessage = "안녕").blockingGet()
|
||||
fun `connectSocket은 bearer header와 listener를 socket client에 위임한다`() {
|
||||
repository.connectSocket(token = "test-token", listener = TestListener())
|
||||
|
||||
assertEquals("Bearer test-token", api.lastAuthHeader)
|
||||
assertEquals(12L, api.lastRoomId)
|
||||
assertEquals(SendDmTextMessageRequest(textMessage = "안녕"), api.lastSendRequest)
|
||||
assertEquals("Bearer test-token", socketFactory.request?.header("Authorization"))
|
||||
assertEquals("wss://api.example.com/ws/v2/user-creator-chat", socketFactory.request?.tag(String::class.java))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `disconnectRealtime은 bearer header와 roomId를 API에 위임한다`() {
|
||||
repository.disconnectRealtime(token = "test-token", roomId = 12L).blockingGet()
|
||||
fun `socket send 메서드는 socket client envelope 전송에 위임한다`() {
|
||||
repository.connectSocket(token = "test-token", listener = TestListener())
|
||||
|
||||
assertEquals("Bearer test-token", api.lastAuthHeader)
|
||||
assertEquals(12L, api.lastRoomId)
|
||||
assertNull(api.lastSendRequest)
|
||||
assertTrue(repository.sendJoinRoom(roomId = 12L))
|
||||
assertTrue(repository.sendLeaveRoom(roomId = 12L))
|
||||
assertTrue(repository.sendSocketText(roomId = 12L, requestId = "request-1", textMessage = "안녕"))
|
||||
assertTrue(repository.sendPing())
|
||||
|
||||
assertEquals(listOf("JOIN_ROOM", "LEAVE_ROOM", "SEND_TEXT", "PING"), socketFactory.webSocket.sentTypes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `closeSocket은 socket close에 위임하고 미연결 전송은 false를 반환한다`() {
|
||||
repository.connectSocket(token = "test-token", listener = TestListener())
|
||||
|
||||
repository.closeSocket()
|
||||
|
||||
assertEquals(1, socketFactory.webSocket.closeCount)
|
||||
assertFalse(repository.sendPing())
|
||||
}
|
||||
|
||||
private class FakeDmChatApi : DmChatApi {
|
||||
var lastAuthHeader: String? = null
|
||||
var lastCreateRequest: CreateDmChatRoomRequest? = null
|
||||
var lastSendRequest: SendDmTextMessageRequest? = null
|
||||
var lastRoomId: Long? = null
|
||||
var lastCursor: Long? = null
|
||||
var lastLimit: Int? = null
|
||||
@@ -126,46 +149,47 @@ class DmChatRepositoryTest {
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendDmTextMessage(
|
||||
authHeader: String,
|
||||
roomId: Long,
|
||||
request: SendDmTextMessageRequest
|
||||
): Single<ApiResponse<SendDmChatMessageResponse>> {
|
||||
lastAuthHeader = authHeader
|
||||
lastRoomId = roomId
|
||||
lastSendRequest = request
|
||||
return Single.just(
|
||||
ApiResponse(
|
||||
success = true,
|
||||
data = SendDmChatMessageResponse(
|
||||
message = message(),
|
||||
deliveredRealtime = true,
|
||||
pushSent = false
|
||||
)
|
||||
)
|
||||
)
|
||||
private open class TestListener : DmChatSocketClient.Listener {
|
||||
override fun onEvent(event: DmChatSocketEvent) = Unit
|
||||
override fun onFailure(throwable: Throwable) = Unit
|
||||
}
|
||||
|
||||
private class FakeWebSocketFactory {
|
||||
val webSocket = FakeWebSocket()
|
||||
var request: Request? = null
|
||||
|
||||
fun newWebSocket(request: Request, listener: WebSocketListener): WebSocket {
|
||||
this.request = request
|
||||
return webSocket
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeWebSocket : WebSocket {
|
||||
val sentTexts = mutableListOf<String>()
|
||||
var closeCount = 0
|
||||
|
||||
override fun request(): Request = Request.Builder().url("wss://example.com").build()
|
||||
|
||||
override fun queueSize(): Long = 0L
|
||||
|
||||
override fun send(text: String): Boolean {
|
||||
sentTexts += text
|
||||
return true
|
||||
}
|
||||
|
||||
override fun disconnectRealtime(
|
||||
authHeader: String,
|
||||
roomId: Long
|
||||
): Single<ApiResponse<Boolean>> {
|
||||
lastAuthHeader = authHeader
|
||||
lastRoomId = roomId
|
||||
return Single.just(ApiResponse(success = true, data = true))
|
||||
override fun send(bytes: ByteString): Boolean = true
|
||||
|
||||
override fun close(code: Int, reason: String?): Boolean {
|
||||
closeCount += 1
|
||||
return true
|
||||
}
|
||||
|
||||
private fun message() = DmChatMessageResponse(
|
||||
messageId = 1L,
|
||||
messageType = "TEXT",
|
||||
mine = true,
|
||||
createdAt = 1000L,
|
||||
textMessage = "안녕",
|
||||
voiceMessageUrl = null,
|
||||
senderId = 2L,
|
||||
senderNickname = "나",
|
||||
senderProfileImageUrl = "https://example.com/profile.png"
|
||||
)
|
||||
override fun cancel() = Unit
|
||||
|
||||
fun sentTypes(): List<String> = sentTexts.map { text ->
|
||||
com.google.gson.JsonParser.parseString(text).asJsonObject.get("type").asString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user