use open_agent::{
AgentOptions, Client, ContentBlock, ImageBlock, ImageDetail, Message, MessageRole, TextBlock,
};
#[test]
fn test_malicious_mime_injection_rejected() {
let malicious_mimes = vec![
"image/png;charset=utf-8", "image/png\nmalicious", "image/png,extra", "image/png\r\nX-Custom: bad", ];
for mime in &malicious_mimes {
let result = ImageBlock::from_base64("AAAA", mime);
assert!(
result.is_err(),
"Should reject malicious MIME type: {:?}",
mime
);
let err = result.unwrap_err();
assert!(
err.to_string().contains("MIME") || err.to_string().contains("character"),
"Error should mention MIME or character issue, got: {}",
err
);
}
}
#[test]
fn test_extremely_large_base64_handled() {
let huge_base64 = "A".repeat(15_000_000);
let result = ImageBlock::from_base64(&huge_base64, "image/png");
assert!(result.is_ok(), "Should accept large base64 (with warning)");
let block = result.unwrap();
assert!(block.url().starts_with("data:image/png;base64,"));
assert!(block.url().len() > 15_000_000);
}
#[test]
fn test_control_characters_in_urls_rejected() {
let malicious_urls = vec![
"https://example.com\n/path", "https://example.com\t/path", "https://example.com\0/path", "https://example.com\r/path", "https://example.com\x1B/path", ];
for url in &malicious_urls {
let result = ImageBlock::from_url(*url);
assert!(
result.is_err(),
"Should reject URL with control char: {:?}",
url
);
let err = result.unwrap_err();
assert!(
err.to_string().contains("control") || err.to_string().contains("character"),
"Error should mention control characters, got: {}",
err
);
}
}
#[tokio::test]
async fn test_empty_text_blocks_warned_but_accepted() {
let messages = vec![
Message::new(
MessageRole::User,
vec![
ContentBlock::Text(TextBlock::new("")), ContentBlock::Image(ImageBlock::from_url("https://example.com/img.jpg").unwrap()),
],
),
Message::new(
MessageRole::User,
vec![
ContentBlock::Text(TextBlock::new(" ")), ContentBlock::Image(ImageBlock::from_url("https://example.com/img2.jpg").unwrap()),
],
),
];
let options = AgentOptions::builder()
.model("test-model")
.base_url("http://localhost:1234/v1")
.build()
.unwrap();
let mut client = Client::new(options).unwrap();
for msg in messages {
client.history_mut().push(msg);
}
assert_eq!(client.history().len(), 2);
for msg in client.history() {
assert_eq!(msg.content.len(), 2);
}
}
#[test]
fn test_all_validation_errors_are_descriptive() {
let err = ImageBlock::from_url("").unwrap_err();
assert!(err.to_string().contains("empty"));
let err = ImageBlock::from_url("https://example.com\n/path").unwrap_err();
assert!(err.to_string().contains("control") || err.to_string().contains("character"));
let err = ImageBlock::from_url("javascript:alert(1)").unwrap_err();
assert!(err.to_string().contains("http") || err.to_string().contains("scheme"));
let err = ImageBlock::from_base64("", "image/png").unwrap_err();
assert!(err.to_string().contains("empty"));
let err = ImageBlock::from_base64("hello world", "image/png").unwrap_err();
assert!(err.to_string().contains("base64") || err.to_string().contains("character"));
let err = ImageBlock::from_base64("ABC", "image/png").unwrap_err();
assert!(err.to_string().contains("length") || err.to_string().contains("multiple"));
let err = ImageBlock::from_base64("AAAA", "image/png;charset=utf-8").unwrap_err();
assert!(err.to_string().contains("MIME") || err.to_string().contains("character"));
}
#[test]
fn test_valid_edge_cases_still_work() {
let result = ImageBlock::from_base64("AAAA", "image/png");
assert!(result.is_ok());
let long_url = format!("https://example.com/{}", "a".repeat(1900));
let result = ImageBlock::from_url(&long_url);
assert!(result.is_ok());
let data_uri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
let result = ImageBlock::from_url(data_uri);
assert!(result.is_ok());
for mime in &[
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/avif",
] {
let result = ImageBlock::from_base64("AAAA", *mime);
assert!(result.is_ok(), "Should accept {}", mime);
}
}
#[test]
fn test_backward_compatibility_maintained() {
let result = ImageBlock::from_url("http://example.com/image.jpg");
assert!(result.is_ok());
let result = ImageBlock::from_url("https://example.com/image.jpg");
assert!(result.is_ok());
let result = ImageBlock::from_url("data:image/png;base64,AAAA");
assert!(result.is_ok());
let result = ImageBlock::from_base64(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"image/png",
);
assert!(result.is_ok());
let img = ImageBlock::from_url("https://example.com/img.jpg")
.unwrap()
.with_detail(ImageDetail::Low);
assert_eq!(img.detail(), ImageDetail::Low);
let img = img.with_detail(ImageDetail::High);
assert_eq!(img.detail(), ImageDetail::High);
let img = img.with_detail(ImageDetail::Auto);
assert_eq!(img.detail(), ImageDetail::Auto);
}
#[test]
fn test_data_uri_base64_validation_integration() {
let result = ImageBlock::from_url("data:image/png;base64,hello world");
assert!(result.is_err());
let result = ImageBlock::from_url("data:image/png;base64,@@@");
assert!(result.is_err());
let result = ImageBlock::from_url("data:image/png;base64,ABC");
assert!(result.is_err());
let result = ImageBlock::from_url("data:image/png;base64,AAAA");
assert!(result.is_ok());
}