Fix subnets validation

This commit is contained in:
2026-10-26 22:59:11 +02:00
parent e141c03c2c
commit ecc5c83d78
11 changed files with 605 additions and 68 deletions
+55 -17
View File
@@ -19,7 +19,7 @@ use super::events::{
};
use super::forms::{FormAction, FormController};
use super::render;
use super::state::{Screen, UiState};
use super::state::{Screen, SessionView, UiState};
use crate::app::Application;
const MIN_WIDTH: u16 = 34;
@@ -765,22 +765,7 @@ fn handle_key(
state.screen = Screen::Message;
return;
};
let mut issues = Vec::new();
issues.extend(
view.document_issues
.iter()
.map(|issue| format!("Document issue: {issue:?}")),
);
for client in &view.clients {
for issue in &client.issues {
issues.push(format!("Peer at line {}: {issue:?}", client.line));
}
}
state.status_message = Some(if issues.is_empty() {
"No document or peer issues were reported.".to_owned()
} else {
issues.join("\n")
});
state.status_message = Some(issue_summary(view));
state.screen = Screen::Message;
state.scroll_offset = 0;
}
@@ -802,6 +787,25 @@ fn handle_key(
}
}
fn issue_summary(view: &SessionView) -> String {
let mut issues = Vec::new();
issues.extend(
view.document_issues
.iter()
.map(|issue| format!("Document issue: {issue:?}")),
);
for client in &view.clients {
for issue in client.presented_issues() {
issues.push(format!("Peer at line {}: {issue:?}", client.line));
}
}
if issues.is_empty() {
"No document or peer issues were reported.".to_owned()
} else {
issues.join("\n")
}
}
/// Handle list/detail navigation and return whether the key was consumed.
/// Keeping this path independent of worker actions makes its state transitions
/// directly testable without starting an application worker.
@@ -921,6 +925,7 @@ mod tests {
identity: None,
label: "unsupported peer".to_owned(),
allowed_ips: Vec::new(),
has_subnet_routes: false,
issues: vec![PeerIssue::MissingIpv4HostRoute],
mutable_identity: None,
};
@@ -933,6 +938,7 @@ mod tests {
identity: Some(first),
label: "first".to_owned(),
allowed_ips: vec!["10.0.0.2/32".to_owned()],
has_subnet_routes: false,
issues: Vec::new(),
mutable_identity: Some(first),
},
@@ -1007,6 +1013,38 @@ mod tests {
assert!(!state.selected_client_mutable());
}
#[test]
fn issues_screen_hides_valid_address_shape_limits_but_keeps_genuine_errors() {
let mut view = navigation_view();
view.clients.truncate(1);
view.clients[0].issues = vec![PeerIssue::UnsupportedAllowedIpShape { field: 0, item: 1 }];
view.clients[0].has_subnet_routes = false;
let additional_host_only = issue_summary(&view);
assert_eq!(
additional_host_only,
"No document or peer issues were reported."
);
assert_eq!(
view.clients[0].issues,
vec![PeerIssue::UnsupportedAllowedIpShape { field: 0, item: 1 }],
"presentation must retain the parser issue for safety checks"
);
view.clients[0].issues = vec![PeerIssue::MissingIpv4HostRoute];
assert_eq!(
issue_summary(&view),
"No document or peer issues were reported.",
"valid IPv6-only address shape is a capability limit"
);
view.clients[0]
.issues
.push(PeerIssue::MalformedAllowedIp { field: 0, item: 2 });
let mixed = issue_summary(&view);
assert!(mixed.contains("MalformedAllowedIp"));
assert!(!mixed.contains("UnsupportedAllowedIpShape"));
}
#[test]
fn restart_results_render_cancel_success_and_uncertain_failure_honestly() {
use crate::app::{RestartFailureKind, RestartReport, RestartStage};