1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct TcpFixture {
9 pub identifier: String,
11
12 pub name: String,
14
15 #[serde(default)]
17 pub description: String,
18
19 pub match_criteria: MatchCriteria,
21
22 pub response: TcpResponse,
24
25 #[serde(default)]
27 pub behavior: BehaviorConfig,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, Default)]
32pub struct MatchCriteria {
33 #[serde(default)]
35 pub data_pattern: Option<String>,
36
37 #[serde(default)]
39 pub text_pattern: Option<String>,
40
41 #[serde(default)]
43 pub exact_bytes: Option<String>,
44
45 #[serde(default)]
47 pub match_all: bool,
48
49 #[serde(default)]
51 pub min_length: Option<usize>,
52
53 #[serde(default)]
55 pub max_length: Option<usize>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct TcpResponse {
61 pub data: String,
63
64 #[serde(default = "default_encoding")]
66 pub encoding: String,
67
68 #[serde(default)]
70 pub file_path: Option<PathBuf>,
71
72 #[serde(default)]
74 pub delay_ms: u64,
75
76 #[serde(default)]
78 pub close_after_response: bool,
79
80 #[serde(default)]
82 pub keep_alive: bool,
83}
84
85fn default_encoding() -> String {
86 "text".to_string()
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, Default)]
91pub struct BehaviorConfig {
92 #[serde(default)]
94 pub connection_delay_ms: u64,
95
96 #[serde(default)]
98 pub throttle_bytes_per_sec: Option<u64>,
99
100 #[serde(default)]
102 pub drop_connection_probability: f64,
103
104 #[serde(default)]
106 pub partial_data_bytes: Option<usize>,
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 fn create_test_fixture() -> TcpFixture {
114 TcpFixture {
115 identifier: "test-fixture".to_string(),
116 name: "Test Fixture".to_string(),
117 description: "A test fixture".to_string(),
118 match_criteria: MatchCriteria::default(),
119 response: TcpResponse {
120 data: "Hello".to_string(),
121 encoding: "text".to_string(),
122 file_path: None,
123 delay_ms: 0,
124 close_after_response: false,
125 keep_alive: true,
126 },
127 behavior: BehaviorConfig::default(),
128 }
129 }
130
131 #[test]
132 fn test_match_criteria_default() {
133 let criteria = MatchCriteria::default();
134 assert!(criteria.data_pattern.is_none());
135 assert!(criteria.text_pattern.is_none());
136 assert!(criteria.exact_bytes.is_none());
137 assert!(!criteria.match_all);
138 assert!(criteria.min_length.is_none());
139 assert!(criteria.max_length.is_none());
140 }
141
142 #[test]
143 fn test_match_criteria_clone() {
144 let criteria = MatchCriteria {
145 data_pattern: Some("48656c6c6f".to_string()),
146 text_pattern: Some("hello.*".to_string()),
147 exact_bytes: None,
148 match_all: true,
149 min_length: Some(5),
150 max_length: Some(100),
151 };
152
153 let cloned = criteria.clone();
154 assert_eq!(criteria.data_pattern, cloned.data_pattern);
155 assert_eq!(criteria.text_pattern, cloned.text_pattern);
156 assert_eq!(criteria.match_all, cloned.match_all);
157 }
158
159 #[test]
160 fn test_match_criteria_debug() {
161 let criteria = MatchCriteria::default();
162 let debug = format!("{:?}", criteria);
163 assert!(debug.contains("MatchCriteria"));
164 }
165
166 #[test]
167 fn test_tcp_response_default_encoding() {
168 let json = r#"{
169 "data": "test"
170 }"#;
171 let response: TcpResponse = serde_json::from_str(json).unwrap();
172 assert_eq!(response.encoding, "text");
173 }
174
175 #[test]
176 fn test_tcp_response_clone() {
177 let response = TcpResponse {
178 data: "Hello World".to_string(),
179 encoding: "base64".to_string(),
180 file_path: Some(PathBuf::from("/path/to/file")),
181 delay_ms: 100,
182 close_after_response: true,
183 keep_alive: false,
184 };
185
186 let cloned = response.clone();
187 assert_eq!(response.data, cloned.data);
188 assert_eq!(response.encoding, cloned.encoding);
189 assert_eq!(response.file_path, cloned.file_path);
190 }
191
192 #[test]
193 fn test_tcp_response_debug() {
194 let response = TcpResponse {
195 data: "test".to_string(),
196 encoding: "text".to_string(),
197 file_path: None,
198 delay_ms: 0,
199 close_after_response: false,
200 keep_alive: true,
201 };
202 let debug = format!("{:?}", response);
203 assert!(debug.contains("TcpResponse"));
204 }
205
206 #[test]
207 fn test_behavior_config_default() {
208 let config = BehaviorConfig::default();
209 assert_eq!(config.connection_delay_ms, 0);
210 assert!(config.throttle_bytes_per_sec.is_none());
211 assert_eq!(config.drop_connection_probability, 0.0);
212 assert!(config.partial_data_bytes.is_none());
213 }
214
215 #[test]
216 fn test_behavior_config_clone() {
217 let config = BehaviorConfig {
218 connection_delay_ms: 500,
219 throttle_bytes_per_sec: Some(1024),
220 drop_connection_probability: 0.1,
221 partial_data_bytes: Some(100),
222 };
223
224 let cloned = config.clone();
225 assert_eq!(config.connection_delay_ms, cloned.connection_delay_ms);
226 assert_eq!(config.throttle_bytes_per_sec, cloned.throttle_bytes_per_sec);
227 assert_eq!(config.drop_connection_probability, cloned.drop_connection_probability);
228 }
229
230 #[test]
231 fn test_behavior_config_debug() {
232 let config = BehaviorConfig::default();
233 let debug = format!("{:?}", config);
234 assert!(debug.contains("BehaviorConfig"));
235 }
236
237 #[test]
238 fn test_tcp_fixture_clone() {
239 let fixture = create_test_fixture();
240 let cloned = fixture.clone();
241 assert_eq!(fixture.identifier, cloned.identifier);
242 assert_eq!(fixture.name, cloned.name);
243 }
244
245 #[test]
246 fn test_tcp_fixture_debug() {
247 let fixture = create_test_fixture();
248 let debug = format!("{:?}", fixture);
249 assert!(debug.contains("TcpFixture"));
250 assert!(debug.contains("test-fixture"));
251 }
252
253 #[test]
254 fn test_tcp_fixture_serialize() {
255 let fixture = create_test_fixture();
256 let json = serde_json::to_string(&fixture).unwrap();
257 assert!(json.contains("\"identifier\":\"test-fixture\""));
258 assert!(json.contains("\"name\":\"Test Fixture\""));
259 }
260
261 #[test]
262 fn test_tcp_fixture_deserialize() {
263 let yaml = r#"
264identifier: my-fixture
265name: My Fixture
266description: A fixture for testing
267match_criteria:
268 match_all: true
269response:
270 data: "Hello"
271 encoding: text
272 delay_ms: 0
273 close_after_response: false
274 keep_alive: true
275"#;
276
277 let fixture: TcpFixture = serde_yaml::from_str(yaml).unwrap();
278 assert_eq!(fixture.identifier, "my-fixture");
279 assert_eq!(fixture.name, "My Fixture");
280 assert!(fixture.match_criteria.match_all);
281 }
282
283 #[test]
284 fn test_tcp_response_with_delay() {
285 let response = TcpResponse {
286 data: "delayed".to_string(),
287 encoding: "text".to_string(),
288 file_path: None,
289 delay_ms: 1000,
290 close_after_response: true,
291 keep_alive: false,
292 };
293
294 assert_eq!(response.delay_ms, 1000);
295 assert!(response.close_after_response);
296 assert!(!response.keep_alive);
297 }
298
299 #[test]
300 fn test_match_criteria_with_lengths() {
301 let criteria = MatchCriteria {
302 min_length: Some(10),
303 max_length: Some(1000),
304 ..Default::default()
305 };
306
307 assert_eq!(criteria.min_length, Some(10));
308 assert_eq!(criteria.max_length, Some(1000));
309 }
310}