use super::handshake::{Version, negotiated};
use super::queue::Prompts;
use super::stream::project;
use serde_json::json;
#[test]
fn accepts_one_and_two_and_refuses_three_by_name() -> anyhow::Result<()> {
assert_eq!(negotiated(1)?, Version::One);
assert_eq!(negotiated(2)?, Version::Two);
let error = negotiated(3)
.err()
.ok_or_else(|| anyhow::anyhow!("3 was admitted"))?;
assert_eq!(
error.to_string(),
"ACP protocol version 3 refused: offered 2, supported 2 and 1"
);
Ok(())
}
#[test]
fn thirty_third_unread_prompt_is_refused_without_displacing_mail() -> anyhow::Result<()> {
let mut queue = Prompts::new(32);
for n in 0..32 {
queue.push(n.to_string())?;
}
let error = queue
.push("overflow".to_owned())
.err()
.ok_or_else(|| anyhow::anyhow!("overflow admitted"))?;
assert_eq!(
error.to_string(),
"prompt_queue 32 reached: 32 unread prompts"
);
for n in 0..32 {
assert_eq!(queue.pop().as_deref(), Some(n.to_string().as_str()));
}
assert!(queue.pop().is_none());
Ok(())
}
#[test]
fn stream_whitelists_title_and_status_and_never_copies_tool_bytes() -> anyhow::Result<()> {
let item = project(Version::One, json!({
"sessionId":"s", "update": {"sessionUpdate":"tool_call", "toolCallId":"t", "title":"Review", "status":"completed",
"rawInput":{"secret":"input"}, "rawOutput":{"secret":"output"},
"content":[{"type":"content","content":{"type":"text","text":"file bytes"}}]}
}), "s", 128)?.ok_or_else(|| anyhow::anyhow!("tool title missing"))?;
assert_eq!(item["title"], "Review");
assert_eq!(item["status"], "completed");
let encoded = serde_json::to_string(&item)?;
for forbidden in ["rawInput", "rawOutput", "file bytes", "secret"] {
assert!(!encoded.contains(forbidden));
}
Ok(())
}
#[test]
fn text_is_cut_at_a_character_boundary_and_marked() -> anyhow::Result<()> {
let item = project(
Version::One,
json!({"sessionId":"s", "update":{
"sessionUpdate":"agent_message_chunk", "content":{"type":"text","text":"é".repeat(128)}
}}),
"s",
128,
)?
.ok_or_else(|| anyhow::anyhow!("chunk missing"))?;
let text = item["text"]
.as_str()
.ok_or_else(|| anyhow::anyhow!("text missing"))?;
assert!(text.chars().all(|ch| ch == 'é'));
assert!(serde_json::to_vec(&item)?.len() <= 128);
assert_eq!(item["truncated"], true);
assert!(
project(
Version::One,
json!({"sessionId":"foreign","update":{
"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"private"}
}}),
"s",
64
)
.is_err()
);
Ok(())
}
#[test]
fn v2_chunks_keep_the_required_message_identity() -> anyhow::Result<()> {
let item = project(Version::Two, json!({"sessionId":"s","update":{
"sessionUpdate":"agent_message_chunk","messageId":"message-1","content":{"type":"text","text":"response"}
}}), "s", 256)?.ok_or_else(|| anyhow::anyhow!("v2 chunk missing"))?;
assert_eq!(item["message_id"], "message-1");
assert_eq!(item["text"], "response");
Ok(())
}