package com.sptmobile.messages import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test class MessageThreadTest { // [unit->REQ-GATEWAY-THREAD] // The wire union parses and flattens into ONE gateway thread: every // endpoint's slice unified, each row tagged with its endpoint, sorted by // timestamp (stable on ties). This is the raw in/out log — no digest. @Test fun parsesAndFlattensUnionSortedByTimestamp() { val reply = """ [ {"endpoint":"star-1","entries":[ {"msg_id":"a-1","ts_ms":3000,"dir":"out","from":"mobile-gw","body":"to star"} ]}, {"endpoint":"agent-b","entries":[ {"msg_id":"b-1","ts_ms":1000,"dir":"in","from":"agent-b","body":"b first"}, {"msg_id":"b-2","ts_ms":5000,"dir":"in","from":"agent-b","body":"b second"} ]} ] """.trimIndent() val rows = MessageThread.parseThread(reply) assertEquals(listOf("b first", "to star", "b second"), rows.map { it.body }) assertEquals(listOf("agent-b", "star-1", "agent-b"), rows.map { it.endpoint }) // dir → outbound relative to the endpoint. assertEquals(listOf(false, true, false), rows.map { it.outbound }) } // [unit->REQ-GATEWAY-THREAD] // Row identity is msg-id, scoped by endpoint (ids may repeat across a // host's endpoints); a msg-id-less row falls back to a content-stable key. @Test fun stableKeyIsEndpointScopedMsgIdWithContentFallback() { val group = EndpointHistoryGroup( endpoint = "star-1", entries = listOf( HistoryRowOf("m-1", 1000, "in", "star-1", "hello"), HistoryRowOf(null, 2000, "in", "star-1", "no id"), ), ) val rows = MessageThread.flatten(listOf(group)) assertEquals("star-1/m-1", rows[0].stableKey) assertTrue(rows[1].stableKey.startsWith("star-1/2000@star-1#")) } @Test fun emptyUnionIsEmptyThread() { assertTrue(MessageThread.parseThread("[]").isEmpty()) } // Unknown wire fields (a newer host adding to HistoryEntry) never break the // parse — the union is forward-compatible. @Test fun toleratesUnknownFields() { val reply = """ [{"endpoint":"e","entries":[ {"msg_id":"x","ts_ms":1,"dir":"out","from":"g","body":"hi","json":"{}","future":42} ]}] """.trimIndent() val rows = MessageThread.parseThread(reply) assertEquals(1, rows.size) assertEquals("hi", rows[0].body) } private fun HistoryRowOf( msgId: String?, tsMs: Long, dir: String, from: String, body: String, ) = com.sptmobile.endpoint.HistoryRow( msg_id = msgId, ts_ms = tsMs, dir = dir, from = from, body = body, ) }