durable_execution_sdk/
summary_generators.rs1pub fn parallel_summary() -> impl Fn(&str) -> String {
36 build_summary("parallel")
37}
38
39pub fn map_summary() -> impl Fn(&str) -> String {
53 build_summary("map")
54}
55
56fn build_summary(summary_type: &str) -> impl Fn(&str) -> String {
57 let summary_type = summary_type.to_string();
58 move |serialized: &str| {
59 let items: Vec<serde_json::Value> = serde_json::from_str(serialized).unwrap_or_default();
60
61 let total = items.len();
62 let failure_count = items
63 .iter()
64 .filter(|item| item.is_object() && item.get("error").is_some())
65 .count();
66 let success_count = total - failure_count;
67
68 let status = if total == 0 || failure_count == total {
69 "failed"
70 } else if failure_count == 0 {
71 "completed"
72 } else {
73 "partial"
74 };
75
76 serde_json::json!({
77 "type": summary_type,
78 "totalCount": total,
79 "successCount": success_count,
80 "failureCount": failure_count,
81 "status": status,
82 })
83 .to_string()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
94 fn parallel_summary_all_success() {
95 let gen = parallel_summary();
96 let input = r#"[{"status":"ok"},{"status":"done"},{"status":"ok"}]"#;
97 let output = gen(input);
98 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
99
100 assert_eq!(parsed["type"], "parallel");
101 assert_eq!(parsed["totalCount"], 3);
102 assert_eq!(parsed["successCount"], 3);
103 assert_eq!(parsed["failureCount"], 0);
104 assert_eq!(parsed["status"], "completed");
105 }
106
107 #[test]
108 fn parallel_summary_all_failure() {
109 let gen = parallel_summary();
110 let input = r#"[{"error":"timeout"},{"error":"connection refused"}]"#;
111 let output = gen(input);
112 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
113
114 assert_eq!(parsed["type"], "parallel");
115 assert_eq!(parsed["totalCount"], 2);
116 assert_eq!(parsed["successCount"], 0);
117 assert_eq!(parsed["failureCount"], 2);
118 assert_eq!(parsed["status"], "failed");
119 }
120
121 #[test]
122 fn parallel_summary_partial() {
123 let gen = parallel_summary();
124 let input = r#"[{"status":"ok"},{"error":"timeout"},{"status":"done"}]"#;
125 let output = gen(input);
126 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
127
128 assert_eq!(parsed["type"], "parallel");
129 assert_eq!(parsed["totalCount"], 3);
130 assert_eq!(parsed["successCount"], 2);
131 assert_eq!(parsed["failureCount"], 1);
132 assert_eq!(parsed["status"], "partial");
133 }
134
135 #[test]
136 fn parallel_summary_empty_array() {
137 let gen = parallel_summary();
138 let output = gen("[]");
139 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
140
141 assert_eq!(parsed["type"], "parallel");
142 assert_eq!(parsed["totalCount"], 0);
143 assert_eq!(parsed["successCount"], 0);
144 assert_eq!(parsed["failureCount"], 0);
145 assert_eq!(parsed["status"], "failed");
146 }
147
148 #[test]
149 fn parallel_summary_invalid_json_returns_empty() {
150 let gen = parallel_summary();
151 let output = gen("not json");
152 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
153
154 assert_eq!(parsed["type"], "parallel");
155 assert_eq!(parsed["totalCount"], 0);
156 assert_eq!(parsed["status"], "failed");
157 }
158
159 #[test]
160 fn parallel_summary_primitive_values_are_successes() {
161 let gen = parallel_summary();
162 let input = r#"[42, "hello", true, null]"#;
163 let output = gen(input);
164 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
165
166 assert_eq!(parsed["totalCount"], 4);
167 assert_eq!(parsed["successCount"], 4);
168 assert_eq!(parsed["failureCount"], 0);
169 assert_eq!(parsed["status"], "completed");
170 }
171
172 #[test]
173 fn parallel_summary_object_without_error_is_success() {
174 let gen = parallel_summary();
175 let input = r#"[{"result":"ok"},{"value":123}]"#;
176 let output = gen(input);
177 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
178
179 assert_eq!(parsed["successCount"], 2);
180 assert_eq!(parsed["failureCount"], 0);
181 }
182
183 #[test]
186 fn map_summary_all_success() {
187 let gen = map_summary();
188 let input = r#"[{"status":"ok"},{"status":"done"}]"#;
189 let output = gen(input);
190 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
191
192 assert_eq!(parsed["type"], "map");
193 assert_eq!(parsed["totalCount"], 2);
194 assert_eq!(parsed["successCount"], 2);
195 assert_eq!(parsed["failureCount"], 0);
196 assert_eq!(parsed["status"], "completed");
197 }
198
199 #[test]
200 fn map_summary_all_failure() {
201 let gen = map_summary();
202 let input = r#"[{"error":"e1"},{"error":"e2"}]"#;
203 let output = gen(input);
204 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
205
206 assert_eq!(parsed["type"], "map");
207 assert_eq!(parsed["totalCount"], 2);
208 assert_eq!(parsed["successCount"], 0);
209 assert_eq!(parsed["failureCount"], 2);
210 assert_eq!(parsed["status"], "failed");
211 }
212
213 #[test]
214 fn map_summary_partial() {
215 let gen = map_summary();
216 let input = r#"[{"status":"ok"},{"error":"timeout"}]"#;
217 let output = gen(input);
218 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
219
220 assert_eq!(parsed["type"], "map");
221 assert_eq!(parsed["totalCount"], 2);
222 assert_eq!(parsed["successCount"], 1);
223 assert_eq!(parsed["failureCount"], 1);
224 assert_eq!(parsed["status"], "partial");
225 }
226
227 #[test]
228 fn map_summary_empty_array() {
229 let gen = map_summary();
230 let output = gen("[]");
231 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
232
233 assert_eq!(parsed["type"], "map");
234 assert_eq!(parsed["totalCount"], 0);
235 assert_eq!(parsed["status"], "failed");
236 }
237
238 #[test]
241 fn parallel_summary_output_is_valid_json() {
242 let gen = parallel_summary();
243 let output = gen(r#"[1,2,3]"#);
244 let parsed: Result<serde_json::Value, _> = serde_json::from_str(&output);
245 assert!(parsed.is_ok());
246 }
247
248 #[test]
249 fn map_summary_output_is_valid_json() {
250 let gen = map_summary();
251 let output = gen(r#"[1,2,3]"#);
252 let parsed: Result<serde_json::Value, _> = serde_json::from_str(&output);
253 assert!(parsed.is_ok());
254 }
255
256 #[test]
257 fn parallel_summary_contains_all_required_fields() {
258 let gen = parallel_summary();
259 let output = gen(r#"[{"status":"ok"}]"#);
260 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
261
262 assert!(parsed.get("type").is_some(), "missing 'type' field");
263 assert!(parsed.get("totalCount").is_some(), "missing 'totalCount'");
264 assert!(
265 parsed.get("successCount").is_some(),
266 "missing 'successCount'"
267 );
268 assert!(
269 parsed.get("failureCount").is_some(),
270 "missing 'failureCount'"
271 );
272 assert!(parsed.get("status").is_some(), "missing 'status' field");
273 }
274
275 #[test]
276 fn map_summary_contains_all_required_fields() {
277 let gen = map_summary();
278 let output = gen(r#"[{"status":"ok"}]"#);
279 let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
280
281 assert!(parsed.get("type").is_some(), "missing 'type' field");
282 assert!(parsed.get("totalCount").is_some(), "missing 'totalCount'");
283 assert!(
284 parsed.get("successCount").is_some(),
285 "missing 'successCount'"
286 );
287 assert!(
288 parsed.get("failureCount").is_some(),
289 "missing 'failureCount'"
290 );
291 assert!(parsed.get("status").is_some(), "missing 'status' field");
292 }
293}