test(session): pin offline-queue session_id stamping (#487 follow-up)

The #487 fix relies on `save_offline_queue_state` correctly
stamping the session id so the load path's mismatch check has
something to compare against. The existing
`test_offline_queue_round_trip_and_clear` covers
serialization + clear but doesn't pin the session_id stamping
behavior.

Adds `test_offline_queue_stamps_session_id_on_save` which
exercises three cases:

* `save(state, Some("session-A"))` → loaded session_id is
  `Some("session-A")`. The stamp made it to disk.
* `save(state, Some("session-B"))` → re-saving replaces the
  stamp; loaded session_id is `Some("session-B")`. No stale
  ID lingers.
* `save(state, None)` → loaded session_id is `None`. The UI's
  load path treats this as legacy-unscoped and refuses to
  restore (fail-closed), which is what protects users from
  pre-#487 queues leaking into new chats.

Pure additive coverage. The 2 existing offline-queue tests
pass unchanged.
This commit is contained in:
Hunter Bown
2026-05-03 07:16:11 -05:00
parent a4c8cb2514
commit 0fe05b682a
+54
View File
@@ -967,6 +967,60 @@ mod tests {
);
}
#[test]
fn test_offline_queue_stamps_session_id_on_save() {
// #487: save_offline_queue_state must stamp the supplied
// session id so the load path's mismatch check has something
// to compare against. A queue persisted without a session id
// is the legacy unscoped form which the load path treats as
// stale-risky and refuses to restore.
let tmp = tempdir().expect("tempdir");
let manager = SessionManager::new(tmp.path().join("sessions")).expect("new");
let state = OfflineQueueState {
messages: vec![QueuedSessionMessage {
display: "first parked".to_string(),
skill_instruction: None,
}],
..OfflineQueueState::default()
};
manager
.save_offline_queue_state(&state, Some("session-A"))
.expect("save with session id");
let loaded = manager
.load_offline_queue_state()
.expect("ok")
.expect("present");
assert_eq!(loaded.session_id.as_deref(), Some("session-A"));
// Re-saving with a different session id replaces the stamp.
manager
.save_offline_queue_state(&state, Some("session-B"))
.expect("re-save");
let reloaded = manager
.load_offline_queue_state()
.expect("ok")
.expect("present");
assert_eq!(reloaded.session_id.as_deref(), Some("session-B"));
// Saving without a session id explicitly (None) clears the
// stamp — UI's load path treats that as legacy-unscoped and
// fails closed.
manager
.save_offline_queue_state(&state, None)
.expect("save without session id");
let unscoped = manager
.load_offline_queue_state()
.expect("ok")
.expect("present");
assert!(
unscoped.session_id.is_none(),
"save with None must persist a missing session_id, got {:?}",
unscoped.session_id
);
}
#[test]
fn test_session_context_references_round_trip() {
let tmp = tempdir().expect("tempdir");