use bacnet_npdu::NetworkMessageType;
use bacnet_transport::loopback::LoopbackTransport;
use bacnet_transport::port::TransportPort;
use crate::layer::{Counters, NetworkLayer};
use crate::loopback_fixture::{APDU, pump, recv, wire};
use bacnet_router_core::node::Node;
#[tokio::test]
async fn npdus_for_another_network_reach_neither_receiver_and_are_counted() {
let (transport, mut peer) = LoopbackTransport::pair(vec![0x01], vec![0x02]);
let mut network = NetworkLayer::new(transport);
let mut controls = network.enable_network_control_receiver().unwrap();
let mut apdus = network.start().await.unwrap();
let control = Some(NetworkMessageType::I_AM_ROUTER_TO_NETWORK);
let routed = [
wire(Some((3, 1)), None, None),
wire(Some((3, 0)), None, None),
wire(Some((3, 1)), None, control),
wire(Some((3, 0)), None, control),
];
for bytes in &routed {
peer.send_unicast(bytes, &[0x01]).await.unwrap();
}
for bytes in [
wire(None, None, None),
wire(None, None, control),
wire(Some((0xFFFF, 0)), None, None),
wire(Some((0xFFFF, 0)), None, control),
] {
peer.send_broadcast(&bytes).await.unwrap();
}
let local = recv(&mut apdus).await;
assert_eq!(local.apdu, APDU[..]);
assert!(
local.is_group && !local.global_broadcast,
"the local APDU first"
);
let global = recv(&mut apdus).await;
assert!(global.is_group && global.global_broadcast);
pump(&mut apdus);
let local = recv(&mut controls).await;
assert_eq!(local.npdu.destination, None, "the local message first");
assert_eq!(local.npdu.payload[..], [0x0B, 0xB8]);
let global = recv(&mut controls).await;
assert_eq!(global.npdu.destination.map(|d| d.network), Some(0xFFFF));
let expected = Counters {
routed_drops: routed.len() as u64,
..Counters::default()
};
assert_eq!(network.counters(), expected);
network.stop().await.unwrap();
peer.stop().await.unwrap();
}
#[tokio::test]
async fn malformed_npdus_are_counted() {
let (transport, mut peer) = LoopbackTransport::pair(vec![0x01], vec![0x02]);
let mut network = NetworkLayer::new(transport);
let mut apdus = network.start().await.unwrap();
for bytes in [vec![0x01], vec![0x02, 0x00, 0x10, 0x08], {
let mut zero_snet = wire(None, Some((1, 1)), None);
zero_snet[2..4].copy_from_slice(&[0, 0]);
zero_snet
}] {
peer.send_unicast(&bytes, &[0x01]).await.unwrap();
}
peer.send_unicast(&wire(None, None, None), &[0x01])
.await
.unwrap();
assert_eq!(recv(&mut apdus).await.apdu, APDU[..], "only the good one");
let expected = Counters {
malformed: 3,
..Counters::default()
};
assert_eq!(network.counters(), expected);
network.stop().await.unwrap();
peer.stop().await.unwrap();
}
#[test]
fn records_from_the_view_match_the_owned_decoder() {
use crate::layer::receive::{Inbound, NodeTotals, admit};
use bacnet_encoding::npdu::decode_npdu;
use bacnet_transport::warn_throttle::WarnThrottle;
use bytes::Bytes;
let control = Bytes::from_static(&[1, 0x8D, 0, 9, 1, 5, 0x00, 0x0B, 0xB8]);
let apdu = Bytes::from_static(&[1, 0x2A, 0xFF, 0xFF, 0, 0, 9, 1, 5, 7, 0x10, 0x08]);
let (mut node, totals) = (Node::new(), NodeTotals::default());
let Inbound::Control(Some(npdu)) = admit(
&mut node,
&totals,
&WarnThrottle::default(),
&[2],
false,
control.clone(),
true,
) else {
panic!("a control");
};
assert_eq!(npdu, decode_npdu(control).unwrap());
let Inbound::Apdu { apdu: payload, .. } = admit(
&mut node,
&totals,
&WarnThrottle::default(),
&[2],
false,
apdu.clone(),
true,
) else {
panic!("an APDU");
};
assert_eq!(payload, decode_npdu(apdu).unwrap().payload);
}
#[tokio::test(start_paused = true)]
async fn dropped_npdus_share_one_warning_throttle() {
use crate::layer::receive::{Inbound, NodeTotals, admit};
use bacnet_transport::warn_throttle::{WARNING_INTERVAL, WarnThrottle};
use bytes::Bytes;
let bad = Bytes::from_static(&[2, 0]);
let (mut node, totals, throttle) =
(Node::new(), NodeTotals::default(), WarnThrottle::default());
for _ in 0..3 {
let inbound = admit(
&mut node,
&totals,
&throttle,
&[2],
false,
bad.clone(),
true,
);
assert!(matches!(inbound, Inbound::Dropped));
}
tokio::time::advance(WARNING_INTERVAL).await;
assert_eq!(
throttle.note(),
Some(3),
"the first drop warned; two held back, and this one"
);
}