use std::time::Duration;
use libgrite_ipc::DaemonLock;
use tempfile::tempdir;
#[test]
fn test_daemon_lock_lifecycle() {
let temp = tempdir().unwrap();
let data_dir = temp.path().to_path_buf();
let _lock = DaemonLock::acquire(
&data_dir,
"/tmp/test-repo".to_string(),
"abc123".to_string(),
"test-host".to_string(),
"/tmp/test.sock".to_string(),
)
.unwrap();
let lock_path = data_dir.join("daemon.lock");
assert!(lock_path.exists());
let read_lock = DaemonLock::read(&data_dir).unwrap().unwrap();
assert_eq!(read_lock.repo_root, "/tmp/test-repo");
assert_eq!(read_lock.actor_id, "abc123");
assert!(!read_lock.is_expired());
DaemonLock::release(&data_dir).unwrap();
assert!(!lock_path.exists());
}
#[test]
fn test_daemon_lock_double_acquire_fails() {
let temp = tempdir().unwrap();
let data_dir = temp.path().to_path_buf();
let _lock1 = DaemonLock::acquire(
&data_dir,
"/tmp/test-repo".to_string(),
"abc123".to_string(),
"test-host".to_string(),
"/tmp/test1.sock".to_string(),
)
.unwrap();
let result = DaemonLock::acquire(
&data_dir,
"/tmp/test-repo".to_string(),
"abc123".to_string(),
"other-host".to_string(),
"/tmp/test2.sock".to_string(),
);
assert!(result.is_err());
}
#[test]
fn test_daemon_lock_expired_takeover() {
let temp = tempdir().unwrap();
let data_dir = temp.path().to_path_buf();
let mut lock = DaemonLock::new(
99999, "/tmp/test-repo".to_string(),
"abc123".to_string(),
"old-host".to_string(),
"/tmp/old.sock".to_string(),
);
lock.expires_ts = lock.started_ts - 1000;
lock.write(&data_dir).unwrap();
let read_lock = DaemonLock::read(&data_dir).unwrap().unwrap();
assert!(read_lock.is_expired());
let _new_lock = DaemonLock::acquire(
&data_dir,
"/tmp/test-repo".to_string(),
"abc123".to_string(),
"new-host".to_string(),
"/tmp/new.sock".to_string(),
)
.unwrap();
let read_lock = DaemonLock::read(&data_dir).unwrap().unwrap();
assert_eq!(read_lock.host_id, "new-host");
assert!(!read_lock.is_expired());
}
#[test]
fn test_daemon_lock_heartbeat_refresh() {
let temp = tempdir().unwrap();
let data_dir = temp.path().to_path_buf();
let mut lock = DaemonLock::acquire(
&data_dir,
"/tmp/test-repo".to_string(),
"abc123".to_string(),
"test-host".to_string(),
"/tmp/test.sock".to_string(),
)
.unwrap();
let original_expires = lock.expires_ts;
let original_heartbeat = lock.last_heartbeat_ts;
std::thread::sleep(Duration::from_millis(10));
lock.refresh();
lock.write(&data_dir).unwrap();
assert!(lock.last_heartbeat_ts > original_heartbeat);
assert!(lock.expires_ts > original_expires);
let read_lock = DaemonLock::read(&data_dir).unwrap().unwrap();
assert!(read_lock.last_heartbeat_ts > original_heartbeat);
}
#[test]
fn test_ipc_message_roundtrip() {
use libgrite_ipc::messages::{IpcRequest, IpcResponse};
use libgrite_ipc::IpcCommand;
let request = IpcRequest::new(
"req-123".to_string(),
"/tmp/repo".to_string(),
"actor123".to_string(),
"/tmp/data".to_string(),
IpcCommand::IssueList {
state: Some("open".to_string()),
label: None,
},
);
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&request).unwrap();
let archived = rkyv::access::<rkyv::Archived<IpcRequest>, rkyv::rancor::Error>(&bytes).unwrap();
let restored: IpcRequest =
rkyv::deserialize::<IpcRequest, rkyv::rancor::Error>(archived).unwrap();
assert_eq!(restored.request_id, "req-123");
assert_eq!(restored.repo_root, "/tmp/repo");
let response =
IpcResponse::success("req-123".to_string(), Some(r#"{"issues":[]}"#.to_string()));
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&response).unwrap();
let archived =
rkyv::access::<rkyv::Archived<IpcResponse>, rkyv::rancor::Error>(&bytes).unwrap();
let restored: IpcResponse =
rkyv::deserialize::<IpcResponse, rkyv::rancor::Error>(archived).unwrap();
assert!(restored.ok);
assert_eq!(restored.data, Some(r#"{"issues":[]}"#.to_string()));
}
#[test]
fn test_notification_roundtrip() {
use libgrite_ipc::Notification;
let notification = Notification::EventApplied {
issue_id: "issue123".to_string(),
event_id: "event456".to_string(),
ts_unix_ms: 1700000000000,
};
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(¬ification).unwrap();
let archived =
rkyv::access::<rkyv::Archived<Notification>, rkyv::rancor::Error>(&bytes).unwrap();
let restored: Notification =
rkyv::deserialize::<Notification, rkyv::rancor::Error>(archived).unwrap();
match restored {
Notification::EventApplied {
issue_id,
event_id,
ts_unix_ms,
} => {
assert_eq!(issue_id, "issue123");
assert_eq!(event_id, "event456");
assert_eq!(ts_unix_ms, 1700000000000);
}
_ => panic!("Wrong notification type"),
}
}