mod common;
use common::*;
use rightkit_daemon::contract::{role_scope, Caller};
use std::time::Duration;
#[test]
fn the_device_listing_reports_every_device_without_its_key() {
let h = Harness::new();
let first = h.enrol_scopes(&["venture", "role:operator"]);
h.advance(5);
let second = h.enrol_scopes(&["channel"]);
h.advance(5);
let third = h.enrol_scopes(&["account", "role:phone", "role:operator"]);
h.daemon.revoke_device(&second.id).unwrap();
let list = h.daemon.list_devices();
assert_eq!(list.len(), 3);
assert_eq!(
list.iter()
.map(|d| d.device_id.as_str())
.collect::<Vec<_>>(),
vec![first.id.as_str(), second.id.as_str(), third.id.as_str()]
);
assert_eq!(list[0].role.as_deref(), Some("operator"));
assert_eq!(list[0].enrolled_at, T0);
assert!(!list[0].revoked);
assert!(list[1].revoked);
assert_eq!(list[1].role, None);
assert_eq!(list[2].role, None);
assert!(list.iter().all(|d| !d.linked));
let rendered = format!("{list:?}");
assert!(!rendered.contains(&first.public_key_b64()));
}
#[test]
fn a_forgotten_device_leaves_the_listing() {
let h = Harness::new();
let dev = h.enrol();
h.daemon.revoke_device(&dev.id).unwrap();
assert_eq!(h.daemon.list_devices().len(), 1);
h.daemon.forget_device(&dev.id).unwrap();
assert!(h.daemon.list_devices().is_empty());
}
#[test]
fn a_cursor_reads_lifecycle_events_in_order() {
let h = Harness::new();
let first = h.daemon.events_since(None, 0, 100);
let before = first.events.len();
assert_eq!(first.next as usize, before);
let dev = h.enrol();
h.daemon.revoke_device(&dev.id).unwrap();
let page = h.daemon.events_since(Some(&first.epoch), first.next, 100);
assert!(!page.reset);
let names: Vec<&str> = page
.events
.iter()
.filter_map(|e| e["event"].as_str())
.collect();
assert!(names.contains(&"ENROLLED"), "{names:?}");
assert!(names.contains(&"DEVICE_REVOKED"), "{names:?}");
let seqs: Vec<u64> = page
.events
.iter()
.filter_map(|e| e["seq"].as_u64())
.collect();
assert!(seqs.windows(2).all(|w| w[1] == w[0] + 1), "{seqs:?}");
let quiet = h.daemon.events_since(Some(&page.epoch), page.next, 100);
assert!(quiet.events.is_empty());
assert_eq!(quiet.next, page.next);
let foreign = h.daemon.events_since(Some("another-run"), page.next, 100);
assert!(foreign.reset);
}
#[test]
fn waiting_for_events_returns_when_one_is_written() {
let h = Harness::new();
let start = h.daemon.events_since(None, 0, 100);
let daemon = h.daemon.clone();
let epoch = start.epoch.clone();
let waiter = std::thread::spawn(move || {
daemon.wait_events(Some(&epoch), start.next, 100, Duration::from_secs(20))
});
std::thread::sleep(Duration::from_millis(100));
h.daemon.emit("OPERATOR_PING", serde_json::json!({}));
let page = waiter.join().unwrap();
assert_eq!(page.events.len(), 1);
assert_eq!(page.events[0]["event"], "OPERATOR_PING");
}
#[test]
fn a_device_caller_has_the_role_its_enrolment_granted() {
let operator = Caller::device("dev_1", ["venture".to_string(), role_scope("operator")]);
assert_eq!(operator.role(), Some("operator"));
let none = Caller::device("dev_2", ["venture"]);
assert_eq!(none.role(), None);
let two = Caller::device("dev_3", [role_scope("operator"), role_scope("phone")]);
assert_eq!(two.role(), None);
let empty = Caller::device("dev_4", ["role:"]);
assert_eq!(empty.role(), None);
}