mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Fix "device changed" notification showing stored devices as unregistered
record_sighting only carried the stored name and IP onto the reconciled sighting, leaving is_registered, owner, and device_type at their bare defaults. The change handed to the notification layer therefore rendered a registered, typed device as "Not registered" with type "-". Carry those fields over too, mirroring how db::devices::seen() preserves them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
8b2c8e7285
commit
916b081225
@@ -15,7 +15,10 @@ use crate::notifications;
|
||||
/// When the device is already known, the sighting is reconciled with the stored record:
|
||||
/// - a previously stored hostname is kept rather than overwritten by this sighting's name;
|
||||
/// - a known IP address is kept when this sighting carries none (an empty string), so a DHCP
|
||||
/// DISCOVER (which has no assigned IP) never clobbers a good address.
|
||||
/// DISCOVER (which has no assigned IP) never clobbers a good address;
|
||||
/// - registration, owner, and a non-empty stored vendor/device_type are carried over, so the
|
||||
/// change passed to the notification layer describes the device as it is stored rather than as
|
||||
/// this bare sighting saw it.
|
||||
///
|
||||
/// Errors are logged and swallowed (an empty list is returned): a scan/listen loop must never stop
|
||||
/// because a single sighting failed to persist.
|
||||
@@ -29,6 +32,19 @@ pub fn record_sighting(mut device: Device, scanner: DeviceEventScanner) -> Vec<D
|
||||
if device.ipv4_address.is_empty() {
|
||||
device.ipv4_address = recorded.ipv4_address.clone();
|
||||
}
|
||||
// Carry forward the fields a sighting can never establish or improve, mirroring how
|
||||
// db::devices::seen() preserves them: registration and owner are only ever set via the
|
||||
// UI, and a non-empty stored vendor/device_type wins over what this sighting deduced.
|
||||
// Without this, the reconciled device (used for notifications) would report a
|
||||
// registered, typed device as "Not registered" with type "-".
|
||||
device.is_registered = recorded.is_registered;
|
||||
device.owner = recorded.owner.clone();
|
||||
if device.vendor.is_empty() {
|
||||
device.vendor = recorded.vendor.clone();
|
||||
}
|
||||
if !recorded.device_type.is_empty() {
|
||||
device.device_type = recorded.device_type.clone();
|
||||
}
|
||||
if let Err(err) = db::devices::seen(
|
||||
device.mac_address.clone(),
|
||||
device.ipv4_address.clone(),
|
||||
@@ -87,6 +103,51 @@ mod tests {
|
||||
assert_eq!(stored.name, Some("printer".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn changed_notification_reflects_stored_registration_and_type() {
|
||||
tests_common::setup().await;
|
||||
let mac = "de:ad:be:ef:00:03".to_string();
|
||||
|
||||
// A device is discovered, then registered with an owner and a device type.
|
||||
let first = Device::new(
|
||||
mac.clone(),
|
||||
"192.168.9.3".to_string(),
|
||||
"Acme".to_string(),
|
||||
Utc::now(),
|
||||
);
|
||||
record_sighting(first, DeviceEventScanner::Arp);
|
||||
db::devices::register(
|
||||
mac.clone(),
|
||||
"Alice".to_string(),
|
||||
"Smartphone".to_string(),
|
||||
None,
|
||||
)
|
||||
.expect("register should succeed");
|
||||
|
||||
// It is later seen at a different IP by a scanner that carries no vendor or type.
|
||||
let moved = Device::new(
|
||||
mac.clone(),
|
||||
"192.168.9.99".to_string(),
|
||||
String::new(),
|
||||
Utc::now(),
|
||||
);
|
||||
let changes = record_sighting(moved, DeviceEventScanner::Arp);
|
||||
|
||||
let new = changes
|
||||
.iter()
|
||||
.find_map(|change| match change {
|
||||
DeviceChange::Changed { new, .. } => Some(new),
|
||||
_ => None,
|
||||
})
|
||||
.expect("an IP change should be reported");
|
||||
|
||||
// The change must describe the device as it is stored, not as the bare sighting saw it.
|
||||
assert!(new.is_registered);
|
||||
assert_eq!(new.owner, "Alice");
|
||||
assert_eq!(new.device_type, "Smartphone");
|
||||
assert_eq!(new.vendor, "Acme");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn known_device_keeps_stored_name_and_ip() {
|
||||
tests_common::setup().await;
|
||||
|
||||
Reference in New Issue
Block a user