feat: Health command — version, pid, started_at (proto + daemon)

This commit is contained in:
2026-06-10 12:01:54 +07:00
parent f18d929c10
commit f7763a84fc
2 changed files with 46 additions and 1 deletions
+10
View File
@@ -122,6 +122,7 @@ pub enum Cmd {
limit: Option<u32>,
},
MarkRead { target: MarkReadTarget },
Health,
Status,
Shutdown,
}
@@ -321,6 +322,15 @@ mod tests {
assert_eq!(back, evt);
}
#[test]
fn health_cmd_round_trips() {
let env = Envelope::Req { id: 1, cmd: Cmd::Health };
let j = serde_json::to_string(&env).unwrap();
assert!(j.contains(r#""cmd":"health""#));
let back: Envelope = serde_json::from_str(&j).unwrap();
assert_eq!(back, env);
}
#[test]
fn event_log_cmd_no_limit_round_trips() {
let env = Envelope::Req { id: 9, cmd: Cmd::EventLog { limit: None } };
+36 -1
View File
@@ -62,9 +62,11 @@ pub async fn serve(socket: &Path, store: Arc<dyn StateStore>, event_store: Arc<d
let initial = store.load().unwrap_or_default();
let event_persister = event_store::spawn(event_store.clone(), Duration::from_millis(500));
let event_initial = event_store.load().unwrap_or_default();
let started_at_ms = now_millis();
let shutdown = tokio::spawn(router(
router_rx, router_tx.clone(), exit_tx, state_tx,
persister, initial, event_persister, event_initial,
started_at_ms,
));
let mut next_client: ClientId = 0;
@@ -125,6 +127,7 @@ async fn router(
initial: crate::state_store::PersistState,
event_persister: EventPersister,
event_initial: crate::event_log::EventLogState,
started_at_ms: u64,
) {
let mut reg = Registry::new();
reg.restore(initial);
@@ -174,7 +177,7 @@ async fn router(
ServerMsg::Request { id, cmd, client, out } => {
handle_request(id, cmd, client, out, &mut reg, &mut subs, &clients,
&router_tx, &exit_tx, &state_tx, &persister,
&mut event_log, &event_persister).await;
&mut event_log, &event_persister, started_at_ms).await;
}
}
}
@@ -272,6 +275,7 @@ async fn handle_request(
persister: &Persister,
event_log: &mut EventLog,
event_persister: &EventPersister,
started_at_ms: u64,
) {
use spacesh_proto::message::SplitDir;
use spacesh_proto::layout::{LayoutNode, Orient};
@@ -590,6 +594,14 @@ async fn handle_request(
}
}
Cmd::Health => {
let _ = out.send(ok(id, serde_json::json!({
"version": env!("CARGO_PKG_VERSION"),
"pid": std::process::id(),
"started_at_ms": started_at_ms,
}))).await;
}
Cmd::Status => {
let (groups, workspaces) = reg.status();
let _ = out.send(ok(id, serde_json::json!({ "groups": groups, "workspaces": workspaces }))).await;
@@ -1300,4 +1312,27 @@ mod tests {
let log = req(&mut s, 6, Cmd::EventLog { limit: None }).await;
assert_eq!(res_data(&log)["unread"].as_u64().unwrap(), 0);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn health_reports_version_pid_started() {
let _serial = crate::test_support::serial();
let dir = tempdir_path();
let sock = dir.join("sock");
let store: std::sync::Arc<dyn crate::state_store::StateStore> =
std::sync::Arc::new(crate::state_store::JsonStateStore::new(dir.join("state.json")));
let event_store = make_event_store(&dir);
let sock_for_task = sock.clone();
let store2 = store.clone();
tokio::spawn(async move { let _ = serve(&sock_for_task, store2, event_store).await; });
wait_for_socket(&sock).await;
let mut s = UnixStream::connect(&sock).await.unwrap();
let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64;
let r = req(&mut s, 1, Cmd::Health).await;
let d = res_data(&r);
assert!(!d["version"].as_str().unwrap().is_empty());
assert!(d["pid"].as_u64().unwrap() > 0);
let started = d["started_at_ms"].as_u64().unwrap();
assert!(started > 0 && started >= now.saturating_sub(5000) && started <= now + 1000, "started_at_ms plausible: {started} vs now {now}");
}
}