use std::io::Cursor;
use flowscope::extract::FiveTuple;
use flowscope::pcap::PcapFlowSource;
use flowscope::{EndReason, FlowEvent};
const HTTP_SESSION: &[u8] = include_bytes!("data/http_session.pcap");
const DNS_QUERIES: &[u8] = include_bytes!("data/dns_queries.pcap");
#[test]
fn views_iterates_in_order() {
let src = PcapFlowSource::from_reader(Cursor::new(HTTP_SESSION)).unwrap();
let views: Vec<_> = src.views().collect::<Result<_, _>>().unwrap();
assert!(!views.is_empty(), "fixture should have packets");
for w in views.windows(2) {
assert!(
w[0].timestamp <= w[1].timestamp,
"timestamps must be ordered"
);
}
}
#[test]
fn event_iter_emits_full_lifecycle_for_http() {
let src = PcapFlowSource::from_reader(Cursor::new(HTTP_SESSION)).unwrap();
let evts: Vec<_> = src
.with_extractor(FiveTuple::bidirectional())
.collect::<Result<_, _>>()
.unwrap();
let started = evts
.iter()
.filter(|e| matches!(e, FlowEvent::Started { .. }))
.count();
let est = evts
.iter()
.filter(|e| matches!(e, FlowEvent::Established { .. }))
.count();
let ended_fin = evts
.iter()
.filter(|e| {
matches!(
e,
FlowEvent::Ended {
reason: EndReason::Fin,
..
}
)
})
.count();
assert_eq!(started, 1);
assert_eq!(est, 1);
assert_eq!(ended_fin, 1, "HTTP fixture closes with FIN");
}
#[test]
fn event_iter_runs_final_sweep_on_pcap_exhaustion() {
let src = PcapFlowSource::from_reader(Cursor::new(DNS_QUERIES)).unwrap();
let evts: Vec<_> = src
.with_extractor(FiveTuple::bidirectional())
.collect::<Result<_, _>>()
.unwrap();
let started = evts
.iter()
.filter(|e| matches!(e, FlowEvent::Started { .. }))
.count();
let ended_idle = evts
.iter()
.filter(|e| {
matches!(
e,
FlowEvent::Ended {
reason: EndReason::IdleTimeout,
..
}
)
})
.count();
assert!(started >= 1);
assert_eq!(
started, ended_idle,
"every Started should reach Ended via the final sweep"
);
}