补中途加入者的合并快照用例

- 新增 `late_subscriber_sees_the_running_turn_and_the_pending_queue_together`
- 钉住:正在跑那一轮的 `turn.started` 锚点(带开口条目身份)与还排着的 `queue.enqueued` 同时进 bootstrap,顺序按事件序
- 原有用例各覆盖一半(一条只测队列、一条只测锚点),这条覆盖两者同时存在的中途加入场景
This commit is contained in:
2026-09-30 12:43:20 +08:00
parent 75f9bbacb1
commit d341a9be13
@@ -1422,6 +1422,44 @@ mod tests {
assert_eq!(enqueued_ids(&bootstrap.events), vec!["turn-2"]);
}
/// 中途加入者拿到的是**合并快照**:正在跑那一轮的锚点 + 还没走的队列,顺序就是事件顺序。
///
/// 队列搬到宿主之后这件事只能由 live-set bootstrap 顶上:另一个窗口、离开工作台再回来,
/// 看到的状态必须和一直在场的那位一致——在跑的那一轮还在跑(锚点带开口条目身份,条目挂得回去),
/// 排着的那几条还在队,而不是只剩其中一个。
#[test]
fn late_subscriber_sees_the_running_turn_and_the_pending_queue_together() {
let mut manager = DirectThreadManager::with_limits(100, 100_000);
manager
.enqueue_pending_turn("thread-1", pending_turn("turn-1"))
.expect("enqueue head");
manager
.claim_pending_turn("thread-1", FIXED_AT_MS)
.expect("claim head");
manager
.enqueue_pending_turn("thread-1", pending_turn("turn-2"))
.expect("enqueue tail");
let bootstrap = manager.subscribe("thread-1");
assert!(
matches!(
bootstrap.events.as_slice(),
[
DirectThreadEvent::TurnStarted { .. },
DirectThreadEvent::QueueEnqueued { client_turn_id, .. }
] if client_turn_id == "turn-2"
),
"中途加入者要先看到在跑的那一轮、再看到还排着的那几条:{:?}",
bootstrap.events
);
assert_eq!(
bootstrap.events[0].user_item_id(),
Some("direct-codex:turn-1:user"),
"锚点必须带着正在跑那一轮的开口条目身份"
);
assert_eq!(enqueued_ids(&bootstrap.events), vec!["turn-2"]);
}
/// 入队幂等:判重范围是「在队 ∪ 正在跑的那一轮」,重复入队不排第二条、不发第二条事件。
#[test]
fn enqueue_is_idempotent_per_client_turn_id() {