#[cfg(all(feature = "streaming", feature = "http"))]
mod streaming_tests {
use futures_util::StreamExt;
use sdforge::streaming::{create_stream_channel, stream_to_sse, StreamEvent, StreamResponse};
use std::time::Duration;
use tokio_stream::wrappers::ReceiverStream;
#[test]
fn test_create_stream_channel() {
let (tx, response) = create_stream_channel::<String>(10);
assert!(tx.capacity() > 0);
let _ = response;
}
#[test]
fn test_stream_response_new() {
let (_tx, rx) = tokio::sync::mpsc::channel::<Result<String, String>>(10);
let stream = ReceiverStream::new(rx);
let response = StreamResponse::new(stream);
let _ = response;
}
#[tokio::test]
async fn test_stream_response_single() {
let response = StreamResponse::<String>::single("test data".to_string());
let _ = response;
}
#[test]
fn test_stream_response_final_marker() {
let response = StreamResponse::<String>::final_marker();
let _ = response;
}
#[tokio::test]
async fn test_stream_to_sse_basic() {
let (_tx, rx) = tokio::sync::mpsc::channel::<Result<String, String>>(10);
let sse_stream = stream_to_sse(
ReceiverStream::new(rx),
|msg: Result<String, String>| -> StreamEvent {
match msg {
Ok(data) => StreamEvent::data(serde_json::Value::String(data)),
Err(e) => StreamEvent::error(e),
}
},
);
let _stream = Box::pin(sse_stream);
}
#[test]
fn test_stream_event_data() {
let event = StreamEvent::<String>::Data {
id: Some("1".to_string()),
event_name: Some("message".to_string()),
data: "test data".to_string(),
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("test data"));
}
#[tokio::test]
async fn test_sse_event_stream_basic() {
use futures_util::stream;
let input_stream = stream::iter(vec![1i32, 2, 3, 4, 5]);
let sse_stream = stream_to_sse(input_stream, |n| StreamEvent::data(serde_json::json!(n)));
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
let data_events: Vec<_> = results
.iter()
.filter(|r| r.contains(r#""type":"data""#))
.collect();
assert_eq!(data_events.len(), 5, "Should have 5 data events");
for result in results.iter() {
if result.contains(r#""type":"data""#) {
assert!(
result.starts_with("data: "),
"SSE data should start with 'data: ' prefix"
);
assert!(
result.ends_with("\n\n"),
"SSE data should end with double newline"
);
}
}
assert!(
results.last().unwrap().contains("complete"),
"Last event should be completion signal"
);
}
#[tokio::test]
async fn test_sse_multiple_event_types() {
use futures_util::stream;
let events = vec![
StreamEvent::data(serde_json::json!("first")),
StreamEvent::ping(),
StreamEvent::data(serde_json::json!("second")),
StreamEvent::error("test error".to_string()),
StreamEvent::data(serde_json::json!("third")),
];
let input_stream = stream::iter(events);
let sse_stream = stream_to_sse(input_stream, |event| event);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
let data_count = results
.iter()
.filter(|r| r.contains(r#""type":"data""#))
.count();
let ping_count = results
.iter()
.filter(|r| r.contains(r#""type":"ping""#))
.count();
let error_count = results
.iter()
.filter(|r| r.contains(r#""type":"error""#))
.count();
let complete_count = results.iter().filter(|r| r.contains("complete")).count();
assert_eq!(data_count, 3, "Should have 3 data events");
assert_eq!(ping_count, 1, "Should have 1 ping event");
assert_eq!(error_count, 1, "Should have 1 error event");
assert!(complete_count >= 1, "Should have at least 1 complete event");
}
#[tokio::test]
async fn test_sse_event_id_tracking() {
let event1 = StreamEvent::Data {
id: Some("msg-001".to_string()),
event_name: Some("update".to_string()),
data: serde_json::json!("first message"),
};
let event2 = StreamEvent::Data {
id: Some("msg-002".to_string()),
event_name: Some("update".to_string()),
data: serde_json::json!("second message"),
};
let event3 = StreamEvent::Data {
id: Some("msg-003".to_string()),
event_name: Some("final".to_string()),
data: serde_json::json!("last message"),
};
let json1 = serde_json::to_string(&event1).unwrap();
let json2 = serde_json::to_string(&event2).unwrap();
let json3 = serde_json::to_string(&event3).unwrap();
assert!(json1.contains(r#""id":"msg-001""#));
assert!(json2.contains(r#""id":"msg-002""#));
assert!(json3.contains(r#""id":"msg-003""#));
assert!(json1.contains(r#""event_name":"update""#));
assert!(json2.contains(r#""event_name":"update""#));
assert!(json3.contains(r#""event_name":"final""#));
let deserialized1: StreamEvent<serde_json::Value> = serde_json::from_str(&json1).unwrap();
let deserialized2: StreamEvent<serde_json::Value> = serde_json::from_str(&json2).unwrap();
let deserialized3: StreamEvent<serde_json::Value> = serde_json::from_str(&json3).unwrap();
match (deserialized1, deserialized2, deserialized3) {
(
StreamEvent::Data { id: id1, .. },
StreamEvent::Data { id: id2, .. },
StreamEvent::Data { id: id3, .. },
) => {
assert_eq!(id1, Some("msg-001".to_string()));
assert_eq!(id2, Some("msg-002".to_string()));
assert_eq!(id3, Some("msg-003".to_string()));
}
_ => panic!("Expected Data events"),
}
}
#[tokio::test]
async fn test_sse_retry_directive() {
let retry_ms = 5000;
let retry_line = format!("retry: {}\n", retry_ms);
assert_eq!(retry_line, "retry: 5000\n");
for retry_value in [1000, 5000, 10000, 30000] {
let retry_line = format!("retry: {}\n", retry_value);
assert!(retry_line.contains(&retry_value.to_string()));
}
}
#[tokio::test]
async fn test_sse_comment_lines() {
let comment = ": This is a comment\n\n";
assert!(comment.starts_with(": "));
let comments = vec![
": Server started\n",
": Processing request\n",
": Request completed\n",
];
for comment in comments {
assert!(comment.starts_with(": "), "Comment should start with ': '");
}
}
#[tokio::test]
async fn test_stream_response_chunks() {
let (tx, response) = create_stream_channel::<String>(10);
let chunks = vec!["chunk1", "chunk2", "chunk3", "chunk4", "chunk5"];
for chunk in &chunks {
tx.send(Ok(chunk.to_string())).await.unwrap();
}
drop(tx);
let mut stream = response.into_stream();
let mut received_chunks = Vec::new();
while let Some(result) = stream.next().await {
received_chunks.push(result.unwrap());
}
assert_eq!(received_chunks.len(), chunks.len());
assert_eq!(received_chunks, chunks);
}
#[tokio::test]
async fn test_stream_large_payload() {
let (tx, response) = create_stream_channel::<Vec<u8>>(10);
let large_data: Vec<u8> = (0..1024 * 1024).map(|i| (i % 256) as u8).collect();
let expected_len = large_data.len();
tx.send(Ok(large_data)).await.unwrap();
drop(tx);
let mut stream = response.into_stream();
let result = stream.next().await;
assert!(result.is_some());
let received = result.unwrap().unwrap();
assert_eq!(received.len(), expected_len);
}
#[tokio::test]
async fn test_stream_backpressure_handling() {
let (tx, response) = create_stream_channel::<String>(2);
assert!(tx.try_send(Ok("item1".to_string())).is_ok());
assert!(tx.try_send(Ok("item2".to_string())).is_ok());
let result = tx.try_send(Ok("item3".to_string()));
assert!(result.is_err(), "try_send should fail when buffer is full");
let mut stream = response.into_stream();
let item1 = stream.next().await.unwrap().unwrap();
let item2 = stream.next().await.unwrap().unwrap();
assert_eq!(item1, "item1");
assert_eq!(item2, "item2");
}
#[tokio::test]
async fn test_stream_cancellation() {
let (tx, response) = create_stream_channel::<i32>(10);
tx.send(Ok(1)).await.unwrap();
tx.send(Ok(2)).await.unwrap();
let mut stream = response.into_stream();
let item1 = stream.next().await.unwrap().unwrap();
let item2 = stream.next().await.unwrap().unwrap();
assert_eq!(item1, 1);
assert_eq!(item2, 2);
drop(stream);
let _send_result = tx.send(Ok(3)).await;
let (_tx2, response2) = create_stream_channel::<i32>(10);
let _stream2 = response2.into_stream();
}
#[tokio::test]
async fn test_stream_error_recovery() {
let (tx, response) = create_stream_channel::<String>(10);
tx.send(Ok("success1".to_string())).await.unwrap();
tx.send(Err("error1".to_string())).await.unwrap();
tx.send(Ok("success2".to_string())).await.unwrap();
tx.send(Err("error2".to_string())).await.unwrap();
tx.send(Ok("success3".to_string())).await.unwrap();
drop(tx);
let mut stream = response.into_stream();
let mut successes = Vec::new();
let mut errors = Vec::new();
while let Some(result) = stream.next().await {
match result {
Ok(data) => successes.push(data),
Err(e) => errors.push(e),
}
}
assert_eq!(successes.len(), 3);
assert_eq!(errors.len(), 2);
assert_eq!(successes, vec!["success1", "success2", "success3"]);
assert_eq!(errors, vec!["error1", "error2"]);
}
#[tokio::test]
async fn test_stream_completion_signal() {
use futures_util::stream;
let empty_stream = stream::iter(Vec::<i32>::new());
let sse_stream = stream_to_sse(empty_stream, |_| StreamEvent::complete());
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert_eq!(results.len(), 1);
assert!(
results[0].contains("complete"),
"Should have completion signal"
);
}
#[tokio::test]
async fn test_stream_timeout_handling() {
let (_tx, rx) = tokio::sync::mpsc::channel::<Result<String, String>>(10);
let stream = ReceiverStream::new(rx);
let timeout_duration = Duration::from_millis(100);
let start = std::time::Instant::now();
let mut stream = Box::pin(stream);
let result = tokio::time::timeout(timeout_duration, stream.next()).await;
assert!(
result.is_err() || result.unwrap().is_none(),
"Should timeout or return None"
);
assert!(
start.elapsed() >= timeout_duration,
"Should have waited at least the timeout duration"
);
}
#[tokio::test]
async fn test_stream_immediate_sender_drop() {
let (tx, response) = create_stream_channel::<String>(10);
drop(tx);
let count = response.into_stream().count().await;
assert_eq!(
count, 0,
"Stream should be empty when sender is dropped immediately"
);
}
#[tokio::test]
async fn test_stream_concurrent_producer_consumer() {
let (tx, response) = create_stream_channel::<usize>(100);
let item_count = 100;
let producer_handle = tokio::spawn(async move {
for i in 0..item_count {
tx.send(Ok(i)).await.unwrap();
}
});
let consumer_handle = tokio::spawn(async move {
let mut count = 0;
let mut stream = response.into_stream();
while stream.next().await.is_some() {
count += 1;
}
count
});
producer_handle.await.unwrap();
let received_count = consumer_handle.await.unwrap();
assert_eq!(received_count, item_count);
}
#[tokio::test]
async fn test_stream_sequential_with_final_marker() {
let (tx, response) = create_stream_channel::<String>(10);
for i in 1..=10 {
tx.send(Ok(format!("item-{}", i))).await.unwrap();
}
drop(tx);
let items: Vec<String> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), 10);
assert_eq!(items[0], "item-1");
assert_eq!(items[9], "item-10");
}
#[tokio::test]
async fn test_sse_complex_json_data() {
use futures_util::stream;
let complex_data = serde_json::json!({
"user": {
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"roles": ["admin", "user"],
"metadata": {
"created_at": "2026-01-01T00:00:00Z",
"last_login": "2026-03-26T10:30:00Z"
}
},
"session": {
"token": "abc123",
"expires_in": 3600
}
});
let input_stream = stream::iter(vec![complex_data]);
let sse_stream = stream_to_sse(input_stream, StreamEvent::data);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(!results.is_empty());
let data_event = &results[0];
assert!(data_event.contains(r#""type":"data""#));
assert!(data_event.contains(r#""name":"Alice""#));
assert!(data_event.contains(r#""token":"abc123""#));
}
#[tokio::test]
async fn test_stream_channel_various_capacities() {
for capacity in [1, 5, 10, 100, 1000] {
let (tx, response) = create_stream_channel::<i32>(capacity);
assert_eq!(tx.capacity(), capacity);
tx.send(Ok(42)).await.unwrap();
let item = response.into_stream().next().await;
assert_eq!(item.unwrap().unwrap(), 42);
}
}
#[tokio::test]
async fn test_stream_rapid_production() {
let (tx, response) = create_stream_channel::<i32>(1000);
let item_count: usize = 500;
for i in 0..item_count {
tx.send(Ok(i as i32)).await.unwrap();
}
drop(tx);
let items: Vec<i32> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), item_count);
assert_eq!(items[0], 0);
assert_eq!(items[item_count - 1], (item_count - 1) as i32);
}
#[tokio::test]
async fn test_sse_event_name_formatting() {
use futures_util::stream;
let events = vec![
StreamEvent::Data {
id: Some("1".to_string()),
event_name: Some("custom-update".to_string()),
data: serde_json::json!("update data"),
},
StreamEvent::Data {
id: Some("2".to_string()),
event_name: Some("notification".to_string()),
data: serde_json::json!("notification data"),
},
];
let input_stream = stream::iter(events);
let sse_stream = stream_to_sse(input_stream, |e| e);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(results[0].contains(r#""event_name":"custom-update""#));
assert!(results[1].contains(r#""event_name":"notification""#));
}
#[tokio::test]
async fn test_sse_id_line_formatting() {
use futures_util::stream;
let event = StreamEvent::Data {
id: Some("msg-abc-123".to_string()),
event_name: None,
data: serde_json::json!("test"),
};
let input_stream = stream::iter(vec![event]);
let sse_stream = stream_to_sse(input_stream, |e| e);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(results[0].contains(r#""id":"msg-abc-123""#));
}
#[tokio::test]
async fn test_sse_multiline_data_content() {
use futures_util::stream;
let multiline_data = "Line 1\nLine 2\nLine 3";
let event = StreamEvent::data(serde_json::json!(multiline_data));
let input_stream = stream::iter(vec![event]);
let sse_stream = stream_to_sse(input_stream, |e| e);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(results[0].contains("Line 1"));
assert!(results[0].contains("Line 2"));
assert!(results[0].contains("Line 3"));
}
#[tokio::test]
async fn test_sse_ping_timestamp_accuracy() {
use futures_util::stream;
let _before = chrono::Utc::now().timestamp();
let input_stream = stream::iter(vec![()]);
let sse_stream = stream_to_sse(input_stream, |_| StreamEvent::ping());
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
let _after = chrono::Utc::now().timestamp();
assert!(results[0].contains(r#""type":"ping""#));
assert!(results[0].contains("timestamp"));
}
#[tokio::test]
async fn test_stream_channel_closed_behavior() {
let (tx, response) = create_stream_channel::<String>(10);
drop(tx);
let count = response.into_stream().count().await;
assert_eq!(count, 0, "Stream should be empty after sender drop");
}
#[tokio::test]
async fn test_stream_zero_capacity_channel() {
let (tx, response) = create_stream_channel::<i32>(1);
tx.send(Ok(1)).await.unwrap();
drop(tx);
let items: Vec<i32> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items, vec![1]);
}
#[tokio::test]
async fn test_stream_alternating_success_error() {
let (tx, response) = create_stream_channel::<String>(10);
let sequence = vec![
Ok("item-1".to_string()),
Err("err-1".to_string()),
Ok("item-2".to_string()),
Err("err-2".to_string()),
Ok("item-3".to_string()),
];
for item in sequence {
tx.send(item).await.unwrap();
}
drop(tx);
let mut stream = response.into_stream();
let mut results = Vec::new();
while let Some(result) = stream.next().await {
results.push(result);
}
assert_eq!(results.len(), 5);
assert!(results[0].is_ok());
assert!(results[1].is_err());
assert!(results[2].is_ok());
assert!(results[3].is_err());
assert!(results[4].is_ok());
}
#[tokio::test]
async fn test_stream_many_small_items() {
let (tx, response) = create_stream_channel::<u8>(1000);
let item_count = 100;
let consumer_handle = tokio::spawn(async move {
let items: Vec<u8> = response.into_stream().map(|r| r.unwrap()).collect().await;
items
});
for i in 0..item_count {
tx.send(Ok(i as u8)).await.unwrap();
}
drop(tx);
let items = consumer_handle.await.unwrap();
assert_eq!(items.len(), item_count);
assert_eq!(items[0], 0);
assert_eq!(items[item_count - 1], ((item_count - 1) % 256) as u8);
}
#[tokio::test]
async fn test_stream_small_buffer_large_data() {
let (tx, response) = create_stream_channel::<String>(100);
let total_items = 20;
let consumer_handle = tokio::spawn(async move {
let items: Vec<String> = response.into_stream().map(|r| r.unwrap()).collect().await;
items
});
for i in 0..total_items {
let result = tx.send(Ok(format!("item-{}", i))).await;
assert!(result.is_ok(), "Send should succeed with consumer running");
}
drop(tx);
let items = consumer_handle.await.unwrap();
assert_eq!(items.len(), total_items);
}
#[tokio::test]
async fn test_stream_immediate_consumption() {
let (tx, response) = create_stream_channel::<i32>(10);
let expected_sum: i32 = (1..=10).sum();
for i in 1..=10 {
tx.send(Ok(i)).await.unwrap();
}
drop(tx);
let mut stream = response.into_stream();
let mut sum = 0;
while let Some(result) = stream.next().await {
sum += result.unwrap();
}
assert_eq!(sum, expected_sum);
}
#[tokio::test]
async fn test_stream_response_state_management() {
let response1 = StreamResponse::<String>::single("first".to_string());
let response2 = StreamResponse::<String>::single("second".to_string());
assert!(!response1.is_final());
assert!(!response2.is_final());
let final_response: StreamResponse<String> = StreamResponse::final_marker();
assert!(final_response.is_final());
}
#[tokio::test]
async fn test_multiple_streams_concurrent() {
let (tx1, response1) = create_stream_channel::<String>(10);
let (tx2, response2) = create_stream_channel::<String>(10);
tx1.send(Ok("stream1-item".to_string())).await.unwrap();
tx2.send(Ok("stream2-item".to_string())).await.unwrap();
drop(tx1);
drop(tx2);
let item1 = response1.into_stream().next().await.unwrap().unwrap();
let item2 = response2.into_stream().next().await.unwrap().unwrap();
assert_eq!(item1, "stream1-item");
assert_eq!(item2, "stream2-item");
}
#[tokio::test]
async fn test_stream_channel_sender_clone() {
let (tx, response) = create_stream_channel::<i32>(100);
let tx_clone = tx.clone();
let tx_for_drop = tx.clone();
let handle1 = tokio::spawn(async move {
for i in 0..50 {
tx.send(Ok(i)).await.unwrap();
}
});
let handle2 = tokio::spawn(async move {
let tx_clone_inner = tx_clone.clone();
for i in 50..100 {
tx_clone_inner.send(Ok(i)).await.unwrap();
}
});
handle1.await.unwrap();
handle2.await.unwrap();
drop(tx_for_drop);
let items: Vec<i32> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), 100);
}
#[tokio::test]
async fn test_sse_large_payload_serialization() {
use futures_util::stream;
let large_data = serde_json::json!({
"items": (0..1000).map(|i| {
serde_json::json!({
"id": i,
"name": format!("item-{}", i),
"description": "x".repeat(100)
})
}).collect::<Vec<_>>()
});
let input_stream = stream::iter(vec![large_data]);
let sse_stream = stream_to_sse(input_stream, StreamEvent::data);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(!results.is_empty());
assert!(results[0].contains("items"));
}
#[tokio::test]
async fn test_sse_custom_event_types() {
use futures_util::stream;
let custom_event_names = ["progress", "notification", "alert", "update", "status"];
let events: Vec<StreamEvent<serde_json::Value>> = custom_event_names
.iter()
.map(|name| StreamEvent::Data {
id: None,
event_name: Some(name.to_string()),
data: serde_json::json!(name),
})
.collect();
let input_stream = stream::iter(events);
let sse_stream = stream_to_sse(input_stream, |e| e);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert_eq!(results.len(), custom_event_names.len() + 1); }
#[tokio::test]
async fn test_sse_rapid_ping_events() {
use futures_util::stream;
let input_stream = stream::iter(vec![(); 10]);
let sse_stream = stream_to_sse(input_stream, |_| StreamEvent::ping());
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert_eq!(results.len(), 11);
for result in &results[..10] {
assert!(result.contains(r#""type":"ping""#));
}
}
#[tokio::test]
async fn test_sse_serialization_roundtrip() {
let original_data = serde_json::json!({
"user": "Alice",
"age": 30,
"active": true,
"roles": ["admin", "user"]
});
let event = StreamEvent::data(original_data.clone());
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains(r#""type":"data""#));
let deserialized: StreamEvent<serde_json::Value> = serde_json::from_str(&json).unwrap();
match deserialized {
StreamEvent::Data { data, .. } => {
assert_eq!(data["user"], "Alice");
assert_eq!(data["age"], 30);
}
_ => panic!("Expected Data event"),
}
}
#[tokio::test]
async fn test_stream_error_at_end() {
let (tx, response) = create_stream_channel::<String>(10);
tx.send(Ok("success1".to_string())).await.unwrap();
tx.send(Ok("success2".to_string())).await.unwrap();
tx.send(Err("final error".to_string())).await.unwrap();
drop(tx);
let mut stream = response.into_stream();
let mut results = Vec::new();
while let Some(result) = stream.next().await {
results.push(result);
}
assert_eq!(results.len(), 3);
assert!(results[0].is_ok());
assert!(results[1].is_ok());
assert!(results[2].is_err());
if let Err(e) = &results[2] {
assert_eq!(e, "final error");
}
}
#[tokio::test]
async fn test_stream_all_errors() {
let (tx, response) = create_stream_channel::<String>(10);
tx.send(Err("error1".to_string())).await.unwrap();
tx.send(Err("error2".to_string())).await.unwrap();
tx.send(Err("error3".to_string())).await.unwrap();
drop(tx);
let mut stream = response.into_stream();
let mut errors = Vec::new();
while let Some(result) = stream.next().await {
if let Err(e) = result {
errors.push(e);
}
}
assert_eq!(errors.len(), 3);
assert_eq!(errors, vec!["error1", "error2", "error3"]);
}
#[tokio::test]
async fn test_stream_empty_data_items() {
let (tx, response) = create_stream_channel::<String>(10);
tx.send(Ok("".to_string())).await.unwrap();
tx.send(Ok("non-empty".to_string())).await.unwrap();
tx.send(Ok("".to_string())).await.unwrap();
drop(tx);
let items: Vec<String> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), 3);
assert_eq!(items[0], "");
assert_eq!(items[1], "non-empty");
assert_eq!(items[2], "");
}
#[tokio::test]
async fn test_stream_json_special_values() {
use futures_util::stream;
let values = vec![
serde_json::json!(null),
serde_json::json!(true),
serde_json::json!(false),
serde_json::json!(0),
serde_json::json!(-1),
serde_json::json!(0.0),
serde_json::json!(-1.5),
];
let input_stream = stream::iter(values);
let sse_stream = stream_to_sse(input_stream, StreamEvent::data);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
let data_events = results
.iter()
.filter(|r| r.contains(r#""type":"data""#))
.count();
assert_eq!(data_events, 7);
}
#[tokio::test]
async fn test_stream_nested_json_arrays() {
use futures_util::stream;
let nested_data = serde_json::json!({
"matrix": [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
],
"nested": {
"level1": {
"level2": {
"level3": ["deep", "array"]
}
}
}
});
let input_stream = stream::iter(vec![nested_data]);
let sse_stream = stream_to_sse(input_stream, StreamEvent::data);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(!results.is_empty());
assert!(results[0].contains("matrix"));
assert!(results[0].contains("nested"));
}
#[tokio::test]
async fn test_stream_burst_handling() {
let (tx, response) = create_stream_channel::<i32>(1000);
let burst_size: usize = 500;
for i in 0..burst_size {
tx.send(Ok(i as i32)).await.unwrap();
}
drop(tx);
let items: Vec<i32> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), burst_size);
}
#[tokio::test]
async fn test_stream_delayed_sending() {
let (tx, response) = create_stream_channel::<i32>(10);
for i in 0..5 {
tx.send(Ok(i)).await.unwrap();
tokio::time::sleep(Duration::from_millis(10)).await;
}
drop(tx);
let items: Vec<i32> = response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), 5);
assert_eq!(items, vec![0, 1, 2, 3, 4]);
}
#[tokio::test]
async fn test_stream_selective_consumption() {
let (tx, response) = create_stream_channel::<i32>(10);
for i in 1..=10 {
tx.send(Ok(i)).await.unwrap();
}
drop(tx);
let mut stream = response.into_stream();
let mut even_numbers = Vec::new();
while let Some(result) = stream.next().await {
let value = result.unwrap();
if value % 2 == 0 {
even_numbers.push(value);
}
}
assert_eq!(even_numbers, vec![2, 4, 6, 8, 10]);
}
#[tokio::test]
async fn test_realistic_progress_scenario() {
let (tx, response) = create_stream_channel::<serde_json::Value>(50);
let total_steps = 20;
for step in 0..total_steps {
let progress = serde_json::json!({
"step": step + 1,
"total": total_steps,
"percent": ((step + 1) as f64 / total_steps as f64 * 100.0).round() as u8,
"message": format!("Processing step {} of {}", step + 1, total_steps)
});
tx.send(Ok(progress)).await.unwrap();
tokio::time::sleep(Duration::from_millis(1)).await;
}
let completion = serde_json::json!({
"status": "complete",
"total_steps": total_steps
});
tx.send(Ok(completion)).await.unwrap();
drop(tx);
let items: Vec<serde_json::Value> =
response.into_stream().map(|r| r.unwrap()).collect().await;
assert_eq!(items.len(), total_steps + 1);
assert_eq!(items[0]["step"], 1);
assert_eq!(items.last().unwrap()["status"], "complete");
}
#[tokio::test]
async fn test_realistic_log_stream_scenario() {
use futures_util::stream;
let log_entries = vec![
(
StreamEvent::data(serde_json::json!({
"level": "INFO",
"message": "Server started",
"timestamp": "2026-03-26T10:00:00Z"
})),
"info",
),
(
StreamEvent::data(serde_json::json!({
"level": "DEBUG",
"message": "Processing request",
"timestamp": "2026-03-26T10:00:01Z"
})),
"debug",
),
(
StreamEvent::data(serde_json::json!({
"level": "WARN",
"message": "High memory usage",
"timestamp": "2026-03-26T10:00:02Z"
})),
"warn",
),
(
StreamEvent::error("Connection timeout".to_string()),
"error",
),
];
let input_stream = stream::iter(log_entries.into_iter().map(|(e, _)| e));
let sse_stream = stream_to_sse(input_stream, |e| e);
let results: Vec<String> = sse_stream.map(|r| r.unwrap()).collect().await;
assert!(results.len() >= 5);
}
}