test(daemon): event log survives cold daemon restart
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1163,6 +1163,82 @@ mod tests {
|
|||||||
assert_eq!(res_data(&log)["unread"].as_u64().unwrap(), 0);
|
assert_eq!(res_data(&log)["unread"].as_u64().unwrap(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn event_log_persists_across_daemon_restart() {
|
||||||
|
let _serial = crate::test_support::serial();
|
||||||
|
let dir = tempdir_path();
|
||||||
|
let state_path = dir.join("state.json");
|
||||||
|
let sock = dir.join("sock");
|
||||||
|
|
||||||
|
let event_id: u64;
|
||||||
|
let ws_id: String;
|
||||||
|
|
||||||
|
// ── Instance A ────────────────────────────────────────────────────────
|
||||||
|
{
|
||||||
|
let store: std::sync::Arc<dyn crate::state_store::StateStore> =
|
||||||
|
std::sync::Arc::new(crate::state_store::JsonStateStore::new(state_path.clone()));
|
||||||
|
let event_store = make_event_store(&dir);
|
||||||
|
let sock2 = sock.clone();
|
||||||
|
tokio::spawn(async move { let _ = serve(&sock2, store, event_store).await; });
|
||||||
|
wait_for_socket(&sock).await;
|
||||||
|
|
||||||
|
let mut s = UnixStream::connect(&sock).await.unwrap();
|
||||||
|
|
||||||
|
// Open workspace, spawn surface.
|
||||||
|
let r = req(&mut s, 1, Cmd::Open { path: std::env::temp_dir().to_string_lossy().into() }).await;
|
||||||
|
ws_id = res_data(&r)["workspace_id"].as_str().unwrap().to_string();
|
||||||
|
|
||||||
|
let r = req(&mut s, 2, Cmd::NewSurface {
|
||||||
|
workspace_id: spacesh_proto::WorkspaceId(ws_id.clone()),
|
||||||
|
command: Some("/bin/sh".into()),
|
||||||
|
args: vec!["-c".into(), "sleep 5".into()],
|
||||||
|
cols: 80, rows: 24,
|
||||||
|
}).await;
|
||||||
|
let sid = res_data(&r)["surface_id"].as_str().unwrap().to_string();
|
||||||
|
|
||||||
|
// Drive an Error state → one unread event is logged.
|
||||||
|
let _ = req(&mut s, 3, Cmd::SetState {
|
||||||
|
surface_id: spacesh_proto::SurfaceId(sid.clone()),
|
||||||
|
state: spacesh_proto::status::SurfaceState::Error,
|
||||||
|
}).await;
|
||||||
|
|
||||||
|
// Query and assert unread == 1 before restart.
|
||||||
|
let log = req(&mut s, 4, Cmd::EventLog { limit: None }).await;
|
||||||
|
let data = res_data(&log);
|
||||||
|
assert_eq!(data["unread"].as_u64().unwrap(), 1, "instance A: expected 1 unread event");
|
||||||
|
assert_eq!(data["events"][0]["kind"].as_str().unwrap(), "error");
|
||||||
|
assert_eq!(data["events"][0]["workspace_id"].as_str().unwrap(), ws_id);
|
||||||
|
event_id = data["events"][0]["id"].as_u64().unwrap();
|
||||||
|
|
||||||
|
// Wait comfortably longer than the 500 ms debounce so events.json is flushed.
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(900)).await;
|
||||||
|
// Drop `s` (and instance A's task) by falling out of scope.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Instance B (same dir, fresh socket path) ──────────────────────────
|
||||||
|
let sock_b = dir.join("sock2");
|
||||||
|
let store_b: std::sync::Arc<dyn crate::state_store::StateStore> =
|
||||||
|
std::sync::Arc::new(crate::state_store::JsonStateStore::new(state_path.clone()));
|
||||||
|
let event_store_b = make_event_store(&dir);
|
||||||
|
let sb2 = sock_b.clone();
|
||||||
|
tokio::spawn(async move { let _ = serve(&sock_b, store_b, event_store_b).await; });
|
||||||
|
wait_for_socket(&sb2).await;
|
||||||
|
|
||||||
|
let mut s2 = UnixStream::connect(&sb2).await.unwrap();
|
||||||
|
|
||||||
|
// Query event log on instance B — the persisted event must survive the restart.
|
||||||
|
let log = req(&mut s2, 1, Cmd::EventLog { limit: None }).await;
|
||||||
|
let data = res_data(&log);
|
||||||
|
assert_eq!(data["unread"].as_u64().unwrap(), 1,
|
||||||
|
"instance B: event log unread count must survive cold restart");
|
||||||
|
assert_eq!(data["events"][0]["id"].as_u64().unwrap(), event_id,
|
||||||
|
"instance B: event id must match");
|
||||||
|
assert_eq!(data["events"][0]["kind"].as_str().unwrap(), "error",
|
||||||
|
"instance B: event kind must be 'error'");
|
||||||
|
assert_eq!(data["events"][0]["workspace_id"].as_str().unwrap(), ws_id,
|
||||||
|
"instance B: workspace_id must match");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn focus_marks_surface_events_read() {
|
async fn focus_marks_surface_events_read() {
|
||||||
let _serial = crate::test_support::serial();
|
let _serial = crate::test_support::serial();
|
||||||
|
|||||||
Reference in New Issue
Block a user