feat(chat): 静默告知改为按事故复位,队列排空即清零
W43 的静默规则只在「补发真的成功一次」时把 queuedResendFailures 清零。若一段坏网络的 队列以回滚或被收编收场(一次都没成功发出去),这一格就永远挂着 ⇒ 之后连接恢复、再断 一次、用户又发消息时,那句解释再也说不出口。那不是减少噪音,是把解释永久关掉(主人 2026-10-06 裁决:哪个体验好用哪个)。 改成按事故复位:一个「事故」= 队列从空到非空、再回到空,结束就复位,不管它是怎么结束 的——发出去了、回滚了(内容退回输入栏)、还是被收编了(气泡换成权威消息)。清零点因此 有两个:补发成功一次(原样保留),以及队列排空(既有钩子 forgetQueuedNoticeIfDrained, 回滚那条路也走这里,它是队列清空的唯一两个入口之一)。 这样不会在长期断网时刷屏:阈值仍是 3;「一批从空到非空只说一次」仍由 queuedNoticeTold 管着;纯网络故障下消息会堆积在队列里 ⇒ 队列不清空 ⇒ 不算新事故、计数不复位 ⇒ 不会每条 消息都弹一次横幅。而事故一旦结束,下一次断线又能重新解释。 判据提为纯函数 queuedResendFailuresAfterDelivery(VM 与测试共用,跟 queuedSendNoticeAllowed 同一个写法),单测钉住:队列排空即复位(哪怕这一批一条都没成功 发出去)、队列还有货则计数保留、补发成功照旧清零、一个事故里失败 0/1/2 次仍允许告知而 到阈值静默、下一个事故重新告知。静默不动队列:那条既有用例(判定那一刻队列仍在、下一步 仍是发这条)一字未改。 只动告知判据与它的记账。队列与出队逻辑、sendRouteFor / 三档路由 / W38 分享落地 / W45 直接试 REST / W46 的 CyreneRoute 日志全部未碰。 验证:gradlew.bat clean assembleDebug 与 gradlew.bat testDebugUnitTest, failure / error 均为 0。
This commit is contained in:
1 parent
798c6ee907
commit
ce50bd2c24
3 files changed
+150
-30
No files matched your search
@@ -6,19 +6,23 @@ import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* 「同一条(或同一批)反复补发失败就**静默**」的判据([queuedSendNoticeAllowed])——
|
||||
* 主人 2026-10-06 定案。
|
||||
* 「同一条(或同一批)反复补发失败就**静默**」的判据([queuedSendNoticeAllowed]),以及静默计数
|
||||
* 在什么时候复位([queuedResendFailuresAfterDelivery])——主人 2026-10-06 定案。
|
||||
*
|
||||
* ## 背景
|
||||
* W40/W41 之后,未连接时普通发送会进补发队列([ChatUiState.queuedForConnection]),并给**一句**
|
||||
* 柔和的告知([SEND_QUEUED_NOTICE]:还没连上、已排队、连上后自动发出);连接恢复后自动补发。
|
||||
* 主人新加的规矩是**不要一直刷屏**:同一条(或同一批)反复重试都失败时,**停止再弹告知**,静静地等。
|
||||
* 2026-10-06 主人又裁决了一件跟它配套的事:静默按**事故**复位——一个「事故」= 队列从空到非空、
|
||||
* 再回到空;结束就复位,**不要求**它是以一次成功的补发结束的。
|
||||
*
|
||||
* ## 本测试钉住四件事(就是那条规则的四个边)
|
||||
* ## 本测试钉住五件事(那条规则的五个边)
|
||||
* 1. 第一次失败 ⇒ 告知;
|
||||
* 2. 到阈值之前(1、2 次)⇒ 仍然允许告知;
|
||||
* 3. 到阈值([QUEUED_RESEND_SILENCE_THRESHOLD] = 3)之后 ⇒ **静默**;
|
||||
* 4. **静默之后连接恢复并补发成功 ⇒ 计数清零**,下一次离线又能重新告知(不能永久闭嘴)。
|
||||
* 4. **补发成功 ⇒ 计数清零**(W43 的旧清零点,一字不改);
|
||||
* 5. **队列排空 ⇒ 计数清零**,哪怕这一次(乃至这一整批)**一次都没成功发出过**
|
||||
* —— 静默按事故复位,不会永久闭嘴。
|
||||
*
|
||||
* 另外钉住两条性质:阈值不许取 1 或 2(那等于根本没告知);静默**只是不说**——队列内容与补发的
|
||||
* 下一步一个字节都不受影响(静默 ≠ 丢弃)。
|
||||
@@ -58,12 +62,71 @@ class QueuedSendSilenceTest {
|
||||
fun `a successful resend clears the count so the next offline tells again`() {
|
||||
// 补发成功时 `ChatViewModel.dropQueuedSend(sent = true)` 把计数归零 ⇒ 又回到「第一次失败」。
|
||||
assertFalse(queuedSendNoticeAllowed(toldThisRound = false, failedResends = QUEUED_RESEND_SILENCE_THRESHOLD))
|
||||
val afterSuccess = 0
|
||||
val afterSuccess = queuedResendFailuresAfterDelivery(
|
||||
QUEUED_RESEND_SILENCE_THRESHOLD,
|
||||
sent = true,
|
||||
drained = false,
|
||||
)
|
||||
assertEquals("补发成功这一个清零点照旧:一批还没发完也清零", 0, afterSuccess)
|
||||
assertTrue(queuedSendNoticeAllowed(toldThisRound = false, failedResends = afterSuccess))
|
||||
// 再连着失败又要静默一次:静默不是一次性的,也不是永久闭嘴。
|
||||
assertFalse(queuedSendNoticeAllowed(toldThisRound = false, failedResends = QUEUED_RESEND_SILENCE_THRESHOLD))
|
||||
}
|
||||
|
||||
// —— 一之二、事故结束(队列排空)就复位(W47 主人 2026-10-06 裁决) ——
|
||||
|
||||
@Test
|
||||
fun `a drained batch resets the count even when nothing ever got sent`() {
|
||||
// W43 的旧口径只在「补发成功」时清零。主人改成按事故复位:一个「事故」= 队列从空到非空、
|
||||
// 再回到空,**它怎么结束都算**——包括一段坏网络以回滚 / 被收编收场、一次都没发出去。
|
||||
// 旧口径下这种事故会把静默永远挂着:之后连接恢复、再断一次,用户永远得不到解释。
|
||||
val failures = QUEUED_RESEND_SILENCE_THRESHOLD
|
||||
assertFalse(queuedSendNoticeAllowed(toldThisRound = false, failedResends = failures))
|
||||
|
||||
val afterDrain = queuedResendFailuresAfterDelivery(failures, sent = false, drained = true)
|
||||
|
||||
assertEquals("队列排空就复位,不管这一批有没有成功发出过一条", 0, afterDrain)
|
||||
assertTrue(
|
||||
"下一个事故要能重新解释,不能永久闭嘴",
|
||||
queuedSendNoticeAllowed(toldThisRound = false, failedResends = afterDrain),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the count is kept while the queue is still non empty`() {
|
||||
// 事故还没结束(队列里还有货、这一次又没成功)⇒ 计数原样留着,**同一个事故内**照旧静默。
|
||||
// 这正是长期断网不刷屏的原因:消息堆在队列里 ⇒ 队列不清空 ⇒ 不算新事故 ⇒ 不重新告知。
|
||||
val failures = QUEUED_RESEND_SILENCE_THRESHOLD
|
||||
val kept = queuedResendFailuresAfterDelivery(failures, sent = false, drained = false)
|
||||
|
||||
assertEquals(failures, kept)
|
||||
assertFalse(queuedSendNoticeAllowed(toldThisRound = false, failedResends = kept))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `one incident tells once at most and the next one starts over`() {
|
||||
// 走一遍完整的事故:进队列那一刻(失败 0 次)说一次,之后每次补发又没上路都 ++,
|
||||
// 到阈值就静默;队列排空(事故结束)后,下一个事故的第一次失败又能说出来。
|
||||
var failures = 0
|
||||
assertTrue("事故里的第一句必须说", queuedSendNoticeAllowed(toldThisRound = false, failedResends = failures))
|
||||
|
||||
while (failures < QUEUED_RESEND_SILENCE_THRESHOLD) {
|
||||
assertTrue(
|
||||
"第 ${failures + 1} 次失败之前仍然允许告知",
|
||||
queuedSendNoticeAllowed(toldThisRound = false, failedResends = failures),
|
||||
)
|
||||
failures++
|
||||
}
|
||||
assertFalse("到阈值就停", queuedSendNoticeAllowed(toldThisRound = false, failedResends = failures))
|
||||
|
||||
failures = queuedResendFailuresAfterDelivery(failures, sent = false, drained = true)
|
||||
assertEquals(0, failures)
|
||||
assertTrue(
|
||||
"下一个事故重新解释",
|
||||
queuedSendNoticeAllowed(toldThisRound = false, failedResends = failures),
|
||||
)
|
||||
}
|
||||
|
||||
// —— 二、阈值本身与既有的「一轮只说一次」 ——
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user