arrange tests

comment
This commit is contained in:
Luna Yao
2026-04-06 17:19:26 +02:00
parent 34dab9c739
commit 012b35acd6
5 changed files with 151 additions and 150 deletions
+12 -48
View File
@@ -161,56 +161,16 @@ impl DnsNodeMgrRpc for DnsNodeMgr {
mod tests {
use super::*;
use crate::common::log;
use crate::dns::tests::new_request;
use crate::dns::tests::{
dns_snapshot_with as snapshot_with, heartbeat_with_snapshot, new_request,
zone_data_a_with_forwarders as valid_zone_data,
};
use crate::dns::utils::response::ResponseHandle;
use crate::proto::common::Url;
use crate::proto::dns::ZoneData;
use hickory_proto::op::{Message, ResponseCode};
use hickory_proto::rr::{rdata, RData, RecordType};
use std::net::Ipv4Addr;
use std::str::FromStr;
use tokio::time::{sleep, Duration};
fn valid_zone_data(origin: &str, record: &str, forwarders: Vec<&str>) -> ZoneData {
ZoneData {
id: Some(Uuid::new_v4().into()),
origin: origin.to_string(),
ttl: 60,
records: vec![format!("@ IN A {record}")],
forwarders: forwarders
.into_iter()
.map(|f| Url::from_str(f).expect("invalid forwarder"))
.collect(),
}
}
fn snapshot_with(
zones: Vec<ZoneData>,
addresses: Vec<&str>,
listeners: Vec<&str>,
) -> DnsSnapshot {
DnsSnapshot {
zones,
addresses: addresses
.into_iter()
.map(|a| Url::from_str(a).expect("invalid address"))
.collect(),
listeners: listeners
.into_iter()
.map(|l| Url::from_str(l).expect("invalid listener"))
.collect(),
}
}
fn heartbeat_with_snapshot(id: Uuid, snapshot: DnsSnapshot) -> HeartbeatRequest {
let mut hb = HeartbeatRequest {
id: Some(id.into()),
..Default::default()
};
hb.update(snapshot);
hb
}
fn heartbeat_digest_only(id: Uuid, digest: Vec<u8>) -> HeartbeatRequest {
HeartbeatRequest {
id: Some(id.into()),
@@ -480,10 +440,12 @@ mod tests {
)
.await;
let zones: Vec<ZoneData> = mgr.collect_zones().into_iter().map(Into::into).collect();
let zones: Vec<_> = mgr.collect_zones().into_iter().map(Into::into).collect();
let loop_zone = zones
.into_iter()
.find(|z| z.origin.trim_end_matches('.') == "filter-loop.test")
.find(|z: &crate::proto::dns::ZoneData| {
z.origin.trim_end_matches('.') == "filter-loop.test"
})
.expect("test zone should exist");
let forwarders: HashSet<NameServerAddr> = loop_zone
@@ -528,10 +490,12 @@ mod tests {
let _ = send_heartbeat(&mgr, heartbeat_with_snapshot(Uuid::new_v4(), node_a)).await;
let _ = send_heartbeat(&mgr, heartbeat_with_snapshot(Uuid::new_v4(), node_b)).await;
let zones: Vec<ZoneData> = mgr.collect_zones().into_iter().map(Into::into).collect();
let zones: Vec<_> = mgr.collect_zones().into_iter().map(Into::into).collect();
let zone = zones
.into_iter()
.find(|z| z.origin.trim_end_matches('.') == "cross-node-filter.test")
.find(|z: &crate::proto::dns::ZoneData| {
z.origin.trim_end_matches('.') == "cross-node-filter.test"
})
.expect("test zone should exist");
let forwarders: HashSet<NameServerAddr> = zone
+2 -11
View File
@@ -198,25 +198,16 @@ mod tests {
use super::*;
use crate::common::global_ctx::tests::get_mock_global_ctx;
use crate::dns::config::zone::ZoneConfig;
use crate::dns::tests::zone_data_a as valid_zone_data;
use crate::peers::create_packet_recv_chan;
use crate::peers::peer_manager::RouteAlgoType;
use crate::peers::tests::{connect_peer_manager, wait_route_appear};
use crate::proto::dns::{GetExportConfigRequest, ZoneData};
use crate::proto::dns::GetExportConfigRequest;
use std::collections::HashSet;
use std::net::Ipv4Addr;
use tokio::time::{sleep, Duration};
use uuid::Uuid;
fn valid_zone_data(origin: &str, record: &str) -> ZoneData {
ZoneData {
id: Some(Uuid::new_v4().into()),
origin: origin.to_string(),
ttl: 60,
records: vec![format!("@ IN A {record}")],
forwarders: vec![],
}
}
async fn create_peer_manager_with_zone(
host: &str,
origin: &str,
+34 -66
View File
@@ -478,9 +478,11 @@ impl DnsServer {
#[cfg(test)]
mod tests {
use super::*;
use crate::dns::tests::{
dns_snapshot_with as snapshot_with, heartbeat_with_snapshot, zone_data_a as valid_zone_data,
};
use crate::peers::tests::create_mock_peer_manager;
use crate::proto::common::Url;
use crate::proto::dns::{DnsNodeMgrRpc, DnsSnapshot, HeartbeatRequest, ZoneData};
use crate::proto::dns::DnsNodeMgrRpc;
use crate::proto::rpc_types::controller::BaseController;
use hickory_client::client::{Client, ClientHandle};
use hickory_proto::op::{Message, MessageType, OpCode, Query};
@@ -618,39 +620,6 @@ mod tests {
buf
}
fn valid_zone_data(origin: &str, record: &str) -> ZoneData {
ZoneData {
id: Some(Uuid::new_v4().into()),
origin: origin.to_string(),
ttl: 60,
records: vec![format!("@ IN A {record}")],
forwarders: vec![],
}
}
fn snapshot_with(zones: Vec<ZoneData>, addresses: Vec<&str>, listeners: Vec<&str>) -> DnsSnapshot {
DnsSnapshot {
zones,
addresses: addresses
.into_iter()
.map(|a| Url::from_str(a).expect("invalid address"))
.collect(),
listeners: listeners
.into_iter()
.map(|l| Url::from_str(l).expect("invalid listener"))
.collect(),
}
}
fn heartbeat_with_snapshot(id: Uuid, snapshot: DnsSnapshot) -> HeartbeatRequest {
let mut hb = HeartbeatRequest {
id: Some(id.into()),
..Default::default()
};
hb.update(snapshot);
hb
}
async fn wait_until(mut f: impl FnMut() -> bool) {
for _ in 0..80 {
if f() {
@@ -664,19 +633,7 @@ mod tests {
// ─── Tests ───────────────────────────────────────────────────────────
#[tokio::test]
async fn test_dynamic_catalog_replace() {
let catalog = DynamicCatalog::new();
let new_catalog = build_test_catalog();
catalog.replace(new_catalog).await;
// After replacement the catalog should resolve test.example.com
// This is implicitly verified by the UDP DNS test below; here we
// just make sure `replace` does not panic and completes.
}
#[tokio::test]
async fn test_is_hijacked_ip_and_addr() {
async fn should_match_hijacked_ip_and_addr_when_address_is_registered() {
let server = create_test_server().await;
let addr: SocketAddr = "10.0.0.53:53".parse().unwrap();
assert!(!server.is_hijacked_ip(&addr.ip()));
@@ -693,7 +650,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_icmp_echo_request() {
async fn should_reply_icmp_echo_and_swap_endpoints_when_packet_is_hijacked() {
let server = create_test_server().await;
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
@@ -732,7 +689,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_icmp_non_echo_ignored() {
async fn should_ignore_icmp_when_type_is_not_echo_request() {
let server = create_test_server().await;
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
@@ -759,7 +716,7 @@ mod tests {
}
#[tokio::test]
async fn test_non_hijacked_ip_ignored() {
async fn should_ignore_packet_when_destination_ip_is_not_hijacked() {
let server = create_test_server().await;
// Do NOT register any hijacked addresses.
let icmp_payload = build_icmp_echo_request();
@@ -781,7 +738,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_udp_dns_packet() {
async fn should_rewrite_udp_dns_packet_when_query_targets_hijacked_dns_addr() {
let server = create_test_server().await;
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
@@ -860,7 +817,7 @@ mod tests {
/// Full end-to-end test: start a real DNS UDP listener via `ServerFuture`,
/// send a query with a `hickory_client`, and verify the response.
#[tokio::test]
async fn test_full_udp_dns_query() {
async fn should_resolve_record_via_real_udp_listener() {
use hickory_server::ServerFuture;
use tokio::net::UdpSocket;
use tokio::time::timeout;
@@ -916,7 +873,7 @@ mod tests {
}
#[tokio::test]
async fn test_try_process_packet_from_nic_and_pipeline_id() {
async fn should_process_icmp_packet_and_expose_pipeline_id() {
let server = create_test_server().await;
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
@@ -927,7 +884,8 @@ mod tests {
.insert(SocketAddr::new(dst_ip.into(), 53).into());
let icmp_payload = build_icmp_echo_request();
let ip_bytes = build_ipv4_packet(src_ip, dst_ip, IpNextHeaderProtocols::Icmp, &icmp_payload);
let ip_bytes =
build_ipv4_packet(src_ip, dst_ip, IpNextHeaderProtocols::Icmp, &icmp_payload);
let mut zc = ZCPacket::new_with_payload(&ip_bytes);
zc.fill_peer_manager_hdr(1, 2, crate::tunnel::packet_def::PacketType::Data as u8);
@@ -936,7 +894,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_ip_packet_non_ipv4_version_ignored() {
async fn should_ignore_packet_when_ipv4_header_has_non_ipv4_version() {
let server = create_test_server().await;
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
@@ -947,7 +905,8 @@ mod tests {
.insert(SocketAddr::new(dst_ip.into(), 53).into());
let icmp_payload = build_icmp_echo_request();
let mut ip_bytes = build_ipv4_packet(src_ip, dst_ip, IpNextHeaderProtocols::Icmp, &icmp_payload);
let mut ip_bytes =
build_ipv4_packet(src_ip, dst_ip, IpNextHeaderProtocols::Icmp, &icmp_payload);
ip_bytes[0] = (6 << 4) | 5; // fake IPv6 version in IPv4 header
let mut zc = ZCPacket::new_with_payload(&ip_bytes);
@@ -957,7 +916,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_ip_packet_unsupported_protocol_ignored() {
async fn should_ignore_packet_when_protocol_is_unsupported() {
let server = create_test_server().await;
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
@@ -982,7 +941,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_udp_packet_wrong_port_ignored() {
async fn should_ignore_udp_dns_packet_when_destination_port_is_not_hijacked() {
let server = create_test_server().await;
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
@@ -1004,7 +963,7 @@ mod tests {
}
#[tokio::test]
async fn test_handle_udp_packet_invalid_dns_payload_ignored() {
async fn should_ignore_udp_packet_when_dns_payload_is_invalid() {
let server = create_test_server().await;
let dst_ip: Ipv4Addr = "10.0.0.53".parse().unwrap();
let src_ip: Ipv4Addr = "10.0.0.1".parse().unwrap();
@@ -1025,7 +984,7 @@ mod tests {
}
#[tokio::test]
async fn test_reload_addresses_updates_public_addresses() {
async fn should_update_public_addresses_when_reload_addresses_is_called() {
let server = create_test_server().await;
let addrs = vec![
@@ -1045,7 +1004,7 @@ mod tests {
}
#[tokio::test]
async fn test_reload_listeners_partial_bind_failure_still_serves_working_listener() {
async fn should_still_serve_working_listener_when_one_bind_fails() {
let server = create_test_server().await;
server.catalog.replace(build_test_catalog()).await;
@@ -1068,7 +1027,10 @@ mod tests {
];
let mut runtime = None;
server.reload_listeners(listeners, &mut runtime).await.unwrap();
server
.reload_listeners(listeners, &mut runtime)
.await
.unwrap();
let conn = UdpClientStream::builder(good_addr, TokioRuntimeProvider::default()).build();
let (mut client, bg) = timeout(Duration::from_secs(2), Client::connect(conn))
@@ -1099,7 +1061,7 @@ mod tests {
}
#[tokio::test]
async fn test_run_applies_snapshot_updates_and_clears_on_shutdown() {
async fn should_apply_snapshot_updates_and_clear_state_on_shutdown() {
let server = create_test_server().await;
let token = CancellationToken::new();
let run_server = server.clone();
@@ -1152,7 +1114,13 @@ mod tests {
token.cancel();
let _ = run_task.await;
assert!(server.addresses().is_empty(), "run() should clear addresses on exit");
assert!(server.listeners.read().is_empty(), "run() should clear listeners on exit");
assert!(
server.addresses().is_empty(),
"run() should clear addresses on exit"
);
assert!(
server.listeners.read().is_empty(),
"run() should clear listeners on exit"
);
}
}
+82 -15
View File
@@ -9,15 +9,16 @@ use crate::common::config::TomlConfigLoader;
use crate::common::global_ctx::tests::get_mock_global_ctx;
use crate::common::global_ctx::GlobalCtx;
use crate::connector::udp_hole_punch::tests::replace_stun_info_collector;
use crate::dns::config::zone::ZoneConfigInner;
use crate::dns::node::DnsNode;
use crate::dns::peer_mgr::DnsPeerMgr;
use crate::dns::config::zone::ZoneConfigInner;
use crate::instance::instance::ArcNicCtx;
use crate::instance::virtual_nic::NicCtx;
use crate::peers::create_packet_recv_chan;
use crate::peers::peer_manager::{PeerManager, RouteAlgoType};
use crate::peers::tests::{connect_peer_manager, wait_route_appear};
use crate::proto::common::NatType;
use crate::proto::common::{NatType, Url};
use crate::proto::dns::{DnsSnapshot, HeartbeatRequest, ZoneData};
use cidr::Ipv4Inet;
use hickory_client::client::{Client, ClientHandle as _};
use hickory_proto::op::{Message, MessageType, OpCode, Query};
@@ -30,7 +31,9 @@ use hickory_proto::xfer::Protocol;
use hickory_server::authority::MessageRequest;
use hickory_server::server::Request;
use tokio::sync::Notify;
use uuid::Uuid;
// TODO: move to system::tests
pub async fn prepare_env(dns_name: &str, tun_ip: Ipv4Inet) -> (Arc<PeerManager>, NicCtx) {
prepare_env_with_tld_dns_zone(dns_name, tun_ip, None).await
}
@@ -98,6 +101,50 @@ pub async fn prepare_env_from_config_str(config_str: &str) -> Arc<PeerManager> {
peer_mgr
}
pub fn zone_data_a(origin: &str, record: &str) -> ZoneData {
zone_data_a_with_forwarders(origin, record, vec![])
}
pub fn zone_data_a_with_forwarders(origin: &str, record: &str, forwarders: Vec<&str>) -> ZoneData {
ZoneData {
id: Some(Uuid::new_v4().into()),
origin: origin.to_string(),
ttl: 60,
records: vec![format!("@ IN A {record}")],
forwarders: forwarders
.into_iter()
.map(|f| Url::from_str(f).expect("invalid forwarder"))
.collect(),
}
}
pub fn dns_snapshot_with(
zones: Vec<ZoneData>,
addresses: Vec<&str>,
listeners: Vec<&str>,
) -> DnsSnapshot {
DnsSnapshot {
zones,
addresses: addresses
.into_iter()
.map(|a| Url::from_str(a).expect("invalid address"))
.collect(),
listeners: listeners
.into_iter()
.map(|l| Url::from_str(l).expect("invalid listener"))
.collect(),
}
}
pub fn heartbeat_with_snapshot(id: Uuid, snapshot: DnsSnapshot) -> HeartbeatRequest {
let mut hb = HeartbeatRequest {
id: Some(id.into()),
..Default::default()
};
hb.update(snapshot);
hb
}
fn find_free_udp_port() -> u16 {
std::net::UdpSocket::bind("127.0.0.1:0")
.expect("failed to bind temp udp socket")
@@ -539,11 +586,10 @@ records = ["api IN A 10.80.0.1"]
zone.records = vec!["api IN A 10.80.0.2".to_string()];
dns.zones[zone_idx] = zone.try_into().expect("patch zone update should be valid");
peer.get_global_ctx().config.set_dns(Some(dns));
peer.get_global_ctx().issue_event(
crate::common::global_ctx::GlobalCtxEvent::ConfigPatched(
peer.get_global_ctx()
.issue_event(crate::common::global_ctx::GlobalCtxEvent::ConfigPatched(
crate::proto::api::config::InstanceConfigPatch::default(),
),
);
));
check_dns_record_at(server_addr, "api.patch.mesh-test.", "10.80.0.2").await;
@@ -555,7 +601,12 @@ records = ["api IN A 10.80.0.1"]
async fn config_patch_reloads_listener_binding() {
let listener_old = find_free_udp_port();
let listener_new = find_free_udp_port();
let config = cfg_with_listener("listener-patch", "10.144.150.11/24", "mesh-test", listener_old);
let config = cfg_with_listener(
"listener-patch",
"10.144.150.11/24",
"mesh-test",
listener_old,
);
let peer = prepare_env_from_config_str(&config).await;
let dns_node = start_dns_node_without_nic(peer.clone());
@@ -570,11 +621,10 @@ async fn config_patch_reloads_listener_binding() {
.expect("invalid listener")]
.into();
peer.get_global_ctx().config.set_dns(Some(dns));
peer.get_global_ctx().issue_event(
crate::common::global_ctx::GlobalCtxEvent::ConfigPatched(
peer.get_global_ctx()
.issue_event(crate::common::global_ctx::GlobalCtxEvent::ConfigPatched(
crate::proto::api::config::InstanceConfigPatch::default(),
),
);
));
check_dns_record_at(new_addr, "listener-patch.mesh-test.", "10.144.150.11").await;
check_dns_unavailable_at(old_addr, "listener-patch.mesh-test.").await;
@@ -621,7 +671,13 @@ records = ["svc IN A 10.77.7.7"]
let addr_a = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), listener_a);
check_dns_record_at(addr_a, "node-c7.mesh7-test.", "10.144.151.13").await;
check_dns_record_at(addr_a, "svc.shared-c7.mesh7-test.", "10.77.7.7").await;
wait_peer_zone_visibility(peer_a.clone(), peer_c.my_peer_id(), "node-c7.mesh7-test", true).await;
wait_peer_zone_visibility(
peer_a.clone(),
peer_c.my_peer_id(),
"node-c7.mesh7-test",
true,
)
.await;
wait_peer_zone_visibility(
peer_a.clone(),
peer_c.my_peer_id(),
@@ -633,8 +689,13 @@ records = ["svc IN A 10.77.7.7"]
disconnect_all_peer_conns(peer_b.clone(), peer_c.clone()).await;
wait_route_disappear(peer_a.clone(), peer_c.my_peer_id()).await;
// Validate via peer-sync snapshot to avoid process-wide DNS-server election side effects.
wait_peer_zone_visibility(peer_a.clone(), peer_c.my_peer_id(), "node-c7.mesh7-test", false)
.await;
wait_peer_zone_visibility(
peer_a.clone(),
peer_c.my_peer_id(),
"node-c7.mesh7-test",
false,
)
.await;
wait_peer_zone_visibility(
peer_a.clone(),
peer_c.my_peer_id(),
@@ -648,7 +709,13 @@ records = ["svc IN A 10.77.7.7"]
.await
.expect("route a-c should recover via b");
wait_peer_zone_visibility(peer_a.clone(), peer_c.my_peer_id(), "node-c7.mesh7-test", true).await;
wait_peer_zone_visibility(
peer_a.clone(),
peer_c.my_peer_id(),
"node-c7.mesh7-test",
true,
)
.await;
wait_peer_zone_visibility(
peer_a.clone(),
peer_c.my_peer_id(),
+21 -10
View File
@@ -163,7 +163,7 @@ impl ZoneGroup {
#[cfg(test)]
mod tests {
use super::*;
use crate::dns;
use crate::dns::tests::new_request;
use crate::dns::utils::response::ResponseHandle;
use crate::proto::common::Url;
use crate::proto::dns::ZoneData;
@@ -220,7 +220,7 @@ mod tests {
name: &str,
record_type: RecordType,
) -> anyhow::Result<(ResponseCode, Option<Message>)> {
let request = dns::tests::new_request(name, record_type)?;
let request = new_request(name, record_type)?;
let response = ResponseHandle::new(1024);
let info = catalog.lookup(&request, None, response.clone()).await;
let message = response
@@ -231,9 +231,10 @@ mod tests {
}
fn has_a_answer(message: &Message, expected: Ipv4Addr) -> bool {
message.answers().iter().any(|record| {
matches!(record.data(), RData::A(addr) if *addr == rdata::a::A(expected))
})
message
.answers()
.iter()
.any(|record| matches!(record.data(), RData::A(addr) if *addr == rdata::a::A(expected)))
}
async fn start_upstream_server() -> anyhow::Result<(SocketAddr, JoinHandle<()>)> {
@@ -288,9 +289,7 @@ mod tests {
let data = zone_data("invalid-forwarder.test", vec![], vec!["http://1.1.1.1:53"]);
let err = Zone::try_from(&data).expect_err("unsupported forwarder should fail");
assert!(
err.to_string().contains("unsupported") || err.to_string().contains("protocol")
);
assert!(err.to_string().contains("unsupported") || err.to_string().contains("protocol"));
}
#[test]
@@ -339,8 +338,20 @@ mod tests {
let groups = zones.into_groups();
assert_eq!(groups.len(), 2);
assert_eq!(groups.get(&LowerName::from_str("same.test.")?).unwrap().len(), 2);
assert_eq!(groups.get(&LowerName::from_str("other.test.")?).unwrap().len(), 1);
assert_eq!(
groups
.get(&LowerName::from_str("same.test.")?)
.unwrap()
.len(),
2
);
assert_eq!(
groups
.get(&LowerName::from_str("other.test.")?)
.unwrap()
.len(),
1
);
Ok(())
}