Add regression tests for Anthropic beta headers and Gemini thought_signature

Covers two recent runtime failures that lacked test coverage:
- Anthropic: assert deprecated beta header values are not sent
- Gemini: test function call parsing/translation with and without thoughtSignature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-02-23 10:22:14 -05:00
parent 94326f5438
commit e992b57ced
2 changed files with 103 additions and 0 deletions

View file

@ -1881,6 +1881,33 @@ mod tests {
assert_eq!(result["source"]["media_type"], "application/pdf");
}
/// Regression test: deprecated beta header values must not be sent.
/// The Anthropic API rejects requests containing these old headers.
#[test]
fn beta_header_rejects_deprecated_values() {
let deprecated = [
"extended-thinking-2025-04-14",
"max-tokens-3-5-sonnet-2025-04-14",
];
// No user headers — only cache header should appear
let header = build_beta_header(None, true).unwrap_or_default();
for dep in &deprecated {
assert!(!header.contains(dep), "default header must not contain deprecated value {dep}");
}
// With a valid user header
let opts = serde_json::json!({
"anthropic": {
"beta_headers": ["interleaved-thinking-2025-05-14"]
}
});
let header = build_beta_header(Some(&opts), true).unwrap_or_default();
for dep in &deprecated {
assert!(!header.contains(dep), "header with user values must not contain deprecated value {dep}");
}
}
#[test]
fn audio_produces_text_fallback() {
let part = ContentPart::Audio(crate::types::AudioData {

View file

@ -1259,6 +1259,82 @@ mod tests {
}
}
#[test]
fn parse_part_function_call() {
let part = serde_json::json!({
"functionCall": {
"name": "get_weather",
"args": {"location": "NYC"}
}
});
let result = parse_part(&part).expect("should parse function call");
match result {
ContentPart::ToolCall(tc) => {
assert_eq!(tc.name, "get_weather");
assert_eq!(tc.arguments, serde_json::json!({"location": "NYC"}));
assert!(tc.provider_metadata.is_none());
}
other => panic!("expected ToolCall, got {other:?}"),
}
}
#[test]
fn parse_part_function_call_with_thought_signature() {
let part = serde_json::json!({
"functionCall": {
"name": "get_weather",
"args": {"location": "NYC"}
},
"thoughtSignature": "abc123sig"
});
let result = parse_part(&part).expect("should parse function call with thought signature");
match result {
ContentPart::ToolCall(tc) => {
assert_eq!(tc.name, "get_weather");
let meta = tc.provider_metadata.expect("provider_metadata should be set");
assert_eq!(meta["thoughtSignature"], "abc123sig");
}
other => panic!("expected ToolCall, got {other:?}"),
}
}
#[test]
fn translate_messages_function_call_includes_thought_signature() {
let mut tc = ToolCall::new("call-1", "get_weather", serde_json::json!({"location": "NYC"}));
tc.provider_metadata = Some(serde_json::json!({"thoughtSignature": "sig456"}));
let msg = Message {
role: Role::Assistant,
content: vec![ContentPart::ToolCall(tc)],
name: None,
tool_call_id: None,
};
let contents = translate_messages(&[&msg]);
assert_eq!(contents.len(), 1);
let part = &contents[0].parts[0];
assert!(part.get("functionCall").is_some());
assert_eq!(part["thoughtSignature"], "sig456");
}
#[test]
fn translate_messages_function_call_without_thought_signature() {
let tc = ToolCall::new("call-1", "get_weather", serde_json::json!({"location": "NYC"}));
let msg = Message {
role: Role::Assistant,
content: vec![ContentPart::ToolCall(tc)],
name: None,
tool_call_id: None,
};
let contents = translate_messages(&[&msg]);
assert_eq!(contents.len(), 1);
let part = &contents[0].parts[0];
assert!(part.get("functionCall").is_some());
assert!(part.get("thoughtSignature").is_none());
}
#[test]
fn parse_part_thought_false_is_regular_text() {
let part = serde_json::json!({"text": "Regular text", "thought": false});