Files
openhuman/tests/web_cancel_request_scoping.rs
T

46 lines
2.0 KiB
Rust

//! Request-scoped web-chat cancellation (#4760).
//!
//! Regression for a real in-flight-teardown bug: a client that timed out on
//! request A and then sent request B — which supersedes A on the same thread —
//! had A's late-arriving cancel tear down B, killing the newer turn at t=0
//! ("Cancelled by newer request"). The root cause was that `cancel_chat`
//! cancelled *whatever* turn was in flight for the thread, unscoped by
//! `request_id`.
//!
//! The fix routes the cancel decision through `cancel_should_target`: a scoped
//! cancel (`Some(request_id)`) only fires when it matches the turn actually in
//! flight, so a stale cancel for a superseded request becomes a no-op and the
//! newer turn survives. An unscoped cancel (`None` — Stop button / session
//! teardown) still stops whatever is running.
//!
//! These assertions pin that decision. `cancel_should_target` is a pure
//! predicate, so this is a fast, deterministic unit test with no runtime setup.
use openhuman_core::openhuman::web_chat::cancel_should_target;
#[test]
fn unscoped_cancel_always_targets_the_in_flight_turn() {
// No request_id => "stop whatever is running on this thread" (the Stop
// button / session-teardown path). It always targets the in-flight turn.
assert!(cancel_should_target(None, "req-A"));
assert!(cancel_should_target(None, "req-B"));
}
#[test]
fn scoped_cancel_fires_for_its_own_request() {
// A client cancelling exactly the turn it started tears that turn down.
assert!(cancel_should_target(Some("req-A"), "req-A"));
}
#[test]
fn stale_scoped_cancel_does_not_kill_a_newer_turn() {
// The #4760 bug: request A timed out client-side and B is now the in-flight
// turn on this thread. A's late, request-scoped cancel must NOT tear down B.
assert!(!cancel_should_target(Some("req-A"), "req-B"));
// Symmetric: a cancel naming an already-finished request is inert.
assert!(!cancel_should_target(
Some("old-request"),
"current-request"
));
}