use fake::{Fake, Faker};
use lark_websocket_protobuf::pbbp2::{Frame, Header};
use open_lark::client::ws_client::frame_handler::FrameHandler;
use proptest::prelude::*;
use quickcheck::{quickcheck, TestResult};
use serde_json::Value;
use std::collections::HashMap;
#[cfg(test)]
mod frame_handler_properties {
use super::*;
fn arbitrary_frame() -> impl Strategy<Value = Frame> {
(
any::<u64>(), any::<u64>(), 0..100i32, 0..2i32, prop::collection::vec(arbitrary_header(), 0..10), prop::option::of(prop::collection::vec(any::<u8>(), 0..10000)), ).prop_map(|(seq_id, log_id, service, method, headers, payload)| Frame {
seq_id,
log_id,
service,
method,
headers,
payload_encoding: None,
payload_type: None,
payload,
log_id_new: None,
})
}
fn arbitrary_header() -> impl Strategy<Value = Header> {
(
"[a-zA-Z0-9_]{1,50}", ".*", ).prop_map(|(key, value)| Header { key, value })
}
proptest! {
#[test]
fn frame_handler_never_panics(frame in arbitrary_frame()) {
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::builder().build();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let _result = FrameHandler::handle_frame(frame, &handler, &tx).await;
});
}
}
proptest! {
#[test]
fn control_frame_handling_is_safe(
seq_id in any::<u64>(),
headers in prop::collection::vec(arbitrary_header(), 0..10),
payload in prop::option::of(prop::collection::vec(any::<u8>(), 0..1000))
) {
let frame = Frame {
seq_id,
log_id: 0,
service: 0,
method: 0, headers,
payload_encoding: None,
payload_type: None,
payload,
log_id_new: None,
};
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::builder().build();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let _result = FrameHandler::handle_frame(frame, &handler, &tx).await;
});
}
}
proptest! {
#[test]
fn data_frame_handles_arbitrary_payloads(
payload_bytes in prop::collection::vec(any::<u8>(), 0..5000)
) {
let frame = Frame {
seq_id: 1,
log_id: 1,
service: 1,
method: 1, headers: vec![
Header { key: "type".to_string(), value: "event".to_string() },
Header { key: "message_id".to_string(), value: "test_msg".to_string() },
Header { key: "trace_id".to_string(), value: "test_trace".to_string() },
],
payload_encoding: None,
payload_type: None,
payload: Some(payload_bytes),
log_id_new: None,
};
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::builder().build();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let _result = FrameHandler::handle_frame(frame, &handler, &tx).await;
});
}
}
#[quickcheck]
fn frame_serialization_roundtrip(seq_id: u64, service: i32, method: u8) -> TestResult {
if method > 1 { return TestResult::discard(); }
let original_frame = Frame {
seq_id,
log_id: 0,
service,
method: method as i32,
headers: vec![],
payload_encoding: None,
payload_type: None,
payload: Some(vec![1, 2, 3]),
log_id_new: None,
};
TestResult::from_bool(
original_frame.seq_id == seq_id &&
original_frame.service == service &&
original_frame.method == method as i32
)
}
#[tokio::test]
async fn test_extremely_large_payload() {
let large_payload = vec![0u8; 1024 * 1024]; let frame = Frame {
seq_id: 1,
log_id: 1,
service: 1,
method: 1,
headers: vec![
Header { key: "type".to_string(), value: "event".to_string() },
],
payload_encoding: None,
payload_type: None,
payload: Some(large_payload),
log_id_new: None,
};
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::new();
let _result = FrameHandler::handle_frame(frame, &handler, &tx).await;
}
#[tokio::test]
async fn test_empty_payload() {
let frame = Frame {
seq_id: 1,
log_id: 1,
service: 1,
method: 1,
headers: vec![
Header { key: "type".to_string(), value: "event".to_string() },
],
payload_encoding: None,
payload_type: None,
payload: None,
};
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::new();
let result = FrameHandler::handle_frame(frame, &handler, &tx).await;
assert!(result.is_none());
}
#[tokio::test]
async fn test_malformed_json_in_pong() {
let malformed_json = b"{invalid json}";
let frame = Frame {
seq_id: 1,
log_id: 1,
service: 1,
method: 0, headers: vec![
Header { key: "type".to_string(), value: "pong".to_string() },
],
payload_encoding: None,
payload_type: None,
payload: Some(malformed_json.to_vec()),
log_id_new: None,
};
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::new();
let result = FrameHandler::handle_frame(frame, &handler, &tx).await;
assert!(result.is_none());
}
#[tokio::test]
async fn test_invalid_header_combinations() {
let frame = Frame {
seq_id: 1,
log_id: 1,
service: 1,
method: 1,
headers: vec![
Header { key: "invalid_type".to_string(), value: "unknown".to_string() },
],
payload_encoding: None,
payload_type: None,
payload: Some(b"some data".to_vec()),
log_id_new: None,
};
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
let handler = open_lark::event::dispatcher::EventDispatcherHandler::new();
let result = FrameHandler::handle_frame(frame, &handler, &tx).await;
assert!(result.is_none());
}
}
mod test_generators {
use super::*;
pub fn generate_valid_event_payload() -> Vec<u8> {
let event = serde_json::json!({
"event": {
"sender": {
"sender_id": {
"open_id": "test_open_id"
}
},
"message": {
"content": "{\"text\":\"test message\"}"
}
}
});
serde_json::to_vec(&event).unwrap()
}
pub fn generate_invalid_event_payloads() -> Vec<Vec<u8>> {
vec![
b"{}".to_vec(),
b"{\"event\":}".to_vec(),
b"not json at all".to_vec(),
vec![123, 34, 116, 101, 115, 116, 34, 58, 0, 125],
format!("{{\"data\":\"{}\"}}", "x".repeat(100000)).into_bytes(),
]
}
}