mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Added list devices to database handler
This commit is contained in:
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
rm -f oott.db
|
||||
cargo test -- --show-output
|
||||
+357
-94
@@ -1,11 +1,95 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{debug, error};
|
||||
use rusqlite::params;
|
||||
use rusqlite::{ToSql, params, params_from_iter};
|
||||
|
||||
use crate::{
|
||||
db::{self, error::DbError},
|
||||
model::devices::Device,
|
||||
};
|
||||
|
||||
pub fn list_devices(
|
||||
is_registered: Option<bool>,
|
||||
last_seen_from: Option<DateTime<Utc>>,
|
||||
last_seen_to: Option<DateTime<Utc>>,
|
||||
owner: Option<String>,
|
||||
device_type: Option<String>,
|
||||
vendor: Option<String>,
|
||||
) -> Result<Vec<Device>, DbError> {
|
||||
debug!("Listing devices");
|
||||
let conn = db::get_db_connection();
|
||||
|
||||
// Prepare SQL and parameters
|
||||
let mut sql_statement = "SELECT mac_address, ipv4_address, vendor, last_seen, is_registered, owner, device_type FROM devices WHERE 1=1 ".to_string();
|
||||
let mut params: Vec<rusqlite::types::Value> = Vec::new();
|
||||
if is_registered.is_some() {
|
||||
debug!("Adding filter is_registered={}", is_registered.unwrap());
|
||||
sql_statement.push_str("AND is_registered=? ");
|
||||
|
||||
params.push(is_registered.unwrap().into());
|
||||
};
|
||||
if last_seen_from.is_some() {
|
||||
debug!(
|
||||
"Adding filter last_seen>={}",
|
||||
last_seen_from.unwrap().format("%Y-%m-%d %H:%M:%S")
|
||||
);
|
||||
sql_statement.push_str("AND last_seen>=? ");
|
||||
params.push(
|
||||
last_seen_from
|
||||
.unwrap()
|
||||
.format("%Y-%m-%d %H:%M:%S")
|
||||
.to_string()
|
||||
.into(),
|
||||
);
|
||||
};
|
||||
if last_seen_to.is_some() {
|
||||
debug!(
|
||||
"Adding filter last_seen<={}",
|
||||
last_seen_to.unwrap().format("%Y-%m-%d %H:%M:%S")
|
||||
);
|
||||
sql_statement.push_str("AND last_seen<=? ");
|
||||
params.push(
|
||||
last_seen_to
|
||||
.unwrap()
|
||||
.format("%Y-%m-%d %H:%M:%S")
|
||||
.to_string()
|
||||
.into(),
|
||||
);
|
||||
};
|
||||
if owner.is_some() {
|
||||
debug!("Adding filter owner={}", owner.clone().unwrap());
|
||||
sql_statement.push_str("AND owner=? ");
|
||||
params.push(owner.unwrap().into());
|
||||
};
|
||||
if device_type.is_some() {
|
||||
debug!("Adding filter device_type={}", device_type.clone().unwrap());
|
||||
sql_statement.push_str("AND device_type=? ");
|
||||
params.push(device_type.unwrap().into());
|
||||
}
|
||||
if vendor.is_some() {
|
||||
debug!("Adding filter vendor={}", vendor.clone().unwrap());
|
||||
sql_statement.push_str("AND vendor=? ");
|
||||
params.push(vendor.unwrap().into());
|
||||
}
|
||||
|
||||
let mut stmt = conn.prepare(sql_statement.as_str())?;
|
||||
|
||||
let devices: Vec<Device> = stmt
|
||||
.query_map(params_from_iter(params.iter()), |row| {
|
||||
Ok(Device {
|
||||
mac_address: row.get(0)?,
|
||||
ipv4_address: row.get(1)?,
|
||||
vendor: row.get(2)?,
|
||||
last_seen: row.get(3)?,
|
||||
is_registered: row.get(4)?,
|
||||
owner: row.get(5)?,
|
||||
device_type: row.get(6)?,
|
||||
})
|
||||
})?
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(devices)
|
||||
}
|
||||
|
||||
// Read device from its MAC address
|
||||
pub fn read(mac_address: String) -> Option<Device> {
|
||||
let conn = db::get_db_connection();
|
||||
@@ -84,11 +168,218 @@ pub fn update(device: Device) -> Result<(), DbError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::{TimeZone, Utc};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
|
||||
use super::*;
|
||||
use crate::tests_common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list() {
|
||||
tests_common::setup().await;
|
||||
|
||||
// List all devices
|
||||
let devices: Vec<Device> = list_devices(None, None, None, None, None, None).unwrap();
|
||||
|
||||
assert!(devices.len() >= 3, "There should be at least 3 devices");
|
||||
// Validate 1 device data
|
||||
let device = devices
|
||||
.iter()
|
||||
.filter(|item| item.mac_address == "bb:bb:bb:bb:bb:bb")
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
validate_device(
|
||||
device.clone(),
|
||||
"bb:bb:bb:bb:bb:bb".to_string(),
|
||||
"Phone".to_string(),
|
||||
"192.168.0.2".to_string(),
|
||||
true,
|
||||
Utc.with_ymd_and_hms(2026, 2, 3, 13, 14, 15).unwrap(),
|
||||
"John".to_string(),
|
||||
"Vendor 2".to_string(),
|
||||
);
|
||||
|
||||
// List registered devices
|
||||
let devices: Vec<Device> = list_devices(Some(true), None, None, None, None, None).unwrap();
|
||||
|
||||
assert!(
|
||||
devices.len() >= 2,
|
||||
"There should be at least 2 registered devices"
|
||||
);
|
||||
// Device aa:aa:aa:aa:aa:aa should not be present
|
||||
assert!(
|
||||
devices
|
||||
.iter()
|
||||
.filter(|item| item.mac_address == "aa:aa:aa:aa:aa:aa")
|
||||
.next()
|
||||
.is_none(),
|
||||
"Device aa:aa:aa:aa:aa:aa should not be present"
|
||||
);
|
||||
|
||||
// All devices should be registered
|
||||
assert!(
|
||||
devices
|
||||
.iter()
|
||||
.filter(|item| !item.is_registered)
|
||||
.next()
|
||||
.is_none(),
|
||||
"There should not be any non-registered devices"
|
||||
);
|
||||
|
||||
// Validate 1 device
|
||||
let device = devices
|
||||
.iter()
|
||||
.filter(|item| item.mac_address == "bb:bb:bb:bb:bb:bb")
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
validate_device(
|
||||
device.clone(),
|
||||
"bb:bb:bb:bb:bb:bb".to_string(),
|
||||
"Phone".to_string(),
|
||||
"192.168.0.2".to_string(),
|
||||
true,
|
||||
Utc.with_ymd_and_hms(2026, 2, 3, 13, 14, 15).unwrap(),
|
||||
"John".to_string(),
|
||||
"Vendor 2".to_string(),
|
||||
);
|
||||
|
||||
// List devices seen in a time period - 2026-02-03 13:14:15
|
||||
let devices = list_devices(
|
||||
None,
|
||||
Some(Utc.with_ymd_and_hms(2026, 2, 2, 13, 13, 13).unwrap()),
|
||||
Some(Utc.with_ymd_and_hms(2026, 2, 4, 11, 11, 11).unwrap()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(devices.len() >= 1, "There should be at least one device");
|
||||
assert!(
|
||||
devices
|
||||
.iter()
|
||||
.filter(|item| item.mac_address == "bb:bb:bb:bb:bb:bb")
|
||||
.next()
|
||||
.is_some(),
|
||||
"Device bb:bb:bb:bb:bb:bb should be present"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update() {
|
||||
tests_common::setup().await;
|
||||
|
||||
// Insert a device, update it and validate it
|
||||
|
||||
let last_seen = Utc::now();
|
||||
insert(Device::new(
|
||||
"uu:tt:tt:tt:tt:aa".to_string(),
|
||||
"192.168.200.1".to_string(),
|
||||
"Test vendor".to_string(),
|
||||
last_seen,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let mut device = read("uu:tt:tt:tt:tt:aa".to_string()).unwrap();
|
||||
device.is_registered = true;
|
||||
device.owner = "Grace".to_string();
|
||||
device.device_type = "Phone".to_string();
|
||||
|
||||
update(device).unwrap();
|
||||
|
||||
let device = read("uu:tt:tt:tt:tt:aa".to_string()).unwrap();
|
||||
|
||||
validate_device(
|
||||
device,
|
||||
"uu:tt:tt:tt:tt:aa".to_string(),
|
||||
"Phone".to_string(),
|
||||
"192.168.200.1".to_string(),
|
||||
true,
|
||||
last_seen,
|
||||
"Grace".to_string(),
|
||||
"Test vendor".to_string(),
|
||||
);
|
||||
|
||||
// Insert a device, change its MAC address and validate it hasnt changed (it cannot)
|
||||
let last_seen = Utc::now();
|
||||
insert(Device::new(
|
||||
"uu:tt:tt:tt:tt:bb".to_string(),
|
||||
"192.168.200.3".to_string(),
|
||||
"Test vendor".to_string(),
|
||||
last_seen,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let mut device = read("uu:tt:tt:tt:tt:bb".to_string()).unwrap();
|
||||
device.mac_address = "uu:tt:tt:tt:tt:cc".to_string();
|
||||
|
||||
update(device).unwrap();
|
||||
|
||||
assert!(
|
||||
read("uu:tt:tt:tt:tt:cc".to_string()).is_none(),
|
||||
"Device should not exist"
|
||||
);
|
||||
|
||||
assert!(
|
||||
read("uu:tt:tt:tt:tt:bb".to_string()).is_some(),
|
||||
"Device should exist"
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_insert() {
|
||||
tests_common::setup().await;
|
||||
|
||||
// Insert constructed device and validate it
|
||||
let last_seen = Utc::now();
|
||||
insert(Device::new(
|
||||
"tt:tt:tt:tt:tt:aa".to_string(),
|
||||
"192.168.100.1".to_string(),
|
||||
"Test vendor".to_string(),
|
||||
last_seen,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let device = read("tt:tt:tt:tt:tt:aa".to_string()).unwrap();
|
||||
validate_device(
|
||||
device,
|
||||
"tt:tt:tt:tt:tt:aa".to_string(),
|
||||
"".to_string(),
|
||||
"192.168.100.1".to_string(),
|
||||
false,
|
||||
last_seen,
|
||||
"".to_string(),
|
||||
"Test vendor".to_string(),
|
||||
);
|
||||
|
||||
// Insert complete device and validate it
|
||||
let last_seen = Utc::now();
|
||||
insert(Device {
|
||||
mac_address: "tt:tt:tt:tt:tt:bb".to_string(),
|
||||
device_type: "Server".to_string(),
|
||||
ipv4_address: "192.168.100.2".to_string(),
|
||||
is_registered: true,
|
||||
last_seen: last_seen,
|
||||
owner: "Carl".to_string(),
|
||||
vendor: "Vendor X".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let device = read("tt:tt:tt:tt:tt:bb".to_string()).unwrap();
|
||||
|
||||
validate_device(
|
||||
device,
|
||||
"tt:tt:tt:tt:tt:bb".to_string(),
|
||||
"Server".to_string(),
|
||||
"192.168.100.2".to_string(),
|
||||
true,
|
||||
last_seen,
|
||||
"Carl".to_string(),
|
||||
"Vendor X".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read() {
|
||||
tests_common::setup().await;
|
||||
@@ -101,116 +392,88 @@ mod tests {
|
||||
// Read all testing devices and validate their values
|
||||
// 'aa:aa:aa:aa:aa:aa', '192.168.0.1', 'Vendor 1', '2026-01-01 11:11:11', 0, '', ''
|
||||
let device1 = read("aa:aa:aa:aa:aa:aa".to_string()).unwrap();
|
||||
assert_eq!(
|
||||
device1.mac_address, "aa:aa:aa:aa:aa:aa",
|
||||
"Invalid MAC address for device aa:aa:aa:aa:aa:aa"
|
||||
);
|
||||
assert_eq!(
|
||||
device1.device_type, "",
|
||||
"Device type should be empty for device {}",
|
||||
device1.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device1.ipv4_address, "192.168.0.1",
|
||||
"Invalid IP address for device {}",
|
||||
device1.mac_address
|
||||
);
|
||||
assert!(
|
||||
!device1.is_registered,
|
||||
"Device {} should not be registered",
|
||||
device1.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device1.last_seen,
|
||||
validate_device(
|
||||
device1,
|
||||
"aa:aa:aa:aa:aa:aa".to_string(),
|
||||
"".to_string(),
|
||||
"192.168.0.1".to_string(),
|
||||
false,
|
||||
Utc.with_ymd_and_hms(2026, 1, 1, 11, 11, 11).unwrap(),
|
||||
"Invalid last_seen for device {}",
|
||||
device1.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device1.owner, "",
|
||||
"Invalid owner for device {}",
|
||||
device1.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device1.vendor, "Vendor 1",
|
||||
"Invalid vendor for device {}",
|
||||
device1.mac_address
|
||||
"".to_string(),
|
||||
"Vendor 1".to_string(),
|
||||
);
|
||||
|
||||
// 'bb:bb:bb:bb:bb:bb', '192.168.0.2', 'Vendor 2', '2026-02-03 13:14:15', 1, 'John', 'Phone'
|
||||
let device2 = read("bb:bb:bb:bb:bb:bb".to_string()).unwrap();
|
||||
assert_eq!(
|
||||
device2.mac_address, "bb:bb:bb:bb:bb:bb",
|
||||
"Invalid MAC address for device bb:bb:bb:bb:bb:bb"
|
||||
);
|
||||
assert_eq!(
|
||||
device2.device_type, "Phone",
|
||||
"Device type should be 'Phone' for device {}",
|
||||
device2.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device2.ipv4_address, "192.168.0.2",
|
||||
"Invalid IP address for device {}",
|
||||
device2.mac_address
|
||||
);
|
||||
assert!(
|
||||
device2.is_registered,
|
||||
"Device {} should not be registered",
|
||||
device2.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device2.last_seen,
|
||||
validate_device(
|
||||
device2,
|
||||
"bb:bb:bb:bb:bb:bb".to_string(),
|
||||
"Phone".to_string(),
|
||||
"192.168.0.2".to_string(),
|
||||
true,
|
||||
Utc.with_ymd_and_hms(2026, 2, 3, 13, 14, 15).unwrap(),
|
||||
"Invalid last_seen for device {}",
|
||||
device2.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device2.owner, "John",
|
||||
"Invalid owner for device {}",
|
||||
device2.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device2.vendor, "Vendor 2",
|
||||
"Invalid vendor for device {}",
|
||||
device2.mac_address
|
||||
"John".to_string(),
|
||||
"Vendor 2".to_string(),
|
||||
);
|
||||
|
||||
// 'cc:cc:cc:cc:cc:cc', '192.168.0.3', 'Vendor 3', '2026-02-17 20:11:00', 1, 'Sarah', 'Laptop'
|
||||
let device3 = read("cc:cc:cc:cc:cc:cc".to_string()).unwrap();
|
||||
assert_eq!(
|
||||
device3.mac_address, "cc:cc:cc:cc:cc:cc",
|
||||
"Invalid MAC address for device cc:cc:cc:cc:cc:cc"
|
||||
);
|
||||
assert_eq!(
|
||||
device3.device_type, "Laptop",
|
||||
"Device type should be 'Laptop' for device {}",
|
||||
device3.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device3.ipv4_address, "192.168.0.3",
|
||||
"Invalid IP address for device {}",
|
||||
device3.mac_address
|
||||
);
|
||||
assert!(
|
||||
device3.is_registered,
|
||||
"Device {} should not be registered",
|
||||
device3.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device3.last_seen,
|
||||
validate_device(
|
||||
device3,
|
||||
"cc:cc:cc:cc:cc:cc".to_string(),
|
||||
"Laptop".to_string(),
|
||||
"192.168.0.3".to_string(),
|
||||
true,
|
||||
Utc.with_ymd_and_hms(2026, 2, 17, 20, 11, 00).unwrap(),
|
||||
"Sarah".to_string(),
|
||||
"Vendor 3".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
fn validate_device(
|
||||
device: Device,
|
||||
mac_address: String,
|
||||
device_type: String,
|
||||
ipv4_address: String,
|
||||
is_registered: bool,
|
||||
last_seen: DateTime<Utc>,
|
||||
owner: String,
|
||||
vendor: String,
|
||||
) {
|
||||
assert_eq!(
|
||||
device.mac_address, mac_address,
|
||||
"Invalid MAC address for device {}",
|
||||
device.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device.device_type, device_type,
|
||||
"Device type should be empty for device {}",
|
||||
device.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device.ipv4_address, ipv4_address,
|
||||
"Invalid IP address for device {}",
|
||||
device.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device.is_registered, is_registered,
|
||||
"Invalid registration status for device {}",
|
||||
device.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device.last_seen, last_seen,
|
||||
"Invalid last_seen for device {}",
|
||||
device3.mac_address
|
||||
device.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device3.owner, "Sarah",
|
||||
device.owner, owner,
|
||||
"Invalid owner for device {}",
|
||||
device3.mac_address
|
||||
device.mac_address
|
||||
);
|
||||
assert_eq!(
|
||||
device3.vendor, "Vendor 3",
|
||||
device.vendor, vendor,
|
||||
"Invalid vendor for device {}",
|
||||
device3.mac_address
|
||||
device.mac_address
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user