so what are these? auto gc and typed observation (because some flett shit is depending on ts)

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
ldaandCopilot committed 2026-05-01 19:25:03 +07:00 Verified
1 parent b5c0626250
commit b5a28c801c
39 files changed
+874 -327

No files matched your search

+1 -156
View File
@@ -1,47 +1,20 @@
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
mod leases;
mod observations;
pub use leases::{
enrich_leases_with_nud_state, parse_dhcp_lease_line, read_dhcp_leases,
read_dhcp_leases_from_path, read_dhcp_leases_with_names,
read_dhcp_leases_with_names_from_paths,
};
pub use observations::{
LocalDeviceObservation, LocalObservationStore, ObservedDhcpClient, ObservedNeighbor,
list_local_observations, list_local_observations_from_path, load_mac_name_cache,
load_mac_name_cache_from_path, load_observation_store, load_observation_store_from_path,
observe_dhcp_event, observe_neighbor_event,
};
const DEFAULT_DHCP_LEASES: &str = "/tmp/dhcp.leases";
const DEFAULT_MAC_NAME_CACHE: &str = "/tmp/wakey_mac_names.json";
const DEFAULT_OBSERVATION_STORE: &str = "/tmp/wakey_observations.json";
const DHCP_LEASES_ENV: &str = "WAKEY_DHCP_LEASES";
const MAC_NAME_CACHE_ENV: &str = "WAKEY_MAC_NAME_CACHE";
const OBSERVATION_STORE_ENV: &str = "WAKEY_OBSERVATION_STORE";
pub(crate) fn now_unix() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_secs())
.unwrap_or(0)
}
pub(crate) fn dhcp_leases_path() -> PathBuf {
configured_path(DHCP_LEASES_ENV, DEFAULT_DHCP_LEASES)
}
pub(crate) fn mac_name_cache_path() -> PathBuf {
configured_path(MAC_NAME_CACHE_ENV, DEFAULT_MAC_NAME_CACHE)
}
pub(crate) fn observation_store_path() -> PathBuf {
configured_path(OBSERVATION_STORE_ENV, DEFAULT_OBSERVATION_STORE)
}
fn configured_path(env_key: &str, default: &str) -> PathBuf {
std::env::var_os(env_key)
.filter(|value| !value.is_empty())
@@ -53,6 +26,7 @@ fn configured_path(env_key: &str, default: &str) -> PathBuf {
mod tests {
use super::*;
use serial_test::serial;
use std::time::{SystemTime, UNIX_EPOCH};
struct EnvGuard {
keys: Vec<&'static str>,
@@ -107,133 +81,4 @@ mod tests {
let _ = tokio::fs::remove_file(path).await;
}
#[tokio::test]
#[serial]
async fn observation_and_name_cache_paths_can_be_overridden() {
let observation_path = temp_file("observations");
let cache_path = temp_file("names");
let _observation_guard = EnvGuard::set(OBSERVATION_STORE_ENV, &observation_path);
let _cache_guard = EnvGuard::set(MAC_NAME_CACHE_ENV, &cache_path);
let changed = observe_dhcp_event(
"add",
"aa:bb:cc:dd:ee:ff".parse().expect("mac should parse"),
Some("192.168.1.2".parse().expect("ip should parse")),
Some("lda"),
)
.await
.expect("observation should write");
assert!(changed);
let store = load_observation_store()
.await
.expect("observation store should read");
assert!(store.dhcp_clients.contains_key("aa:bb:cc:dd:ee:ff"));
let cache = load_mac_name_cache().await.expect("name cache should read");
assert_eq!(cache.get("aa:bb:cc:dd:ee:ff"), Some(&"lda".to_string()));
let _ = tokio::fs::remove_file(observation_path).await;
let _ = tokio::fs::remove_file(cache_path).await;
}
#[tokio::test]
#[serial]
async fn neighbor_observations_are_keyed_by_mac_ip_pair() {
let observation_path = temp_file("neighbor-observations");
let _observation_guard = EnvGuard::set(OBSERVATION_STORE_ENV, &observation_path);
let mac = "aa:bb:cc:dd:ee:ff".parse().expect("mac should parse");
observe_neighbor_event(
"add",
Some(mac),
Some("192.168.1.2".parse().expect("ip should parse")),
)
.await
.expect("first observation should write");
observe_neighbor_event(
"update",
Some(mac),
Some("192.168.1.3".parse().expect("ip should parse")),
)
.await
.expect("second observation should write");
let store = load_observation_store()
.await
.expect("observation store should read");
assert!(
store
.neighbors
.contains_key("mac:aa:bb:cc:dd:ee:ff:ip:192.168.1.2")
);
assert!(
store
.neighbors
.contains_key("mac:aa:bb:cc:dd:ee:ff:ip:192.168.1.3")
);
assert_eq!(
store.neighbors["mac:aa:bb:cc:dd:ee:ff:ip:192.168.1.2"].last_action,
"remove"
);
let _ = tokio::fs::remove_file(observation_path).await;
}
#[tokio::test]
#[serial]
async fn neighbor_observation_migrates_coarse_mac_key_to_mac_ip_pair() {
let observation_path = temp_file("neighbor-observations-migrate");
let _observation_guard = EnvGuard::set(OBSERVATION_STORE_ENV, &observation_path);
let mut neighbors = std::collections::BTreeMap::new();
neighbors.insert(
"mac:aa:bb:cc:dd:ee:ff".to_string(),
ObservedNeighbor {
key: "mac:aa:bb:cc:dd:ee:ff".to_string(),
mac: Some("aa:bb:cc:dd:ee:ff".to_string()),
ip: None,
first_seen_unix: 1,
last_seen_unix: 1,
last_action: "add".to_string(),
},
);
let fixture = LocalObservationStore {
dhcp_clients: Default::default(),
neighbors,
};
tokio::fs::write(
&observation_path,
serde_json::to_string(&fixture).expect("fixture should serialize"),
)
.await
.expect("fixture should write");
observe_neighbor_event(
"update",
Some("aa:bb:cc:dd:ee:ff".parse().expect("mac should parse")),
Some("192.168.1.2".parse().expect("ip should parse")),
)
.await
.expect("observation should write");
let store = load_observation_store()
.await
.expect("observation store should read");
assert!(!store.neighbors.contains_key("mac:aa:bb:cc:dd:ee:ff"));
let row = store
.neighbors
.get("mac:aa:bb:cc:dd:ee:ff:ip:192.168.1.2")
.expect("coarse key should migrate to pair key");
assert_eq!(row.first_seen_unix, 1);
assert_eq!(row.mac.as_deref(), Some("aa:bb:cc:dd:ee:ff"));
assert_eq!(
row.ip
.expect("ip should be carried into migrated row")
.to_string(),
"192.168.1.2"
);
assert_eq!(row.last_action, "update");
let _ = tokio::fs::remove_file(observation_path).await;
}
}