use crate::broker::protocol::registry::PROTOCOL_VERSION;
use crate::broker::protocol::{Frame, FrameKind, PayloadEncoding};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FrameValidationError {
EnvelopeVersion {
actual: u32,
},
Kind {
expected: FrameKind,
actual: i32,
},
PayloadProtocol {
expected: u32,
actual: u32,
},
PayloadEncoding {
actual: i32,
},
}
pub fn validate_frame_envelope(
frame: &Frame,
expected_kind: FrameKind,
expected_payload_protocol: u32,
) -> Result<(), FrameValidationError> {
if frame.envelope_version != PROTOCOL_VERSION {
return Err(FrameValidationError::EnvelopeVersion {
actual: frame.envelope_version,
});
}
if FrameKind::try_from(frame.kind) != Ok(expected_kind) {
return Err(FrameValidationError::Kind {
expected: expected_kind,
actual: frame.kind,
});
}
if frame.payload_protocol != expected_payload_protocol {
return Err(FrameValidationError::PayloadProtocol {
expected: expected_payload_protocol,
actual: frame.payload_protocol,
});
}
if PayloadEncoding::try_from(frame.payload_encoding) != Ok(PayloadEncoding::None) {
return Err(FrameValidationError::PayloadEncoding {
actual: frame.payload_encoding,
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{validate_frame_envelope, FrameValidationError};
use crate::broker::protocol::registry::{
CONTROL_PAYLOAD_PROTOCOL, HANDOFF_PAYLOAD_PROTOCOL, PROTOCOL_VERSION,
};
use crate::broker::protocol::{Frame, FrameKind, PayloadEncoding};
fn valid_frame(kind: FrameKind, payload_protocol: u32) -> Frame {
Frame {
envelope_version: PROTOCOL_VERSION,
kind: kind as i32,
payload_protocol,
payload: Vec::new(),
request_id: 7,
payload_encoding: PayloadEncoding::None as i32,
deadline_unix_ms: 0,
traceparent: String::new(),
tracestate: String::new(),
}
}
#[test]
fn accepts_well_formed_envelope() {
let frame = valid_frame(FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL);
assert_eq!(
validate_frame_envelope(&frame, FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL),
Ok(())
);
}
#[test]
fn rejects_wrong_envelope_version_first() {
let mut frame = valid_frame(FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL);
frame.envelope_version = 2;
frame.payload_protocol = HANDOFF_PAYLOAD_PROTOCOL;
assert_eq!(
validate_frame_envelope(&frame, FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL),
Err(FrameValidationError::EnvelopeVersion { actual: 2 })
);
}
#[test]
fn rejects_unexpected_kind() {
let frame = valid_frame(FrameKind::Event, CONTROL_PAYLOAD_PROTOCOL);
assert_eq!(
validate_frame_envelope(&frame, FrameKind::Response, CONTROL_PAYLOAD_PROTOCOL),
Err(FrameValidationError::Kind {
expected: FrameKind::Response,
actual: FrameKind::Event as i32,
})
);
}
#[test]
fn rejects_unknown_kind_value() {
let mut frame = valid_frame(FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL);
frame.kind = 99;
assert_eq!(
validate_frame_envelope(&frame, FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL),
Err(FrameValidationError::Kind {
expected: FrameKind::Request,
actual: 99,
})
);
}
#[test]
fn rejects_unexpected_payload_protocol() {
let frame = valid_frame(FrameKind::Request, HANDOFF_PAYLOAD_PROTOCOL);
assert_eq!(
validate_frame_envelope(&frame, FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL),
Err(FrameValidationError::PayloadProtocol {
expected: CONTROL_PAYLOAD_PROTOCOL,
actual: HANDOFF_PAYLOAD_PROTOCOL,
})
);
}
#[test]
fn rejects_compressed_payload() {
let mut frame = valid_frame(FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL);
frame.payload_encoding = PayloadEncoding::Zstd as i32;
assert_eq!(
validate_frame_envelope(&frame, FrameKind::Request, CONTROL_PAYLOAD_PROTOCOL),
Err(FrameValidationError::PayloadEncoding {
actual: PayloadEncoding::Zstd as i32,
})
);
}
}