#![cfg_attr(test, allow(clippy::expect_used, clippy::unwrap_used))]
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use capo_agent::{test_support::ScriptedLlm, AppBuilder, Attachment, Config, UserMessage};
use futures::StreamExt;
use motosan_agent_loop::{ContentPart, Message};
fn png_fixture() -> PathBuf {
let mut path = std::env::temp_dir();
path.push(format!("capo-v06-e2e-print-{}.png", std::process::id()));
let mut bytes = vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
bytes.extend_from_slice(&[0u8; 64]);
std::fs::File::create(&path)
.expect("create fixture")
.write_all(&bytes)
.expect("write fixture");
path
}
#[tokio::test]
async fn print_mode_with_image_forwards_multipart_message_to_llm() {
let path = png_fixture();
let dir = tempfile::tempdir().expect("tempdir");
let llm = Arc::new(ScriptedLlm::builder().then_message("acknowledged").build());
let mut cfg = Config::default();
cfg.anthropic.api_key = Some("sk-unused".into());
let app = AppBuilder::new()
.with_config(cfg)
.with_cwd(dir.path())
.with_builtin_tools()
.with_llm(llm.clone())
.build()
.await
.expect("build app");
let msg = UserMessage {
text: "describe".into(),
attachments: vec![Attachment::Image { path }],
};
let _events: Vec<_> = app.send_user_message(msg).collect().await;
let captured = llm.captured_inputs();
assert!(!captured.is_empty(), "ScriptedLlm.chat never called");
let last_call = captured.last().expect("captured input");
let user_msg = last_call
.iter()
.rev()
.find(|m| matches!(m, Message::User { .. }))
.expect("user message in chat input");
let parts = match user_msg {
Message::User { content, .. } => content.as_slice(),
_ => unreachable!(),
};
assert_eq!(parts.len(), 2, "expected [text, image]; got: {parts:?}");
assert!(matches!(&parts[0], ContentPart::Text { text, .. } if text == "describe"));
assert!(matches!(&parts[1], ContentPart::Image { .. }));
}