Files
wakey/src/lib.rs
T
lda Verified 134d3d0ef9 fmt + add and wire observation retention
what ever new thing could add stuff in CONTEXT.md and docs/adr/ hmmmmmmmmmmmmmmmmmmmmmm
2026-05-15 16:15:09 +07:00

112 lines
3.8 KiB
Rust

//! Operator-facing service API: inventory, leases, interfaces, wake, and query resolution.
//! The `wakey` binary is Linux-only; use `wakey-agent` / `wakey-control-plane` for remote control.
pub mod service;
pub mod utils;
pub use wakey_linux;
pub use service::{
broadcast_wake_targets, get_interface_summaries, get_interface_summary, get_ips, get_leases,
get_leases_with_neighbor_state, inventory, leases_without_state, local_observation_to_fact,
merge_devices, merge_devices_with_observations, resolve_devices, resolve_query,
resolve_selector, resolve_wake_targets, wake_explicit, wake_from_query, wake_targets,
};
pub use wakey_linux::observations::{
list_local_observations, observe_dhcp_event, observe_neighbor_event,
prune_stale_observations_from_path,
};
#[cfg(test)]
mod tests {
use super::*;
use std::net::{IpAddr, Ipv4Addr};
use wakey_core::{
DhcpLease, NeighborEntry, NeighborState, Presence, Query, WakeStatus, WakeTarget,
};
#[tokio::test]
async fn resolve_query_parses_ip() {
let query = resolve_query("192.168.1.10").await.expect("resolve query");
assert_eq!(
query,
vec![Query::Ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)))]
);
}
#[tokio::test]
async fn resolve_query_parses_mac() {
let query = resolve_query("aa:bb:cc:dd:ee:ff")
.await
.expect("resolve query");
assert_eq!(
query,
vec![Query::Mac("aa:bb:cc:dd:ee:ff".parse().expect("mac"))]
);
}
#[tokio::test]
async fn resolve_query_parses_nud() {
let query = resolve_query("reachable").await.expect("resolve query");
assert_eq!(query, vec![Query::NeighborState(NeighborState::Reachable)]);
}
#[tokio::test]
async fn resolve_selector_keeps_text_vs_structured() {
let selector = resolve_selector("reachable")
.await
.expect("resolve selector");
match selector {
Query::NeighborState(NeighborState::Reachable) => {}
_ => panic!("expected neighbor-state selector"),
}
}
#[test]
fn leases_without_state_clears_nud_state() {
let leases = vec![DhcpLease {
expires_epoch: 1,
ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
mac: "aa:bb:cc:dd:ee:ff".parse().expect("mac"),
name: Some("pc".into()),
}];
let out = leases_without_state(leases);
assert_eq!(out.len(), 1);
assert!(out[0].nud_state.is_none());
}
#[test]
fn merge_devices_combines_lease_and_neighbor() {
let neighbors = vec![NeighborEntry {
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)),
dev: Some("br-lan".into()),
mac: Some("aa:bb:cc:dd:ee:ff".parse().expect("mac")),
state: NeighborState::Reachable,
}];
let leases = vec![wakey_core::DhcpLeaseWithState {
lease_line: DhcpLease {
expires_epoch: 1,
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)),
mac: "aa:bb:cc:dd:ee:ff".parse().expect("mac"),
name: Some("pc".into()),
},
nud_state: None,
}];
let devices = merge_devices(neighbors, leases, &wakey_core::InventoryQuery::default());
assert_eq!(devices.len(), 1);
assert_eq!(devices[0].presence, Presence::Online);
assert_eq!(devices[0].names, vec!["pc".to_string()]);
}
#[tokio::test]
async fn wake_targets_marks_incomplete() {
let out = wake_targets(vec![WakeTarget {
ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
mac: None,
}])
.await
.expect("wake");
assert_eq!(out.result.len(), 1);
assert_eq!(out.result[0].status, WakeStatus::Incomplete);
}
}