durable_execution_sdk_testing/
types.rs1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26pub enum ExecutionStatus {
27 Running,
29 Succeeded,
31 Failed,
33 Cancelled,
35 TimedOut,
37}
38
39impl ExecutionStatus {
40 pub fn is_terminal(&self) -> bool {
45 !matches!(self, Self::Running)
46 }
47
48 pub fn is_success(&self) -> bool {
50 matches!(self, Self::Succeeded)
51 }
52
53 pub fn is_failure(&self) -> bool {
55 matches!(self, Self::Failed | Self::Cancelled | Self::TimedOut)
56 }
57}
58
59impl std::fmt::Display for ExecutionStatus {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 Self::Running => write!(f, "Running"),
63 Self::Succeeded => write!(f, "Succeeded"),
64 Self::Failed => write!(f, "Failed"),
65 Self::Cancelled => write!(f, "Cancelled"),
66 Self::TimedOut => write!(f, "TimedOut"),
67 }
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct TestResultError {
87 pub error_type: Option<String>,
89 pub error_message: Option<String>,
91 pub error_data: Option<String>,
93 pub stack_trace: Option<Vec<String>>,
95}
96
97impl TestResultError {
98 pub fn new(error_type: impl Into<String>, error_message: impl Into<String>) -> Self {
100 Self {
101 error_type: Some(error_type.into()),
102 error_message: Some(error_message.into()),
103 error_data: None,
104 stack_trace: None,
105 }
106 }
107
108 pub fn from_message(message: impl Into<String>) -> Self {
110 Self {
111 error_type: None,
112 error_message: Some(message.into()),
113 error_data: None,
114 stack_trace: None,
115 }
116 }
117
118 pub fn with_data(mut self, data: impl Into<String>) -> Self {
120 self.error_data = Some(data.into());
121 self
122 }
123
124 pub fn with_stack_trace(mut self, stack_trace: Vec<String>) -> Self {
126 self.stack_trace = Some(stack_trace);
127 self
128 }
129}
130
131impl std::fmt::Display for TestResultError {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 match (&self.error_type, &self.error_message) {
134 (Some(t), Some(m)) => write!(f, "{}: {}", t, m),
135 (None, Some(m)) => write!(f, "{}", m),
136 (Some(t), None) => write!(f, "{}", t),
137 (None, None) => write!(f, "Unknown error"),
138 }
139 }
140}
141
142impl std::error::Error for TestResultError {}
143
144impl From<durable_execution_sdk::ErrorObject> for TestResultError {
145 fn from(error: durable_execution_sdk::ErrorObject) -> Self {
146 Self {
147 error_type: Some(error.error_type),
148 error_message: Some(error.error_message),
149 error_data: None,
150 stack_trace: error.stack_trace.map(|s| vec![s]),
151 }
152 }
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct Invocation {
171 pub start_timestamp: Option<DateTime<Utc>>,
173 pub end_timestamp: Option<DateTime<Utc>>,
175 pub request_id: Option<String>,
177 pub error: Option<TestResultError>,
179}
180
181impl Invocation {
182 pub fn new() -> Self {
184 Self {
185 start_timestamp: None,
186 end_timestamp: None,
187 request_id: None,
188 error: None,
189 }
190 }
191
192 pub fn with_start(start: DateTime<Utc>) -> Self {
194 Self {
195 start_timestamp: Some(start),
196 end_timestamp: None,
197 request_id: None,
198 error: None,
199 }
200 }
201
202 pub fn with_end(mut self, end: DateTime<Utc>) -> Self {
204 self.end_timestamp = Some(end);
205 self
206 }
207
208 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
210 self.request_id = Some(request_id.into());
211 self
212 }
213
214 pub fn with_error(mut self, error: TestResultError) -> Self {
216 self.error = Some(error);
217 self
218 }
219
220 pub fn duration(&self) -> Option<chrono::Duration> {
222 match (&self.start_timestamp, &self.end_timestamp) {
223 (Some(start), Some(end)) => Some(*end - *start),
224 _ => None,
225 }
226 }
227}
228
229impl Default for Invocation {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
249pub enum WaitingOperationStatus {
250 Started,
252 Submitted,
254 Completed,
256}
257
258impl std::fmt::Display for WaitingOperationStatus {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 match self {
261 Self::Started => write!(f, "Started"),
262 Self::Submitted => write!(f, "Submitted"),
263 Self::Completed => write!(f, "Completed"),
264 }
265 }
266}
267
268#[derive(Debug, Clone, Default, Serialize, Deserialize)]
294pub struct InvokeRequest<T = serde_json::Value> {
295 pub payload: Option<T>,
297}
298
299impl<T> InvokeRequest<T> {
300 pub fn new() -> Self {
302 Self { payload: None }
303 }
304
305 pub fn with_payload(payload: T) -> Self {
307 Self {
308 payload: Some(payload),
309 }
310 }
311}
312
313impl<T> From<T> for InvokeRequest<T> {
314 fn from(value: T) -> Self {
315 Self::with_payload(value)
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_execution_status_terminal() {
325 assert!(!ExecutionStatus::Running.is_terminal());
326 assert!(ExecutionStatus::Succeeded.is_terminal());
327 assert!(ExecutionStatus::Failed.is_terminal());
328 assert!(ExecutionStatus::Cancelled.is_terminal());
329 assert!(ExecutionStatus::TimedOut.is_terminal());
330 }
331
332 #[test]
333 fn test_execution_status_success() {
334 assert!(ExecutionStatus::Succeeded.is_success());
335 assert!(!ExecutionStatus::Failed.is_success());
336 assert!(!ExecutionStatus::Running.is_success());
337 }
338
339 #[test]
340 fn test_execution_status_failure() {
341 assert!(!ExecutionStatus::Succeeded.is_failure());
342 assert!(ExecutionStatus::Failed.is_failure());
343 assert!(ExecutionStatus::Cancelled.is_failure());
344 assert!(ExecutionStatus::TimedOut.is_failure());
345 assert!(!ExecutionStatus::Running.is_failure());
346 }
347
348 #[test]
349 fn test_test_result_error_new() {
350 let error = TestResultError::new("TestError", "Test message");
351 assert_eq!(error.error_type, Some("TestError".to_string()));
352 assert_eq!(error.error_message, Some("Test message".to_string()));
353 assert!(error.error_data.is_none());
354 assert!(error.stack_trace.is_none());
355 }
356
357 #[test]
358 fn test_test_result_error_display() {
359 let error = TestResultError::new("TestError", "Test message");
360 assert_eq!(format!("{}", error), "TestError: Test message");
361
362 let error_no_type = TestResultError::from_message("Just a message");
363 assert_eq!(format!("{}", error_no_type), "Just a message");
364 }
365
366 #[test]
367 fn test_invocation_duration() {
368 use chrono::TimeZone;
369 let start = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
370 let end = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 5).unwrap();
371
372 let invocation = Invocation::with_start(start).with_end(end);
373 let duration = invocation.duration().unwrap();
374 assert_eq!(duration.num_seconds(), 5);
375 }
376
377 #[test]
378 fn test_waiting_operation_status_display() {
379 assert_eq!(format!("{}", WaitingOperationStatus::Started), "Started");
380 assert_eq!(
381 format!("{}", WaitingOperationStatus::Submitted),
382 "Submitted"
383 );
384 assert_eq!(
385 format!("{}", WaitingOperationStatus::Completed),
386 "Completed"
387 );
388 }
389
390 #[test]
391 fn test_invoke_request_new_has_no_payload() {
392 let req: InvokeRequest<String> = InvokeRequest::new();
393 assert!(req.payload.is_none());
394 }
395
396 #[test]
397 fn test_invoke_request_with_payload() {
398 let req = InvokeRequest::with_payload(42);
399 assert_eq!(req.payload, Some(42));
400 }
401
402 #[test]
403 fn test_invoke_request_default_has_no_payload() {
404 let req: InvokeRequest<String> = InvokeRequest::default();
405 assert!(req.payload.is_none());
406 }
407
408 #[test]
409 fn test_invoke_request_from_raw_value() {
410 let req: InvokeRequest<String> = "hello".to_string().into();
411 assert_eq!(req.payload, Some("hello".to_string()));
412 }
413
414 #[test]
415 fn test_invoke_request_serde_round_trip() {
416 let req = InvokeRequest::with_payload(serde_json::json!({"key": "value"}));
417 let serialized = serde_json::to_string(&req).unwrap();
418 let deserialized: InvokeRequest<serde_json::Value> =
419 serde_json::from_str(&serialized).unwrap();
420 assert_eq!(req.payload, deserialized.payload);
421 }
422
423 #[test]
424 fn test_invoke_request_serde_no_payload() {
425 let req: InvokeRequest<String> = InvokeRequest::new();
426 let serialized = serde_json::to_string(&req).unwrap();
427 let deserialized: InvokeRequest<String> = serde_json::from_str(&serialized).unwrap();
428 assert!(deserialized.payload.is_none());
429 }
430}