/// 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 = 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 = 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 = 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 = 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 = 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 = 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")); }