379 lines
12 KiB
Rust
379 lines
12 KiB
Rust
/// Comprehensive edge case tests for ContentBlock
|
|
use dirigent_protocol::types::ContentBlock;
|
|
|
|
/// Test empty string in Text variant
|
|
#[test]
|
|
fn test_text_empty_string() {
|
|
let block = ContentBlock::Text {
|
|
text: String::new(),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
assert!(json.contains(r#""type":"text"#));
|
|
assert!(json.contains(r#""text":"""#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test very long string (>10KB) in Text variant
|
|
#[test]
|
|
fn test_text_very_long_string() {
|
|
let long_text = "a".repeat(10_000);
|
|
let block = ContentBlock::Text {
|
|
text: long_text.clone(),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
|
|
match deserialized {
|
|
ContentBlock::Text { text } => {
|
|
assert_eq!(text.len(), 10_000);
|
|
assert_eq!(text, long_text);
|
|
}
|
|
_ => panic!("Expected Text variant"),
|
|
}
|
|
}
|
|
|
|
/// Test special characters (unicode, emojis, newlines) in Text
|
|
#[test]
|
|
fn test_text_special_characters() {
|
|
let special_text = "Hello 👋\nWorld 🌍\t中文\r\n\"quotes\" and 'apostrophes' \\ backslash";
|
|
let block = ContentBlock::Text {
|
|
text: special_text.to_string(),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
|
|
match deserialized {
|
|
ContentBlock::Text { text } => {
|
|
assert_eq!(text, special_text);
|
|
}
|
|
_ => panic!("Expected Text variant"),
|
|
}
|
|
}
|
|
|
|
/// Test JSON control characters in Text
|
|
#[test]
|
|
fn test_text_json_control_characters() {
|
|
let control_chars = "Line1\nLine2\r\nLine3\tTabbed\x08Backspace";
|
|
let block = ContentBlock::Text {
|
|
text: control_chars.to_string(),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
|
|
match deserialized {
|
|
ContentBlock::Text { text } => {
|
|
assert_eq!(text, control_chars);
|
|
}
|
|
_ => panic!("Expected Text variant"),
|
|
}
|
|
}
|
|
|
|
/// Test ResourceLink with empty URI
|
|
#[test]
|
|
fn test_resource_link_empty_uri() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: String::new(),
|
|
name: None,
|
|
mime_type: None,
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
assert!(json.contains(r#""type":"resource_link"#));
|
|
assert!(json.contains(r#""uri":"""#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test ResourceLink with all optional fields as None
|
|
#[test]
|
|
fn test_resource_link_all_none() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: "file:///test.txt".to_string(),
|
|
name: None,
|
|
mime_type: None,
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
|
|
// Verify optional fields are NOT in JSON
|
|
assert!(!json.contains(r#""name""#));
|
|
assert!(!json.contains(r#""mime_type""#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test ResourceLink with all optional fields as Some
|
|
#[test]
|
|
fn test_resource_link_all_some() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: "https://example.com/file.pdf".to_string(),
|
|
name: Some("document.pdf".to_string()),
|
|
mime_type: Some("application/pdf".to_string()),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
|
|
// Verify all fields ARE in JSON
|
|
assert!(json.contains(r#""uri":"https://example.com/file.pdf"#));
|
|
assert!(json.contains(r#""name":"document.pdf"#));
|
|
assert!(json.contains(r#""mime_type":"application/pdf"#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test ResourceLink with only name
|
|
#[test]
|
|
fn test_resource_link_only_name() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: "file:///path/to/file".to_string(),
|
|
name: Some("my_file".to_string()),
|
|
mime_type: None,
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
|
|
assert!(json.contains(r#""name":"my_file"#));
|
|
assert!(!json.contains(r#""mime_type""#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test ResourceLink with only mime_type
|
|
#[test]
|
|
fn test_resource_link_only_mime_type() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: "file:///path/to/file".to_string(),
|
|
name: None,
|
|
mime_type: Some("text/plain".to_string()),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
|
|
assert!(!json.contains(r#""name""#));
|
|
assert!(json.contains(r#""mime_type":"text/plain"#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test ResourceLink with empty optional strings
|
|
#[test]
|
|
fn test_resource_link_empty_optional_strings() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: "file:///test".to_string(),
|
|
name: Some(String::new()),
|
|
mime_type: Some(String::new()),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
|
|
// Empty strings should still serialize
|
|
assert!(json.contains(r#""name":"""#));
|
|
assert!(json.contains(r#""mime_type":"""#));
|
|
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test ResourceLink with special characters in URI
|
|
#[test]
|
|
fn test_resource_link_special_uri() {
|
|
let uri = "file:///path/to/file%20with%20spaces.txt?query=param&other=value#fragment";
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: uri.to_string(),
|
|
name: Some("file with spaces.txt".to_string()),
|
|
mime_type: Some("text/plain; charset=utf-8".to_string()),
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
|
|
match deserialized {
|
|
ContentBlock::ResourceLink {
|
|
uri: deser_uri,
|
|
name,
|
|
mime_type,
|
|
} => {
|
|
assert_eq!(deser_uri, uri);
|
|
assert_eq!(name, Some("file with spaces.txt".to_string()));
|
|
assert_eq!(mime_type, Some("text/plain; charset=utf-8".to_string()));
|
|
}
|
|
_ => panic!("Expected ResourceLink variant"),
|
|
}
|
|
}
|
|
|
|
/// Test ResourceLink with very long URI
|
|
#[test]
|
|
fn test_resource_link_long_uri() {
|
|
let long_path = format!("file:///{}", "a/".repeat(500));
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: long_path.clone(),
|
|
name: Some("file.txt".to_string()),
|
|
mime_type: None,
|
|
};
|
|
let json = serde_json::to_string(&block).unwrap();
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
|
|
match deserialized {
|
|
ContentBlock::ResourceLink { uri, .. } => {
|
|
assert_eq!(uri, long_path);
|
|
}
|
|
_ => panic!("Expected ResourceLink variant"),
|
|
}
|
|
}
|
|
|
|
/// Test deserialization from JSON without type field (should fail)
|
|
#[test]
|
|
fn test_deserialization_missing_type_field() {
|
|
let json = r#"{"text": "Hello"}"#;
|
|
let result: Result<ContentBlock, _> = serde_json::from_str(json);
|
|
assert!(result.is_err(), "Should fail without type field");
|
|
}
|
|
|
|
/// Test deserialization with invalid type value
|
|
#[test]
|
|
fn test_deserialization_invalid_type() {
|
|
let json = r#"{"type": "invalid_type", "text": "Hello"}"#;
|
|
let result: Result<ContentBlock, _> = serde_json::from_str(json);
|
|
assert!(result.is_err(), "Should fail with invalid type");
|
|
}
|
|
|
|
/// Test deserialization Text without text field (should fail)
|
|
#[test]
|
|
fn test_deserialization_text_missing_field() {
|
|
let json = r#"{"type": "text"}"#;
|
|
let result: Result<ContentBlock, _> = serde_json::from_str(json);
|
|
assert!(result.is_err(), "Should fail without text field");
|
|
}
|
|
|
|
/// Test deserialization ResourceLink without uri field (should fail)
|
|
#[test]
|
|
fn test_deserialization_resource_link_missing_uri() {
|
|
let json = r#"{"type": "resource_link", "name": "file.txt"}"#;
|
|
let result: Result<ContentBlock, _> = serde_json::from_str(json);
|
|
assert!(result.is_err(), "Should fail without uri field");
|
|
}
|
|
|
|
/// Test that type tag is snake_case
|
|
#[test]
|
|
fn test_type_tag_format() {
|
|
let text = ContentBlock::Text {
|
|
text: "test".to_string(),
|
|
};
|
|
let json = serde_json::to_string(&text).unwrap();
|
|
assert!(json.contains(r#""type":"text"#));
|
|
assert!(!json.contains(r#""type":"Text"#));
|
|
|
|
let resource = ContentBlock::ResourceLink {
|
|
uri: "file:///test".to_string(),
|
|
name: None,
|
|
mime_type: None,
|
|
};
|
|
let json = serde_json::to_string(&resource).unwrap();
|
|
assert!(json.contains(r#""type":"resource_link"#));
|
|
assert!(!json.contains(r#""type":"ResourceLink"#));
|
|
}
|
|
|
|
/// Test roundtrip with all ContentBlock variants
|
|
#[test]
|
|
fn test_all_variants_roundtrip() {
|
|
let variants = vec![
|
|
ContentBlock::Text {
|
|
text: "Test text".to_string(),
|
|
},
|
|
ContentBlock::ResourceLink {
|
|
uri: "file:///test.txt".to_string(),
|
|
name: Some("test.txt".to_string()),
|
|
mime_type: Some("text/plain".to_string()),
|
|
},
|
|
ContentBlock::ResourceLink {
|
|
uri: "https://example.com/resource".to_string(),
|
|
name: None,
|
|
mime_type: None,
|
|
},
|
|
];
|
|
|
|
for variant in variants {
|
|
let json = serde_json::to_string(&variant).unwrap();
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(variant, deserialized);
|
|
}
|
|
}
|
|
|
|
/// Test pretty-printed JSON
|
|
#[test]
|
|
fn test_pretty_json() {
|
|
let block = ContentBlock::ResourceLink {
|
|
uri: "file:///test.txt".to_string(),
|
|
name: Some("test.txt".to_string()),
|
|
mime_type: Some("text/plain".to_string()),
|
|
};
|
|
let json = serde_json::to_string_pretty(&block).unwrap();
|
|
|
|
// Should be parseable
|
|
let deserialized: ContentBlock = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(block, deserialized);
|
|
}
|
|
|
|
/// Test null values in JSON (should fail for required fields)
|
|
#[test]
|
|
fn test_null_values() {
|
|
let json = r#"{"type": "text", "text": null}"#;
|
|
let result: Result<ContentBlock, _> = serde_json::from_str(json);
|
|
assert!(result.is_err(), "Should fail with null text");
|
|
|
|
let json = r#"{"type": "resource_link", "uri": null}"#;
|
|
let result: Result<ContentBlock, _> = serde_json::from_str(json);
|
|
assert!(result.is_err(), "Should fail with null uri");
|
|
}
|
|
|
|
/// Test null values for optional fields (should deserialize as None)
|
|
#[test]
|
|
fn test_null_optional_fields() {
|
|
let json = r#"{
|
|
"type": "resource_link",
|
|
"uri": "file:///test",
|
|
"name": null,
|
|
"mime_type": null
|
|
}"#;
|
|
let result: ContentBlock = serde_json::from_str(json).unwrap();
|
|
|
|
match result {
|
|
ContentBlock::ResourceLink {
|
|
uri,
|
|
name,
|
|
mime_type,
|
|
} => {
|
|
assert_eq!(uri, "file:///test");
|
|
assert!(name.is_none());
|
|
assert!(mime_type.is_none());
|
|
}
|
|
_ => panic!("Expected ResourceLink"),
|
|
}
|
|
}
|
|
|
|
/// Test ContentBlock Clone and PartialEq
|
|
#[test]
|
|
fn test_clone_and_equality() {
|
|
let original = ContentBlock::Text {
|
|
text: "Test".to_string(),
|
|
};
|
|
let cloned = original.clone();
|
|
assert_eq!(original, cloned);
|
|
|
|
let different = ContentBlock::Text {
|
|
text: "Different".to_string(),
|
|
};
|
|
assert_ne!(original, different);
|
|
}
|
|
|
|
/// Test ContentBlock Debug formatting
|
|
#[test]
|
|
fn test_debug_formatting() {
|
|
let block = ContentBlock::Text {
|
|
text: "Debug test".to_string(),
|
|
};
|
|
let debug_str = format!("{:?}", block);
|
|
assert!(debug_str.contains("Text"));
|
|
assert!(debug_str.contains("Debug test"));
|
|
}
|