Count distinct devices (by MAC) in active scanner status

The ARP and SNMP scanners reported every online sighting, so a device
seen on multiple IPs or via duplicate ARP replies was counted more than
once. Fold the dedup into ActiveStatus::record_scan, which now takes the
device slice and reports the number of distinct MAC addresses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-06-05 21:19:06 -04:00
co-authored by Claude Opus 4.8
parent 844d7d189c
commit 6b044a6739
6 changed files with 45 additions and 12 deletions
+36 -5
View File
@@ -1,4 +1,6 @@
use crate::model::devices::Device;
use chrono::{DateTime, Utc};
use std::collections::HashSet;
/// Status state for the active (polling) scanners — ARP and SNMP. Each scanner owns its own
/// `OnceCell<Mutex<ActiveStatus>>` and delegates to these methods (see e.g.
@@ -41,9 +43,16 @@ impl ActiveStatus {
self.next_scan_at = Some(next_scan_at);
}
/// Record the result of a completed scan.
pub fn record_scan(&mut self, devices_seen: u64) {
self.last_scan_devices_seen = Some(devices_seen);
/// Record the result of a completed scan. A single scan can surface the same device more than
/// once — duplicate ARP replies, or one MAC bound to several IPs in an SNMP ARP cache — so the
/// reported count is the number of distinct devices (by MAC address), not raw sightings.
pub fn record_scan(&mut self, devices: &[Device]) {
let distinct = devices
.iter()
.map(|device| &device.mac_address)
.collect::<HashSet<_>>()
.len();
self.last_scan_devices_seen = Some(distinct as u64);
self.last_scan_at = Some(Utc::now());
}
@@ -83,15 +92,37 @@ mod tests {
assert_eq!(snapshot.next_scan_at.unwrap(), next);
}
fn device_with_mac(mac: &str, ip: &str) -> Device {
Device::new(mac.to_string(), ip.to_string(), String::new(), Utc::now())
}
#[test]
fn record_scan_stores_count_and_time() {
let mut status = ActiveStatus::new();
status.record_scan(7);
let devices = vec![
device_with_mac("aa:bb:cc:dd:ee:01", "192.168.0.2"),
device_with_mac("aa:bb:cc:dd:ee:02", "192.168.0.3"),
];
status.record_scan(&devices);
let snapshot = status.snapshot();
assert_eq!(snapshot.last_scan_devices_seen, Some(7));
assert_eq!(snapshot.last_scan_devices_seen, Some(2));
assert!(snapshot.last_scan_at.is_some());
}
#[test]
fn record_scan_counts_distinct_devices() {
let mut status = ActiveStatus::new();
// Same MAC seen twice (e.g. duplicate ARP reply or one MAC on two IPs) plus a second
// distinct device — only two unique devices should be reported.
let devices = vec![
device_with_mac("aa:bb:cc:dd:ee:01", "192.168.0.2"),
device_with_mac("aa:bb:cc:dd:ee:01", "192.168.0.9"),
device_with_mac("aa:bb:cc:dd:ee:02", "192.168.0.3"),
];
status.record_scan(&devices);
assert_eq!(status.snapshot().last_scan_devices_seen, Some(2));
}
#[test]
fn initial_state_is_empty() {
let snapshot = ActiveStatus::new().snapshot();