test(agent): parity test backfill — credential scrub + failure classification (#4468) (#4509)

This commit is contained in:
Steven Enamakel
2026-07-04 12:32:59 -07:00
committed by GitHub
parent c0e24d8856
commit 7e15c0f6e5
4 changed files with 137 additions and 0 deletions
@@ -111,4 +111,53 @@ mod tests {
let output = scrub_credentials(input);
assert!(output.contains("api_key: 1234*[REDACTED]"));
}
// #4453: bare, unlabelled secrets that show up in env dumps / API responses.
#[test]
fn scrubs_bare_aws_access_key() {
let out = scrub_credentials("config dump AKIAIOSFODNN7EXAMPLE trailing text");
assert!(
!out.contains("AKIAIOSFODNN7EXAMPLE"),
"bare AWS access key must be redacted: {out}"
);
assert!(
out.contains("[REDACTED]"),
"redaction marker present: {out}"
);
}
#[test]
fn scrubs_bare_openai_key() {
let out = scrub_credentials("response body sk-abcdefghij1234567890ABCDEFGHIJ end");
assert!(
!out.contains("abcdefghij1234567890ABCDEFGHIJ"),
"openai secret body must be redacted: {out}"
);
assert!(
out.contains("sk-"),
"the sk- scheme is kept for context: {out}"
);
assert!(
out.contains("[REDACTED]"),
"redaction marker present: {out}"
);
}
#[test]
fn scrubs_space_separated_bearer_token() {
let out = scrub_credentials("Authorization: Bearer abcDEF1234567890ghijklmnop done");
assert!(
!out.contains("abcDEF1234567890ghijklmnop"),
"space-separated bearer token must be redacted: {out}"
);
assert!(
out.contains("Bearer"),
"the scheme word is kept for context: {out}"
);
assert!(
out.contains("[REDACTED]"),
"redaction marker present: {out}"
);
}
}
+40
View File
@@ -3140,6 +3140,46 @@ impl Middleware<()> for ImageAwareMessageTrimMiddleware {
#[cfg(test)]
mod tests {
use super::*;
// #4462: image-aware token estimation. A base64 image marker must be priced
// at the flat IMAGE_MARKER_TOKEN_COST, not chars/4 of its payload — otherwise
// one image reads as millions of tokens and the trimmer evicts everything,
// including the system prompt.
#[test]
fn estimate_text_tokens_markerless_is_chars_over_four() {
assert_eq!(estimate_text_tokens(&"a".repeat(40)), (40 + 3) / 4);
assert_eq!(estimate_text_tokens(""), 0);
}
#[test]
fn estimate_text_tokens_prices_image_marker_flat_not_by_length() {
let huge = "x".repeat(40_000);
let text = format!("[IMAGE:{huge}]");
let tokens = estimate_text_tokens(&text);
// chars/4 of the payload would be ~10_000; the flat price is 1_200.
assert!(
tokens >= IMAGE_MARKER_TOKEN_COST,
"at least the flat image cost: {tokens}"
);
assert!(
tokens < 2_000,
"image priced flat, not by base64 length: {tokens}"
);
}
#[test]
fn estimate_text_tokens_charges_each_image_marker_once() {
let tokens = estimate_text_tokens("[IMAGE:aaaa] and [IMAGE:bbbb]");
assert!(
tokens >= 2 * IMAGE_MARKER_TOKEN_COST,
"two images each priced: {tokens}"
);
assert!(
tokens < 2 * IMAGE_MARKER_TOKEN_COST + 100,
"no runaway from the surrounding text: {tokens}"
);
}
use serde_json::json;
use tinyagents::harness::context::{RunConfig, RunContext};
use tinyagents::harness::model::ModelRequest;
+27
View File
@@ -684,3 +684,30 @@ fn record_unobserved_turn_usage_gates_on_observed_tokens() {
assert!(record_unobserved_turn_usage("m", 0, 3, 0, 0.0));
assert!(record_unobserved_turn_usage("m", 10, 3, 2, 0.5));
}
#[test]
fn spawn_and_delegate_tools_are_never_registered_on_subagents() {
// #4452: a child run must never be able to register a spawn/delegate tool,
// even if the resolved allowlist somehow contains one — the registration
// site strips these unconditionally as defense-in-depth.
for name in [
"spawn_subagent",
"spawn_worker_thread",
"use_tinyplace",
"agent_prepare_context",
"delegate_research",
"delegate_",
] {
assert!(
is_subagent_spawn_or_delegate_tool(name),
"{name} must be treated as a spawn/delegate tool"
);
}
// Ordinary tools (and near-miss names) must NOT be stripped.
for name in ["shell", "read_file", "web_search", "spawn", "subagent"] {
assert!(
!is_subagent_spawn_or_delegate_tool(name),
"{name} is a normal tool and must not be stripped"
);
}
}
+21
View File
@@ -388,6 +388,27 @@ mod tests {
);
}
#[test]
fn policy_denied_marker_is_denied_and_not_recoverable() {
use crate::openhuman::security::POLICY_DENIED_MARKER;
let text = format!("{POLICY_DENIED_MARKER} you declined this shell action");
assert_eq!(class_of(&text), ToolFailureClass::Denied);
// UserDeclined family — never eligible for an auto-retry (#4459).
assert!(!classify(&text, false).recoverable);
}
#[test]
fn policy_denied_ttl_expiry_is_approval_expired_not_timeout() {
use crate::openhuman::security::POLICY_DENIED_MARKER;
// A TTL-expiry deny reason literally contains "timed out" — the policy
// marker must win over the timeout sniff so it classifies as an expired
// approval, NOT an execution Timeout that promises an auto-retry (#4459).
let text = format!("{POLICY_DENIED_MARKER} Approval for 'shell' timed out after 600s");
assert_eq!(class_of(&text), ToolFailureClass::ApprovalExpired);
assert_ne!(class_of(&text), ToolFailureClass::Timeout);
assert!(!classify(&text, false).recoverable);
}
#[test]
fn numeric_status_codes_need_word_boundaries() {
// `403`/`503` embedded in a longer digit run must NOT trip the code