fix(chat): WS history 帧按会话过滤,修次级会话串进主会话消息
WS 首帧 history 是**主会话**最近 50 条的回放,但连接是全局的、帧会被投给任意 ChatViewModel;onEvent 此前把它**原样**并进当前消息列表,导致进次级会话会看到 主会话的消息混在一起——是数据串台,不是显示问题。 - data/repository/PagedMessages.kt:新增纯函数 historyForSession(messages, sessionId), 按**消息自身的 sessionId** 过滤;sessionId 为空的帧视为「无法判定」放行, 不因缺一个字段就整段丢掉历史(保持既有行为) - ui/chat/ChatViewModel.kt:history 分支先收敛到本会话再合并 为什么按 sessionId 而不是「只有主会话才收 history」:前者更耐将来—— 服务端哪天开始给次级会话回放,这里无需改动。 测试:178 全绿(175 → +3),新增三条:主会话回放不得泄漏进次级会话、 缺 sessionId 的帧放行、过滤不修改入参。
This commit is contained in:
1 parent
c09a21136c
commit
2a49c25e1b
3 files changed
+56
-1
No files matched your search
@@ -85,4 +85,38 @@ class MessageMergeTest {
|
||||
assertEquals(listOf("m2", "m1"), idsOf(existing))
|
||||
assertEquals(listOf("m3"), idsOf(incoming))
|
||||
}
|
||||
|
||||
private fun msgIn(sessionId: String, id: String) = HistoryMessageDto(
|
||||
id = id,
|
||||
sessionId = sessionId,
|
||||
role = "user",
|
||||
content = id,
|
||||
createdAt = 0L,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `the main session's history replay cannot leak into a sub session`() {
|
||||
// 真实缺陷:WS 首帧 history 是主会话的回放,却会被并进每一个 ChatViewModel。
|
||||
val replay = listOf(msgIn("web", "m1"), msgIn("web:abc", "m2"))
|
||||
|
||||
assertEquals(listOf("m2"), idsOf(historyForSession(replay, "web:abc")))
|
||||
assertEquals(listOf("m1"), idsOf(historyForSession(replay, "web")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a frame without a sessionId is kept rather than dropping history`() {
|
||||
// 缺字段时无法判定归属,放行以保持既有行为——不因缺一个字段整段丢掉历史。
|
||||
val replay = listOf(msgIn("", "m1"))
|
||||
|
||||
assertEquals(listOf("m1"), idsOf(historyForSession(replay, "web:abc")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `history filtering does not mutate the input`() {
|
||||
val replay = listOf(msgIn("web", "m1"), msgIn("web:abc", "m2"))
|
||||
|
||||
historyForSession(replay, "web")
|
||||
|
||||
assertEquals(listOf("m1", "m2"), idsOf(replay))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user