diff --git a/src/control/queries.rs b/src/control/queries.rs index 2bdccd36..df8eefc8 100644 --- a/src/control/queries.rs +++ b/src/control/queries.rs @@ -2591,6 +2591,8 @@ mod tests { "idle_ms", "first_seen_secs_ago", "last_contact_secs_ago", + // Interface presence: elapsed since the current phase began. + "since_secs", ]; /// Build a Node with a fixed identity, default config, and empty @@ -2722,6 +2724,56 @@ mod tests { // ---- 19 handler snapshot tests -------------------------------------- + /// The `interface` block, which `build_test_node` cannot produce: it + /// keeps every transport list empty, so the nineteen snapshots above pin + /// `show_transports` only in its empty form. The block is emitted by two + /// hand-duplicated sites (the live handler and the read-handle variant) + /// that agree today with nothing enforcing it, and the control-socket + /// reference states the response schema is pinned by these snapshots. + /// + /// A separate node rather than a richer `build_test_node`, so the other + /// snapshots keep their empty-state determinism. + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[tokio::test] + async fn snapshot_show_transports_with_interface() { + use crate::config::EthernetConfig; + use crate::transport::ethernet::EthernetTransport; + use crate::transport::{TransportHandle, TransportId}; + + let mut node = build_test_node(); + + // An interface no host has, so presence is deterministically absent + // and carrier deterministically false on every machine this runs on. + let config = EthernetConfig { + interface: "fips-absent-x0".to_string(), + ethertype: None, + mtu: None, + recv_buf_size: None, + send_buf_size: None, + listen: Some(true), + announce: Some(false), + auto_connect: None, + accept_connections: None, + beacon_interval_secs: None, + optional: Some(false), + }; + let (tx, _rx) = crate::transport::packet_channel(8); + let mut eth = EthernetTransport::new(TransportId::new(1), Some("lab".into()), config, tx); + + // Started, because "up with its interface absent" is the state an + // operator actually meets — and the one whose shape is new here. + eth.start_async() + .await + .expect("absence is not a start failure"); + + node.insert_transport_for_test(TransportId::new(1), TransportHandle::Ethernet(eth)); + + assert_snapshot( + "show_transports_with_interface", + &render(show_transports(&node)), + ); + } + #[test] fn snapshot_show_status() { let node = build_test_node(); diff --git a/src/control/snapshots/show_transports_with_interface.json b/src/control/snapshots/show_transports_with_interface.json new file mode 100644 index 00000000..205ed5e2 --- /dev/null +++ b/src/control/snapshots/show_transports_with_interface.json @@ -0,0 +1,36 @@ +{ + "data": { + "transports": [ + { + "interface": { + "binds": 0, + "carrier": false, + "failed_attempts": 0, + "name": "fips-absent-x0", + "policy": "required", + "presence": "absent", + "since_secs": "" + }, + "mtu": 1499, + "name": "lab", + "state": "up", + "stats": { + "beacons_dropped": 0, + "beacons_recv": 0, + "beacons_sent": 0, + "bytes_recv": 0, + "bytes_sent": 0, + "frames_recv": 0, + "frames_sent": 0, + "frames_too_long": 0, + "frames_too_short": 0, + "recv_errors": 0, + "send_errors": 0 + }, + "transport_id": 1, + "type": "ethernet" + } + ] + }, + "status": "ok" +} \ No newline at end of file