Skip to content

Commit d2c947f

Browse files
beastoinclaude
andcommitted
Add edge case tests for proxy thinking budget injection
Tests for: dual generation_config casings, null generation_config, string generation_config. All malformed cases get a fresh generationConfig with default thinking budget. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2fef6c6 commit d2c947f

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

desktop/Backend-Rust/src/routes/proxy.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,4 +1671,59 @@ mod tests {
16711671
let tc = gc.get("thinkingConfig").expect("thinkingConfig should be injected");
16721672
assert_eq!(tc["thinkingBudget"], DEFAULT_THINKING_BUDGET);
16731673
}
1674+
1675+
#[test]
1676+
fn sanitize_dual_generation_config_both_get_thinking() {
1677+
// Attacker sends both casings — both should get thinking budget injected.
1678+
let body = serde_json::json!({
1679+
"contents": [{"parts": [{"text": "hello"}]}],
1680+
"generation_config": {"max_output_tokens": 100},
1681+
"generationConfig": {"maxOutputTokens": 200}
1682+
});
1683+
let result = sanitize_gemini_body(
1684+
serde_json::to_vec(&body).unwrap().as_slice(),
1685+
"generateContent",
1686+
).unwrap();
1687+
let parsed: serde_json::Value = serde_json::from_slice(&result).unwrap();
1688+
// Both casings should have thinkingConfig injected
1689+
let gc_snake = parsed.get("generation_config").unwrap().as_object().unwrap();
1690+
assert!(gc_snake.contains_key("thinkingConfig"));
1691+
let gc_camel = parsed.get("generationConfig").unwrap().as_object().unwrap();
1692+
assert!(gc_camel.contains_key("thinkingConfig"));
1693+
}
1694+
1695+
#[test]
1696+
fn sanitize_null_generation_config_gets_new_one() {
1697+
// Malformed: generation_config is null — proxy should create a fresh one.
1698+
let body = serde_json::json!({
1699+
"contents": [{"parts": [{"text": "hello"}]}],
1700+
"generation_config": null
1701+
});
1702+
let result = sanitize_gemini_body(
1703+
serde_json::to_vec(&body).unwrap().as_slice(),
1704+
"generateContent",
1705+
).unwrap();
1706+
let parsed: serde_json::Value = serde_json::from_slice(&result).unwrap();
1707+
// null generation_config is not an object, so proxy creates generationConfig
1708+
let gc = parsed.get("generationConfig").expect("generationConfig should be created");
1709+
let tc = gc.get("thinkingConfig").expect("thinkingConfig should be injected");
1710+
assert_eq!(tc["thinkingBudget"], DEFAULT_THINKING_BUDGET);
1711+
}
1712+
1713+
#[test]
1714+
fn sanitize_string_generation_config_gets_new_one() {
1715+
// Malformed: generation_config is a string — proxy should create a fresh one.
1716+
let body = serde_json::json!({
1717+
"contents": [{"parts": [{"text": "hello"}]}],
1718+
"generation_config": "invalid"
1719+
});
1720+
let result = sanitize_gemini_body(
1721+
serde_json::to_vec(&body).unwrap().as_slice(),
1722+
"generateContent",
1723+
).unwrap();
1724+
let parsed: serde_json::Value = serde_json::from_slice(&result).unwrap();
1725+
let gc = parsed.get("generationConfig").expect("generationConfig should be created");
1726+
let tc = gc.get("thinkingConfig").expect("thinkingConfig should be injected");
1727+
assert_eq!(tc["thinkingBudget"], DEFAULT_THINKING_BUDGET);
1728+
}
16741729
}

0 commit comments

Comments
 (0)