#[test]
fn test_session_lifecycle() {
let mut session = StreamingSession::new();
assert!(!session.has_any_streamed_content());
session.on_message_start();
assert!(!session.has_any_streamed_content());
let show_prefix = session.on_text_delta(0, "Hello");
assert!(show_prefix);
assert!(session.has_any_streamed_content());
let show_prefix = session.on_text_delta(0, " World");
assert!(!show_prefix);
let was_in_block = session.on_message_stop();
assert!(was_in_block);
}
#[test]
fn test_accumulated_content() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Hello");
session.on_text_delta(0, " World");
let accumulated = session.get_accumulated(ContentType::Text, "0");
assert_eq!(accumulated, Some("Hello World"));
}
#[test]
fn test_reset_between_messages() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "First");
assert!(session.has_any_streamed_content());
session.on_message_stop();
session.on_message_start();
assert!(!session.has_any_streamed_content());
}
#[test]
fn test_multiple_indices() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "First block");
session.on_text_delta(1, "Second block");
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("First block")
);
assert_eq!(
session.get_accumulated(ContentType::Text, "1"),
Some("Second block")
);
}
#[test]
fn test_clear_index() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Before");
let mut fresh_session = StreamingSession::new();
fresh_session.on_message_start();
fresh_session.on_text_delta(0, "After");
assert_eq!(
fresh_session.get_accumulated(ContentType::Text, "0"),
Some("After")
);
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("Before")
);
}
#[test]
fn test_token_by_token_streaming_scenario() {
let mut session = StreamingSession::new();
session.on_message_start();
let tokens = ["H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d", "!"];
for token in tokens {
let show_prefix = session.on_text_delta(0, token);
if token == "H" {
assert!(show_prefix, "First token should show prefix");
} else {
assert!(!show_prefix, "Subsequent tokens should not show prefix");
}
}
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("Hello World!")
);
}
#[test]
fn test_set_and_get_current_message_id() {
let mut session = StreamingSession::new();
assert!(session.get_current_message_id().is_none());
session.set_current_message_id(Some("msg-123".to_string()));
assert_eq!(session.get_current_message_id(), Some("msg-123"));
session.set_current_message_id(None);
assert!(session.get_current_message_id().is_none());
}
#[test]
fn test_mark_message_displayed() {
let mut session = StreamingSession::new();
assert!(!session.is_duplicate_final_message("msg-123"));
session.mark_message_displayed("msg-123");
assert!(session.is_duplicate_final_message("msg-123"));
assert!(!session.is_duplicate_final_message("msg-456"));
}
#[test]
fn test_message_stop_marks_displayed() {
let mut session = StreamingSession::new();
session.set_current_message_id(Some("msg-123".to_string()));
session.on_message_start();
session.on_text_delta(0, "Hello");
session.on_message_stop();
assert!(session.is_duplicate_final_message("msg-123"));
}
#[test]
fn test_multiple_messages_tracking() {
let mut session = StreamingSession::new();
session.set_current_message_id(Some("msg-1".to_string()));
session.on_message_start();
session.on_text_delta(0, "First");
session.on_message_stop();
assert!(session.is_duplicate_final_message("msg-1"));
session.set_current_message_id(Some("msg-2".to_string()));
session.on_message_start();
session.on_text_delta(0, "Second");
session.on_message_stop();
assert!(session.is_duplicate_final_message("msg-1"));
assert!(session.is_duplicate_final_message("msg-2"));
}
#[test]
fn test_repeated_message_start_preserves_output_started() {
let mut session = StreamingSession::new();
session.on_message_start();
let show_prefix = session.on_text_delta(0, "Hello");
assert!(show_prefix, "First delta should show prefix");
let show_prefix = session.on_text_delta(0, " World");
assert!(!show_prefix, "Second delta should not show prefix");
session.on_message_start();
let show_prefix = session.on_text_delta(0, "!");
assert!(
!show_prefix,
"After repeated MessageStart, delta should not show prefix"
);
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("!"),
"Accumulated content should start fresh after repeated MessageStart"
);
}
#[test]
fn test_repeated_message_start_with_normal_reset_between_messages() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "First");
session.on_message_stop();
session.on_message_start();
let show_prefix = session.on_text_delta(0, "Second");
assert!(
show_prefix,
"First delta of new message should show prefix after normal reset"
);
}
#[test]
fn test_repeated_message_start_with_multiple_indices() {
let mut session = StreamingSession::new();
session.on_message_start();
let show_prefix = session.on_text_delta(0, "Index0");
assert!(show_prefix, "First delta for index 0 should show prefix");
let show_prefix = session.on_text_delta(1, "Index1");
assert!(show_prefix, "First delta for index 1 should show prefix");
session.on_message_start();
let show_prefix = session.on_text_delta(0, " more");
assert!(
!show_prefix,
"Delta for index 0 should not show prefix after repeated MessageStart"
);
let show_prefix = session.on_text_delta(1, " more");
assert!(
!show_prefix,
"Delta for index 1 should not show prefix after repeated MessageStart"
);
}
#[test]
fn test_repeated_message_start_during_thinking_stream() {
let mut session = StreamingSession::new();
session.on_message_start();
let show_prefix = session.on_thinking_delta(0, "Thinking...");
assert!(show_prefix, "First thinking delta should show prefix");
session.on_message_start();
let show_prefix = session.on_thinking_delta(0, " more");
assert!(
!show_prefix,
"Thinking delta after repeated MessageStart should not show prefix"
);
}
#[test]
fn test_message_stop_then_message_start_resets_normally() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "First");
session.on_message_stop();
session.on_message_start();
let show_prefix = session.on_text_delta(0, "Second");
assert!(
show_prefix,
"First delta after MessageStop should show prefix (normal reset)"
);
}
#[test]
fn test_repeated_content_block_start_same_index() {
let mut session = StreamingSession::new();
session.on_message_start();
let show_prefix = session.on_text_delta(0, "Hello");
assert!(show_prefix, "First delta should show prefix");
session.on_content_block_start(0);
let show_prefix = session.on_text_delta(0, " World");
assert!(
!show_prefix,
"Delta after repeated ContentBlockStart should not show prefix"
);
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("Hello World"),
"Accumulated content should be preserved across repeated ContentBlockStart"
);
}
#[test]
fn test_large_delta_warning_respects_verbose_flag() {
let mut session_verbose = StreamingSession::new().with_verbose_warnings(true);
session_verbose.on_message_start();
let large_delta = "x".repeat(snapshot_threshold() + 1);
let _show_prefix = session_verbose.on_text_delta(0, &large_delta);
let mut session_quiet = StreamingSession::new();
session_quiet.on_message_start();
let large_delta = "x".repeat(snapshot_threshold() + 1);
let _show_prefix = session_quiet.on_text_delta(0, &large_delta);
assert_eq!(
session_verbose.get_accumulated(ContentType::Text, "0"),
Some(large_delta.as_str())
);
assert_eq!(
session_quiet.get_accumulated(ContentType::Text, "0"),
Some(large_delta.as_str())
);
}
#[test]
fn test_repeated_message_start_warning_respects_verbose_flag() {
let mut session_verbose = StreamingSession::new().with_verbose_warnings(true);
session_verbose.on_message_start();
session_verbose.on_text_delta(0, "Hello");
session_verbose.on_message_start();
let mut session_quiet = StreamingSession::new();
session_quiet.on_message_start();
session_quiet.on_text_delta(0, "Hello");
session_quiet.on_message_start();
assert_eq!(
session_verbose.get_accumulated(ContentType::Text, "0"),
None,
"Accumulated content should be cleared after repeated MessageStart"
);
assert_eq!(
session_quiet.get_accumulated(ContentType::Text, "0"),
None,
"Accumulated content should be cleared after repeated MessageStart"
);
}
#[test]
fn test_pattern_detection_warning_respects_verbose_flag() {
let mut session_verbose = StreamingSession::new().with_verbose_warnings(true);
session_verbose.on_message_start();
for i in 0..3 {
let large_delta = format!("{}{i}", "x".repeat(snapshot_threshold() + 1));
let _ = session_verbose.on_text_delta(0, &large_delta);
}
let mut session_quiet = StreamingSession::new();
session_quiet.on_message_start();
for i in 0..3 {
let large_delta = format!("{}{i}", "x".repeat(snapshot_threshold() + 1));
let _ = session_quiet.on_text_delta(0, &large_delta);
}
assert_eq!(
session_verbose
.get_streaming_quality_metrics()
.large_delta_count,
3
);
assert_eq!(
session_quiet
.get_streaming_quality_metrics()
.large_delta_count,
3
);
}
#[test]
fn test_content_hash_computed_on_message_stop() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Hello");
session.on_text_delta(0, " World");
session.on_message_stop();
assert!(
session.is_duplicate_by_hash("Hello World", None),
"Hash should be computed after message_stop"
);
}
#[test]
fn test_content_hash_none_when_no_content() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_message_stop();
assert!(
!session.is_duplicate_by_hash("any content", None),
"No hash when no content was streamed"
);
}
#[test]
fn test_is_duplicate_by_hash_returns_true_for_matching_content() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Hello World");
session.on_message_stop();
assert!(session.is_duplicate_by_hash("Hello World", None));
}
#[test]
fn test_is_duplicate_by_hash_returns_false_for_different_content() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Hello World");
session.on_message_stop();
assert!(!session.is_duplicate_by_hash("Different content", None));
}
#[test]
fn test_is_duplicate_by_hash_returns_false_when_no_content_streamed() {
let session = StreamingSession::new();
assert!(!session.is_duplicate_by_hash("Hello World", None));
}
#[test]
fn test_content_hash_multiple_content_blocks() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "First block");
session.on_text_delta(1, "Second block");
session.on_message_stop();
assert!(!session.is_duplicate_by_hash("First block", None));
assert!(!session.is_duplicate_by_hash("Second block", None));
}
#[test]
fn test_is_duplicate_by_hash_returns_true_for_matching_mixed_text_and_tool_use_content() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Let's inspect this ");
session.on_tool_input_delta(1, "{\"path\":\"src\"}");
session.set_tool_name(1, Some("read_file".to_string()));
session.on_message_stop();
let normalized_content = "Let's inspect this TOOL_USE:read_file:{\"path\":\"src\"}";
assert!(session.is_duplicate_by_hash(normalized_content, None));
}
#[test]
fn test_content_hash_consistent_for_same_content() {
let mut session1 = StreamingSession::new();
session1.on_message_start();
session1.on_text_delta(0, "Hello");
session1.on_text_delta(0, " World");
session1.on_message_stop();
let mut session2 = StreamingSession::new();
session2.on_message_start();
session2.on_text_delta(0, "Hello World");
session2.on_message_stop();
assert!(session1.is_duplicate_by_hash("Hello World", None));
assert!(session2.is_duplicate_by_hash("Hello World", None));
}
#[test]
fn test_content_hash_multiple_content_blocks_non_sequential_indices() {
let mut session1 = StreamingSession::new();
session1.on_message_start();
session1.on_text_delta(0, "Block 0");
session1.on_text_delta(1, "Block 1");
session1.on_text_delta(10, "Block 10");
session1.on_text_delta(2, "Block 2");
session1.on_message_stop();
let mut session2 = StreamingSession::new();
session2.on_message_start();
session2.on_text_delta(0, "Block 0");
session2.on_text_delta(1, "Block 1");
session2.on_text_delta(2, "Block 2");
session2.on_text_delta(10, "Block 10");
session2.on_message_stop();
let combined_content = "Block 0Block 1Block 2Block 10";
assert!(
session1.is_duplicate_by_hash(combined_content, None),
"is_duplicate_by_hash should match content in numeric order"
);
assert!(
session2.is_duplicate_by_hash(combined_content, None),
"session2 hash should also match content in numeric order"
);
}
#[test]
fn test_rapid_index_switch_with_clear() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_content_block_start(0);
let show_prefix = session.on_text_delta(0, "X");
assert!(show_prefix, "First delta for index 0 should show prefix");
assert_eq!(session.get_accumulated(ContentType::Text, "0"), Some("X"));
session.on_content_block_start(1);
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("X"),
"Accumulated content for index 0 should be PRESERVED when switching to index 1"
);
session.on_content_block_start(0);
let show_prefix = session.on_text_delta(0, "Y");
assert!(
!show_prefix,
"Prefix should NOT show when switching back to an index that already started output"
);
assert_eq!(
session.get_accumulated(ContentType::Text, "0"),
Some("XY"),
"New content should be APPENDED to preserved accumulated content"
);
}
#[test]
fn test_delta_sizes_preserved_on_index_switch() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_text_delta(0, "Hello");
session.on_text_delta(0, " World");
let metrics = session.get_streaming_quality_metrics();
assert_eq!(metrics.total_deltas, 2, "Two deltas tracked for index 0");
session.on_content_block_start(1);
let metrics = session.get_streaming_quality_metrics();
assert_eq!(
metrics.total_deltas, 2,
"Delta sizes for index 0 preserved when switching to index 1"
);
session.on_text_delta(1, "New");
let metrics = session.get_streaming_quality_metrics();
assert_eq!(
metrics.total_deltas, 3,
"Total deltas includes both index 0 (2) and index 1 (1) entries"
);
}
#[test]
fn test_rapid_index_switch_with_thinking_content() {
let mut session = StreamingSession::new();
session.on_message_start();
session.on_content_block_start(0);
let show_prefix = session.on_thinking_delta(0, "Thinking...");
assert!(show_prefix, "First thinking delta should show prefix");
assert_eq!(
session.get_accumulated(ContentType::Thinking, "0"),
Some("Thinking...")
);
session.on_content_block_start(1);
assert_eq!(
session.get_accumulated(ContentType::Thinking, "0"),
Some("Thinking..."),
"Thinking content for index 0 should be PRESERVED when switching to index 1"
);
let show_prefix = session.on_text_delta(1, "Text");
assert!(
show_prefix,
"First text delta for index 1 should show prefix"
);
session.on_content_block_start(0);
let show_prefix = session.on_thinking_delta(0, " more");
assert!(
!show_prefix,
"Thinking prefix should NOT show when switching back to an index that already started output"
);
assert_eq!(
session.get_accumulated(ContentType::Thinking, "0"),
Some("Thinking... more"),
"Thinking content should be APPENDED to preserved accumulated content"
);
}