1use serde::{Deserialize, Serialize};
8
9#[derive(
14 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
15)]
16#[serde(rename_all = "snake_case")]
17pub enum SandboxTier {
18 #[default]
20 None,
21 Basic,
23 Process,
25 Container,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
31pub struct SandboxLimits {
32 pub max_runtime_secs: u64,
34 pub max_output_bytes: usize,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub max_memory_mb: Option<u64>,
39}
40
41impl Default for SandboxLimits {
42 fn default() -> Self {
43 Self {
44 max_runtime_secs: 30,
45 max_output_bytes: 64 * 1024,
46 max_memory_mb: None,
47 }
48 }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
53#[serde(tag = "policy", rename_all = "snake_case")]
54pub enum NetworkPolicy {
55 #[default]
57 Disabled,
58 AllowAll,
60 AllowList {
62 #[serde(default)]
63 hosts: Vec<String>,
64 },
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
74 fn tier_ordering() {
75 assert!(SandboxTier::None < SandboxTier::Basic);
76 assert!(SandboxTier::Basic < SandboxTier::Process);
77 assert!(SandboxTier::Process < SandboxTier::Container);
78 }
79
80 #[test]
81 fn tier_default_is_none() {
82 assert_eq!(SandboxTier::default(), SandboxTier::None);
83 }
84
85 #[test]
86 fn tier_serde_roundtrip() {
87 for tier in [
88 SandboxTier::None,
89 SandboxTier::Basic,
90 SandboxTier::Process,
91 SandboxTier::Container,
92 ] {
93 let json = serde_json::to_string(&tier).unwrap();
94 let back: SandboxTier = serde_json::from_str(&json).unwrap();
95 assert_eq!(back, tier);
96 }
97 assert_eq!(
98 serde_json::to_string(&SandboxTier::None).unwrap(),
99 "\"none\""
100 );
101 assert_eq!(
102 serde_json::to_string(&SandboxTier::Container).unwrap(),
103 "\"container\""
104 );
105 }
106
107 #[test]
108 fn tier_ge_comparison_for_policy() {
109 let required = SandboxTier::Process;
110 assert!(SandboxTier::Process >= required);
111 assert!(SandboxTier::Container >= required);
112 assert!(SandboxTier::Basic < required);
113 assert!(SandboxTier::None < required);
114 }
115
116 #[test]
119 fn limits_default() {
120 let limits = SandboxLimits::default();
121 assert_eq!(limits.max_runtime_secs, 30);
122 assert_eq!(limits.max_output_bytes, 64 * 1024);
123 assert!(limits.max_memory_mb.is_none());
124 }
125
126 #[test]
127 fn limits_serde_roundtrip() {
128 let limits = SandboxLimits {
129 max_runtime_secs: 60,
130 max_output_bytes: 128 * 1024,
131 max_memory_mb: Some(512),
132 };
133 let json = serde_json::to_string(&limits).unwrap();
134 let back: SandboxLimits = serde_json::from_str(&json).unwrap();
135 assert_eq!(limits, back);
136 }
137
138 #[test]
139 fn limits_omits_none_memory() {
140 let limits = SandboxLimits::default();
141 let json = serde_json::to_string(&limits).unwrap();
142 assert!(!json.contains("max_memory_mb"));
143 }
144
145 #[test]
148 fn network_policy_default_is_disabled() {
149 assert_eq!(NetworkPolicy::default(), NetworkPolicy::Disabled);
150 }
151
152 #[test]
153 fn network_policy_disabled_serde() {
154 let policy = NetworkPolicy::Disabled;
155 let json = serde_json::to_string(&policy).unwrap();
156 assert!(json.contains("\"policy\":\"disabled\""));
157 let back: NetworkPolicy = serde_json::from_str(&json).unwrap();
158 assert_eq!(policy, back);
159 }
160
161 #[test]
162 fn network_policy_allow_all_serde() {
163 let policy = NetworkPolicy::AllowAll;
164 let json = serde_json::to_string(&policy).unwrap();
165 let back: NetworkPolicy = serde_json::from_str(&json).unwrap();
166 assert_eq!(policy, back);
167 }
168
169 #[test]
170 fn network_policy_allow_list_serde() {
171 let policy = NetworkPolicy::AllowList {
172 hosts: vec!["api.anthropic.com".into(), "api.openai.com".into()],
173 };
174 let json = serde_json::to_string(&policy).unwrap();
175 assert!(json.contains("api.anthropic.com"));
176 let back: NetworkPolicy = serde_json::from_str(&json).unwrap();
177 assert_eq!(policy, back);
178 }
179
180 #[test]
181 fn network_policy_allow_list_empty_hosts() {
182 let json = r#"{"policy":"allow_list"}"#;
183 let policy: NetworkPolicy = serde_json::from_str(json).unwrap();
184 match policy {
185 NetworkPolicy::AllowList { hosts } => assert!(hosts.is_empty()),
186 _ => panic!("expected AllowList"),
187 }
188 }
189}