mirror of https://github.com/ospab/ostp.git
test: integration tests for ProtocolMachine (handshake, data, close, wrong-psk, CC, multi-frame)
8 new integration tests in ostp-core::protocol::tests: - test_full_handshake: Noise handshake -> Established state - test_data_exchange_client_to_server: encrypt/decrypt data frame C->S - test_data_exchange_server_to_client: encrypt/decrypt data frame S->C - test_close_sequence: Close frame -> Closed state - test_wrong_psk_handshake_fails: bad PSK rejected, never reaches Established - test_congestion_controller_after_handshake: CC budget >= 2 in SlowStart - test_multiple_data_frames: 10 sequential frames, payload integrity verified - test_tick_no_crash: Tick event stable on both sides Total: 43 tests, 0 failures
This commit is contained in:
parent
bd3def32bb
commit
9b01466953
|
|
@ -744,3 +744,235 @@ fn derive_split_keys(base_key: &[u8; 32], role: NoiseRole) -> ([u8; 32], [u8; 32
|
|||
NoiseRole::Responder => (responder_key, initiator_key),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::framing::PaddingStrategy;
|
||||
|
||||
fn test_psk() -> [u8; 32] {
|
||||
let mut psk = [0u8; 32];
|
||||
psk[0] = 0xAB;
|
||||
psk[15] = 0xCD;
|
||||
psk[31] = 0xEF;
|
||||
psk
|
||||
}
|
||||
|
||||
fn make_config(role: NoiseRole) -> ProtocolConfig {
|
||||
ProtocolConfig {
|
||||
role,
|
||||
psk: test_psk(),
|
||||
session_id: 1,
|
||||
handshake_payload: vec![],
|
||||
max_padding: 64,
|
||||
padding_strategy: PaddingStrategy::Adaptive,
|
||||
obfuscation_key: [0u8; 8],
|
||||
max_reorder: 128,
|
||||
max_reorder_buffer: 256,
|
||||
ack_delay_ms: 5,
|
||||
rto_ms: 100,
|
||||
max_retries: 4,
|
||||
max_sent_history: 1024,
|
||||
handshake_pad_min: 8,
|
||||
handshake_pad_max: 32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Full handshake: Initiator -> Responder -> Initiator -> Established
|
||||
fn do_handshake() -> (ProtocolMachine, ProtocolMachine) {
|
||||
let mut client = ProtocolMachine::new(make_config(NoiseRole::Initiator)).unwrap();
|
||||
let mut server = ProtocolMachine::new(make_config(NoiseRole::Responder)).unwrap();
|
||||
|
||||
// Client sends handshake message 1
|
||||
let action = client.on_event(OstpEvent::Start).unwrap();
|
||||
let msg1 = match action {
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
_ => panic!("expected SendDatagram from client Start"),
|
||||
};
|
||||
assert_eq!(client.state(), OstpState::Handshaking);
|
||||
|
||||
// Server receives msg1 and responds
|
||||
let action = server.on_event(OstpEvent::Start).unwrap();
|
||||
assert!(matches!(action, ProtocolAction::Noop));
|
||||
|
||||
let action = server.on_event(OstpEvent::Inbound(msg1)).unwrap();
|
||||
let msg2 = match action {
|
||||
ProtocolAction::Multiple(actions) => {
|
||||
actions.into_iter().find_map(|a| match a {
|
||||
ProtocolAction::SendDatagram(d) => Some(d),
|
||||
_ => None,
|
||||
}).expect("server should send datagram in handshake response")
|
||||
}
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
ProtocolAction::HandshakePayload(_, Some(d)) => d,
|
||||
other => panic!("unexpected server response: {:?}", std::mem::discriminant(&other)),
|
||||
};
|
||||
|
||||
// Client receives msg2 -> Established
|
||||
let action = client.on_event(OstpEvent::Inbound(msg2)).unwrap();
|
||||
match action {
|
||||
ProtocolAction::HandshakePayload(_, _) => {}
|
||||
ProtocolAction::Multiple(_) => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Both should be Established
|
||||
assert_eq!(client.state(), OstpState::Established);
|
||||
assert_eq!(server.state(), OstpState::Established);
|
||||
|
||||
(client, server)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_handshake() {
|
||||
let (client, server) = do_handshake();
|
||||
assert_eq!(client.state(), OstpState::Established);
|
||||
assert_eq!(server.state(), OstpState::Established);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_exchange_client_to_server() {
|
||||
let (mut client, mut server) = do_handshake();
|
||||
|
||||
// Client sends data
|
||||
let payload = Bytes::from_static(b"hello from client");
|
||||
let action = client.on_event(OstpEvent::Outbound(1, payload.clone())).unwrap();
|
||||
let datagram = match action {
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
_ => panic!("expected SendDatagram"),
|
||||
};
|
||||
|
||||
// Server receives and decrypts
|
||||
let action = server.on_event(OstpEvent::Inbound(datagram)).unwrap();
|
||||
match action {
|
||||
ProtocolAction::DeliverApp(stream_id, data) => {
|
||||
assert_eq!(stream_id, 1);
|
||||
assert_eq!(data.as_ref(), b"hello from client");
|
||||
}
|
||||
ProtocolAction::Multiple(actions) => {
|
||||
let found = actions.iter().any(|a| matches!(a,
|
||||
ProtocolAction::DeliverApp(1, d) if d.as_ref() == b"hello from client"
|
||||
));
|
||||
assert!(found, "expected DeliverApp in Multiple");
|
||||
}
|
||||
_ => panic!("expected DeliverApp or Multiple"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_exchange_server_to_client() {
|
||||
let (mut client, mut server) = do_handshake();
|
||||
|
||||
// Server sends data
|
||||
let payload = Bytes::from_static(b"hello from server");
|
||||
let action = server.on_event(OstpEvent::Outbound(2, payload.clone())).unwrap();
|
||||
let datagram = match action {
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
_ => panic!("expected SendDatagram"),
|
||||
};
|
||||
|
||||
// Client receives
|
||||
let action = client.on_event(OstpEvent::Inbound(datagram)).unwrap();
|
||||
match action {
|
||||
ProtocolAction::DeliverApp(stream_id, data) => {
|
||||
assert_eq!(stream_id, 2);
|
||||
assert_eq!(data.as_ref(), b"hello from server");
|
||||
}
|
||||
ProtocolAction::Multiple(actions) => {
|
||||
let found = actions.iter().any(|a| matches!(a,
|
||||
ProtocolAction::DeliverApp(2, d) if d.as_ref() == b"hello from server"
|
||||
));
|
||||
assert!(found, "expected DeliverApp in Multiple");
|
||||
}
|
||||
_ => panic!("expected DeliverApp or Multiple"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_close_sequence() {
|
||||
let (mut client, mut server) = do_handshake();
|
||||
|
||||
// Client sends Close
|
||||
let action = client.on_event(OstpEvent::Close).unwrap();
|
||||
let close_datagram = match action {
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
_ => panic!("expected SendDatagram for Close"),
|
||||
};
|
||||
assert_eq!(client.state(), OstpState::Closing);
|
||||
|
||||
// Server receives Close
|
||||
let _action = server.on_event(OstpEvent::Inbound(close_datagram)).unwrap();
|
||||
assert_eq!(server.state(), OstpState::Closed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_psk_handshake_fails() {
|
||||
let mut client = ProtocolMachine::new(make_config(NoiseRole::Initiator)).unwrap();
|
||||
|
||||
let mut bad_psk_config = make_config(NoiseRole::Responder);
|
||||
bad_psk_config.psk = [0xFF; 32]; // Different PSK
|
||||
let mut server = ProtocolMachine::new(bad_psk_config).unwrap();
|
||||
|
||||
let action = client.on_event(OstpEvent::Start).unwrap();
|
||||
let msg1 = match action {
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
_ => panic!("expected SendDatagram"),
|
||||
};
|
||||
|
||||
let _ = server.on_event(OstpEvent::Start).unwrap();
|
||||
// Server should fail to process handshake with wrong PSK
|
||||
let result = server.on_event(OstpEvent::Inbound(msg1));
|
||||
// Either an error or the server stays in Handshaking (never reaches Established)
|
||||
assert!(result.is_err() || server.state() != OstpState::Established);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_congestion_controller_after_handshake() {
|
||||
let (client, _server) = do_handshake();
|
||||
// CC should be in SlowStart after handshake
|
||||
let budget = client.cc.retransmit_budget();
|
||||
assert!(budget >= 2, "initial retransmit budget should be >= 2, got {}", budget);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_data_frames() {
|
||||
let (mut client, mut server) = do_handshake();
|
||||
|
||||
// Send 10 frames
|
||||
for i in 0..10u8 {
|
||||
let payload = Bytes::from(vec![i; 100]);
|
||||
let action = client.on_event(OstpEvent::Outbound(1, payload)).unwrap();
|
||||
let datagram = match action {
|
||||
ProtocolAction::SendDatagram(d) => d,
|
||||
_ => panic!("expected SendDatagram for frame {}", i),
|
||||
};
|
||||
|
||||
let action = server.on_event(OstpEvent::Inbound(datagram)).unwrap();
|
||||
match action {
|
||||
ProtocolAction::DeliverApp(_, data) => {
|
||||
assert_eq!(data.len(), 100);
|
||||
assert_eq!(data[0], i);
|
||||
}
|
||||
ProtocolAction::Multiple(actions) => {
|
||||
let found = actions.iter().any(|a| matches!(a,
|
||||
ProtocolAction::DeliverApp(_, d) if d.len() == 100 && d[0] == i
|
||||
));
|
||||
assert!(found, "frame {} not found in Multiple", i);
|
||||
}
|
||||
_ => panic!("unexpected action for frame {}", i),
|
||||
}
|
||||
}
|
||||
|
||||
// Verify in-flight state
|
||||
assert!(client.in_flight_count() > 0, "should have in-flight frames");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tick_no_crash() {
|
||||
let (mut client, mut server) = do_handshake();
|
||||
|
||||
// Tick should not crash on either side
|
||||
let _ = client.on_event(OstpEvent::Tick).unwrap();
|
||||
let _ = server.on_event(OstpEvent::Tick).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue