use tracing::{info, warn};
use crate::obs_outbox;
pub fn emit(pc_id: &str, agent_version: &str, obs_outbox_dir: &std::path::Path) {
let event = kanade_shared::wire::ObsEvent {
pc_id: pc_id.to_string(),
at: chrono::Utc::now(),
kind: "agent_online".to_string(),
source: "agent:startup".to_string(),
event_record_id: Some(format!("startup_{}", uuid::Uuid::new_v4().simple())),
payload: serde_json::json!({
"agent_version": agent_version,
"boot_time": match sysinfo::System::boot_time() {
0 => None,
secs => Some(secs),
},
}),
};
let res = obs_outbox::ensure_outbox_dir(obs_outbox_dir)
.and_then(|()| obs_outbox::enqueue(obs_outbox_dir, &event).map(|_| ()));
match res {
Ok(()) => info!(agent_version, "queued agent_online obs event"),
Err(e) => warn!(error = %e, "failed to queue agent_online obs event"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enqueues_a_file_the_drain_can_ship() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("obs-outbox");
emit("pc1", "0.44.29", &path);
let files: Vec<_> = std::fs::read_dir(&path)
.unwrap()
.filter_map(Result::ok)
.filter(|e| e.path().extension().is_some_and(|x| x == "json"))
.collect();
assert_eq!(files.len(), 1, "one queued event");
let raw = std::fs::read(files[0].path()).unwrap();
let ev: kanade_shared::wire::ObsEvent = serde_json::from_slice(&raw).unwrap();
assert_eq!(ev.kind, "agent_online");
assert_eq!(ev.source, "agent:startup");
assert_eq!(ev.pc_id, "pc1");
assert_eq!(ev.payload["agent_version"], "0.44.29");
assert!(
ev.event_record_id.is_some(),
"must not be None: NULL never dedups, but more importantly every start is its own event"
);
}
#[test]
fn two_starts_are_two_events() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("obs-outbox");
emit("pc1", "0.44.29", &path);
emit("pc1", "0.44.29", &path);
let n = std::fs::read_dir(&path)
.unwrap()
.filter_map(Result::ok)
.filter(|e| e.path().extension().is_some_and(|x| x == "json"))
.count();
assert_eq!(n, 2);
}
#[test]
fn an_unusable_directory_does_not_panic() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("obs-outbox");
std::fs::write(&path, b"not a directory").unwrap();
emit("pc1", "0.44.29", &path);
}
}