use secure_exec_sidecar::limits::{vm_limits_from_config, VmLimits};
use secure_exec_vm_config::{
HttpLimitsConfig, JsRuntimeLimitsConfig, PythonLimitsConfig, ResourceLimitsConfig,
ToolLimitsConfig, VmLimitsConfig, WasmLimitsConfig,
};
use serde_json::json;
const SIDECAR_FRAME_CAP: usize = 1024 * 1024;
#[test]
fn defaults_match_struct_default() {
let parsed =
vm_limits_from_config(None, SIDECAR_FRAME_CAP).expect("empty config parses to defaults");
assert_eq!(parsed, VmLimits::default());
assert_eq!(
parsed.js_runtime.v8_heap_limit_mb,
Some(128),
"JavaScript heap must be bounded by default"
);
assert_eq!(
parsed.resources.max_wasm_memory_bytes,
Some(128 * 1024 * 1024),
"WASM memory must be bounded by default"
);
}
#[test]
fn overrides_only_present_keys() {
let config = VmLimitsConfig {
tools: Some(ToolLimitsConfig {
max_tool_schema_bytes: Some(4096),
..Default::default()
}),
wasm: Some(WasmLimitsConfig {
max_module_file_bytes: Some(1_048_576),
..Default::default()
}),
js_runtime: Some(JsRuntimeLimitsConfig {
v8_heap_limit_mb: Some(256),
..Default::default()
}),
python: Some(PythonLimitsConfig {
execution_timeout_ms: Some(1000),
..Default::default()
}),
http: Some(HttpLimitsConfig {
max_fetch_response_bytes: Some(65_536),
}),
..Default::default()
};
let parsed = vm_limits_from_config(Some(&config), SIDECAR_FRAME_CAP).expect("valid overrides");
assert_eq!(parsed.tools.max_tool_schema_bytes, 4096);
assert_eq!(parsed.wasm.max_module_file_bytes, 1_048_576);
assert_eq!(parsed.js_runtime.v8_heap_limit_mb, Some(256));
assert_eq!(parsed.python.execution_timeout_ms, 1000);
assert_eq!(parsed.http.max_fetch_response_bytes, 65536);
let defaults = VmLimits::default();
assert_eq!(
parsed.tools.max_registered_toolkits,
defaults.tools.max_registered_toolkits
);
assert_eq!(
parsed.wasm.sync_read_limit_bytes,
defaults.wasm.sync_read_limit_bytes
);
}
#[test]
fn resources_subset_threads_through() {
let config = VmLimitsConfig {
resources: Some(ResourceLimitsConfig {
max_processes: Some(8),
..Default::default()
}),
..Default::default()
};
let parsed =
vm_limits_from_config(Some(&config), SIDECAR_FRAME_CAP).expect("resources thread through");
assert_eq!(parsed.resources.max_processes, Some(8));
}
#[test]
fn rejects_unparseable_value() {
let error = serde_json::from_value::<VmLimitsConfig>(json!({
"tools": { "maxToolSchemaBytes": "not-a-number" }
}))
.expect_err("unparseable value rejected");
assert!(error.to_string().contains("invalid type"));
}
#[test]
fn rejects_fetch_body_exceeding_frame_cap() {
let config = VmLimitsConfig {
http: Some(HttpLimitsConfig {
max_fetch_response_bytes: Some((SIDECAR_FRAME_CAP + 1) as u64),
}),
..Default::default()
};
let error = vm_limits_from_config(Some(&config), SIDECAR_FRAME_CAP)
.expect_err("oversized fetch body rejected");
assert!(error.to_string().contains("wire frame cap"));
}
#[test]
fn rejects_default_timeout_above_max() {
let config = VmLimitsConfig {
tools: Some(ToolLimitsConfig {
default_tool_timeout_ms: Some(60_000),
max_tool_timeout_ms: Some(30_000),
..Default::default()
}),
..Default::default()
};
let error =
vm_limits_from_config(Some(&config), SIDECAR_FRAME_CAP).expect_err("default above max");
assert!(error.to_string().contains("max_tool_timeout_ms"));
}
#[test]
fn rejects_zero_buffer_cap() {
let config = VmLimitsConfig {
js_runtime: Some(JsRuntimeLimitsConfig {
captured_output_limit_bytes: Some(0),
..Default::default()
}),
..Default::default()
};
let error =
vm_limits_from_config(Some(&config), SIDECAR_FRAME_CAP).expect_err("zero buffer cap");
assert!(error.to_string().contains("captured_output_limit_bytes"));
}