refactor: extract maybe_update_primary for testable upstream re-detection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -85,6 +85,20 @@ impl UpstreamPool {
|
||||
self.primary = primary;
|
||||
}
|
||||
|
||||
/// Update the primary upstream if `new_addr` (parsed with `port`) differs
|
||||
/// from the current preferred upstream. Returns `true` if the pool changed.
|
||||
pub fn maybe_update_primary(&mut self, new_addr: &str, port: u16) -> bool {
|
||||
let Ok(new_sock) = format!("{}:{}", new_addr, port).parse::<SocketAddr>() else {
|
||||
return false;
|
||||
};
|
||||
let new_upstream = Upstream::Udp(new_sock);
|
||||
if self.preferred() == Some(&new_upstream) {
|
||||
return false;
|
||||
}
|
||||
self.primary = vec![new_upstream];
|
||||
true
|
||||
}
|
||||
|
||||
pub fn label(&self) -> String {
|
||||
match self.preferred() {
|
||||
Some(u) => {
|
||||
@@ -469,4 +483,33 @@ mod tests {
|
||||
assert_eq!(result.header.id, 0xABCD);
|
||||
assert_eq!(result.answers.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maybe_update_primary_swaps_when_different() {
|
||||
let mut pool = UpstreamPool::new(
|
||||
vec![Upstream::Udp("1.2.3.4:53".parse().unwrap())],
|
||||
vec![Upstream::Udp("8.8.8.8:53".parse().unwrap())],
|
||||
);
|
||||
assert!(pool.maybe_update_primary("5.6.7.8", 53));
|
||||
assert_eq!(pool.preferred().unwrap().to_string(), "5.6.7.8:53");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maybe_update_primary_noop_when_same() {
|
||||
let mut pool = UpstreamPool::new(
|
||||
vec![Upstream::Udp("1.2.3.4:53".parse().unwrap())],
|
||||
vec![],
|
||||
);
|
||||
assert!(!pool.maybe_update_primary("1.2.3.4", 53));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maybe_update_primary_rejects_invalid_addr() {
|
||||
let mut pool = UpstreamPool::new(
|
||||
vec![Upstream::Udp("1.2.3.4:53".parse().unwrap())],
|
||||
vec![],
|
||||
);
|
||||
assert!(!pool.maybe_update_primary("not-an-ip", 53));
|
||||
assert_eq!(pool.preferred().unwrap().to_string(), "1.2.3.4:53");
|
||||
}
|
||||
}
|
||||
|
||||
15
src/main.rs
15
src/main.rs
@@ -610,17 +610,10 @@ async fn network_watch_loop(ctx: Arc<numa::ctx::ServerCtx>) {
|
||||
.default_upstream
|
||||
.or_else(numa::system_dns::detect_dhcp_dns)
|
||||
.unwrap_or_else(|| QUAD9_IP.to_string());
|
||||
if let Ok(new_sock) =
|
||||
format!("{}:{}", new_addr, ctx.upstream_port).parse::<SocketAddr>()
|
||||
{
|
||||
let new_upstream = Upstream::Udp(new_sock);
|
||||
let mut pool = ctx.upstream_pool.lock().unwrap();
|
||||
let current = pool.preferred().cloned();
|
||||
if current.as_ref() != Some(&new_upstream) {
|
||||
info!("upstream changed: {} → {}", pool.label(), new_upstream);
|
||||
pool.set_primary(vec![new_upstream]);
|
||||
changed = true;
|
||||
}
|
||||
let mut pool = ctx.upstream_pool.lock().unwrap();
|
||||
if pool.maybe_update_primary(&new_addr, ctx.upstream_port) {
|
||||
info!("upstream changed → {}", pool.label());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user