package com.suno.android.common_networking.ably import com.suno.android.common_core_utils.SunoLogger import com.suno.android.common_networking.orpheus.AblyOrpheusRealtimeClient import com.suno.android.common_networking.orpheus.OrpheusEventData import com.suno.android.common_networking.orpheus.OrpheusModelParser import com.suno.android.common_networking.sse.RealtimeEvent import io.mockk.every import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test class AblyOrpheusRealtimeClientTest { private val loggerFactory = mockk(relaxed = true) private val ablySseClient = mockk() private val orpheusModelParser = mockk() private val subject = AblyOrpheusRealtimeClient( loggerFactory = loggerFactory, ablySseClient = ablySseClient, orpheusModelParser = orpheusModelParser, ) @Test fun `given session id when subscribe called then subscribes to correct channel`() = runTest { val sessionId = "test-session-123" every { ablySseClient.subscribe(channel = "orpheus-chat:$sessionId") } returns flowOf() subject.subscribe(sessionId = sessionId).toList() verify { ablySseClient.subscribe(channel = "orpheus-chat:$sessionId") } } @Test fun `given Connected event when subscribe called then emits Connected event`() = runTest { val sessionId = "test-session" every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Connected) val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(1, result.size) assertTrue(result[0] is RealtimeEvent.Connected) } @Test fun `given Disconnected event when subscribe called then emits Disconnected event`() = runTest { val sessionId = "test-session" every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Disconnected) val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(1, result.size) assertTrue(result[0] is RealtimeEvent.Disconnected) } @Test fun `given Error event when subscribe called then emits Error event`() = runTest { val sessionId = "test-session" val throwable = RuntimeException("Test error") every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Error(throwable = throwable)) val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(1, result.size) assertTrue(result[0] is RealtimeEvent.Error) assertEquals(throwable, (result[0] as RealtimeEvent.Error).throwable) } @Test fun `given orpheus-message when subscribe called then delegates to parser`() = runTest { val sessionId = "test-session" val jsonData = """{"data": {"type": "content", "message_id": "msg-123"}}""" val message = createAblyMessage(name = "orpheus-message", data = jsonData) val expectedEvent = RealtimeEvent.Data( data = OrpheusEventData.MessageContent( messageId = "msg-123", content = "Test content", userId = null, ), ) every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Message(message = message)) every { orpheusModelParser.parseMessageString(messageString = jsonData) } returns expectedEvent val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(1, result.size) assertEquals(expectedEvent, result[0]) verify { orpheusModelParser.parseMessageString(messageString = jsonData) } } @Test fun `given orpheus-tool-call when subscribe called then delegates to parser`() = runTest { val sessionId = "test-session" val jsonData = """{"tool_call_id": "tool-123", "tool_call_name": "write_lyrics"}""" val message = createAblyMessage(name = "orpheus-tool-call", data = jsonData) val expectedEvent = RealtimeEvent.Data( data = OrpheusEventData.ToolCallContent( toolCallId = "tool-123", toolCallName = "write_lyrics", content = "Test lyrics", ), ) every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Message(message = message)) every { orpheusModelParser.parseToolString(toolCallString = jsonData) } returns expectedEvent val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(1, result.size) assertEquals(expectedEvent, result[0]) verify { orpheusModelParser.parseToolString(toolCallString = jsonData) } } @Test fun `given message with null data when subscribe called then filters out null result`() = runTest { val sessionId = "test-session" val message = createAblyMessage(name = "orpheus-message", data = null) every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Message(message = message)) val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(0, result.size) } @Test fun `given unknown message type when subscribe called then filters out null result`() = runTest { val sessionId = "test-session" val message = createAblyMessage(name = "unknown-type", data = """{}""") every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Message(message = message)) val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(0, result.size) } @Test fun `given parser returns null when subscribe called then filters out null result`() = runTest { val sessionId = "test-session" val jsonData = """{"data": {"type": "unknown"}}""" val message = createAblyMessage(name = "orpheus-message", data = jsonData) every { ablySseClient.subscribe(channel = any()) } returns flowOf(AblyEvent.Message(message = message)) every { orpheusModelParser.parseMessageString(messageString = jsonData) } returns null val result = subject.subscribe(sessionId = sessionId).toList() assertEquals(0, result.size) verify { orpheusModelParser.parseMessageString(messageString = jsonData) } } private fun createAblyMessage( name: String, data: String?, ) = AblyMessage( id = "msg-id", name = name, data = data, timestamp = 1234567890L, ) }