feat: 初始化 Android 客户端(M0 骨架 + M1 登录 + M2 基础模块)
M0:Gradle 9.7.1 / AGP 9.2.1 / Kotlin 2.4 / Miuix 0.9.3,minSdk 33,compileSdk 37。 M1:登录页(服务器地址可配、服务端/本地错误分流、EncryptedSharedPreferences、自动登录与静默重登、401 登出)。 M2 基础模块:消息内容解析器(<p>/<action>/<narration> + 流式增量)、WebSocket 客户端、REST 数据层与分页去重。 45 个单元测试全绿,真机验证通过(登录页 + 启动图标)。 devdocs/ 收录设计文档、前后端协同事项与细化方案。
This commit is contained in:
commit
84de51c7b7
67 files changed
+3960
No files matched your search
@@ -0,0 +1,88 @@
|
||||
package top.yeij.cyrene.data.repository
|
||||
|
||||
import top.yeij.cyrene.data.api.dto.HistoryMessageDto
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class MessageMergeTest {
|
||||
|
||||
private fun msg(id: String, createdAt: Long, content: String = id) = HistoryMessageDto(
|
||||
id = id,
|
||||
sessionId = "web",
|
||||
role = "user",
|
||||
content = content,
|
||||
createdAt = createdAt,
|
||||
)
|
||||
|
||||
private fun idsOf(messages: List<HistoryMessageDto>) = messages.map { it.id }
|
||||
|
||||
@Test
|
||||
fun `overlapping page dedupes by id`() {
|
||||
val existing = listOf(msg("m2", 200), msg("m1", 100))
|
||||
val incoming = listOf(msg("m3", 300), msg("m2", 200))
|
||||
|
||||
val merged = mergeMessages(existing, incoming)
|
||||
|
||||
assertEquals(listOf("m1", "m2", "m3"), idsOf(merged))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `out of order incoming page is sorted ascending`() {
|
||||
val existing = listOf(msg("m1", 100), msg("m2", 200))
|
||||
val incoming = listOf(msg("m4", 400), msg("m3", 300))
|
||||
|
||||
val merged = mergeMessages(existing, incoming)
|
||||
|
||||
assertEquals(listOf("m1", "m2", "m3", "m4"), idsOf(merged))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `same millisecond falls back to id ordering`() {
|
||||
val existing = listOf(msg("b", 100), msg("a", 100))
|
||||
val incoming = listOf(msg("c", 100))
|
||||
|
||||
val merged = mergeMessages(existing, incoming)
|
||||
|
||||
assertEquals(listOf("a", "b", "c"), idsOf(merged))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty incoming page keeps existing`() {
|
||||
val existing = listOf(msg("m1", 100), msg("m2", 200))
|
||||
|
||||
val merged = mergeMessages(existing, emptyList())
|
||||
|
||||
assertEquals(listOf("m1", "m2"), idsOf(merged))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty existing returns sorted incoming`() {
|
||||
val incoming = listOf(msg("m3", 300), msg("m1", 100))
|
||||
|
||||
val merged = mergeMessages(emptyList(), incoming)
|
||||
|
||||
assertEquals(listOf("m1", "m3"), idsOf(merged))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `incoming wins on duplicate id`() {
|
||||
val existing = listOf(msg("m1", 100, content = "old"))
|
||||
val incoming = listOf(msg("m1", 100, content = "new"))
|
||||
|
||||
val merged = mergeMessages(existing, incoming)
|
||||
|
||||
assertEquals(1, merged.size)
|
||||
assertEquals("new", merged.single().content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `merge does not mutate inputs`() {
|
||||
val existing = listOf(msg("m2", 200), msg("m1", 100))
|
||||
val incoming = listOf(msg("m3", 300))
|
||||
|
||||
mergeMessages(existing, incoming)
|
||||
|
||||
assertEquals(listOf("m2", "m1"), idsOf(existing))
|
||||
assertEquals(listOf("m3"), idsOf(incoming))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user