1use crate::{
122 AsyncRunnableChild, Child, ChildResult, Component, ComponentError, EventCategory,
123 RunnableChild, Status,
124};
125use orcs_event::{Signal, SignalResponse};
126use orcs_types::{ChannelId, ComponentId, Principal};
127use serde::{Deserialize, Serialize};
128use serde_json::Value;
129use std::time::{Duration, Instant};
130use thiserror::Error;
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct RequestRecord {
135 pub operation: String,
137 pub category: String,
139 pub result: RequestResult,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145#[serde(tag = "type", content = "value")]
146pub enum RequestResult {
147 Ok(Value),
149 Err(String),
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct SignalRecord {
156 pub kind: String,
158 pub response: String,
160}
161
162pub struct ComponentTestHarness<C: Component> {
167 component: C,
169 request_log: Vec<RequestRecord>,
171 signal_log: Vec<SignalRecord>,
173 test_channel: ChannelId,
175}
176
177impl<C: Component> ComponentTestHarness<C> {
178 pub fn new(component: C) -> Self {
180 Self {
181 component,
182 request_log: Vec::new(),
183 signal_log: Vec::new(),
184 test_channel: ChannelId::new(),
185 }
186 }
187
188 pub fn component(&self) -> &C {
190 &self.component
191 }
192
193 pub fn component_mut(&mut self) -> &mut C {
195 &mut self.component
196 }
197
198 pub fn init(&mut self) -> Result<(), ComponentError> {
204 self.component
205 .init(&serde_json::Value::Object(serde_json::Map::new()))
206 }
207
208 pub fn init_with_config(&mut self, config: &serde_json::Value) -> Result<(), ComponentError> {
214 self.component.init(config)
215 }
216
217 pub fn send_request(&mut self, request: &orcs_event::Request) -> Result<Value, ComponentError> {
227 let result = self.component.on_request(request);
228
229 self.request_log.push(RequestRecord {
230 operation: request.operation.clone(),
231 category: request.category.name(),
232 result: match &result {
233 Ok(v) => RequestResult::Ok(v.clone()),
234 Err(e) => RequestResult::Err(e.to_string()),
235 },
236 });
237
238 result
239 }
240
241 pub fn request(
255 &mut self,
256 category: EventCategory,
257 operation: &str,
258 payload: Value,
259 ) -> Result<Value, ComponentError> {
260 let req = orcs_event::Request::new(
261 category,
262 operation,
263 self.component.id().clone(),
264 self.test_channel,
265 payload,
266 );
267 self.send_request(&req)
268 }
269
270 pub fn send_signal(&mut self, signal: Signal) -> SignalResponse {
280 let response = self.component.on_signal(&signal);
281
282 self.signal_log.push(SignalRecord {
283 kind: format!("{:?}", signal.kind),
284 response: format!("{:?}", response),
285 });
286
287 response
288 }
289
290 pub fn veto(&mut self) -> SignalResponse {
296 self.send_signal(Signal::veto(Principal::System))
297 }
298
299 pub fn cancel(&mut self) -> SignalResponse {
305 self.send_signal(Signal::cancel(self.test_channel, Principal::System))
306 }
307
308 pub fn cancel_channel(&mut self, channel: ChannelId) -> SignalResponse {
318 self.send_signal(Signal::cancel(channel, Principal::System))
319 }
320
321 pub fn approve(&mut self, approval_id: &str) -> SignalResponse {
331 self.send_signal(Signal::approve(approval_id, Principal::System))
332 }
333
334 pub fn reject(&mut self, approval_id: &str, reason: Option<String>) -> SignalResponse {
345 self.send_signal(Signal::reject(approval_id, reason, Principal::System))
346 }
347
348 pub fn abort(&mut self) {
350 self.component.abort();
351 }
352
353 pub fn shutdown(&mut self) {
355 self.component.shutdown();
356 }
357
358 pub fn status(&self) -> Status {
360 self.component.status()
361 }
362
363 pub fn id(&self) -> &ComponentId {
365 self.component.id()
366 }
367
368 pub fn request_log(&self) -> &[RequestRecord] {
370 &self.request_log
371 }
372
373 pub fn signal_log(&self) -> &[SignalRecord] {
375 &self.signal_log
376 }
377
378 pub fn clear_logs(&mut self) {
380 self.request_log.clear();
381 self.signal_log.clear();
382 }
383
384 pub fn test_channel(&self) -> ChannelId {
386 self.test_channel
387 }
388
389 pub fn with_channel(mut self, channel: ChannelId) -> Self {
391 self.test_channel = channel;
392 self
393 }
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct RunRecord {
403 pub input: Value,
405 pub result: RunResult,
407 pub elapsed_ms: Option<u64>,
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
413#[serde(tag = "type")]
414pub enum RunResult {
415 Ok {
417 value: Value,
419 },
420 Err {
422 kind: String,
424 message: String,
426 },
427 Aborted,
429}
430
431impl From<&ChildResult> for RunResult {
432 fn from(result: &ChildResult) -> Self {
433 match result {
434 ChildResult::Ok(v) => Self::Ok { value: v.clone() },
435 ChildResult::Err(e) => Self::Err {
436 kind: e.kind().to_string(),
437 message: e.to_string(),
438 },
439 ChildResult::Aborted => Self::Aborted,
440 }
441 }
442}
443
444#[derive(Debug, Clone, Error)]
446#[error("operation timed out after {elapsed_ms}ms (limit: {timeout_ms}ms)")]
447pub struct TimeoutError {
448 pub elapsed_ms: u64,
450 pub timeout_ms: u64,
452}
453
454pub struct ChildTestHarness<C: Child> {
505 child: C,
507 signal_log: Vec<SignalRecord>,
509}
510
511impl<C: Child> ChildTestHarness<C> {
512 pub fn new(child: C) -> Self {
514 Self {
515 child,
516 signal_log: Vec::new(),
517 }
518 }
519
520 pub fn child(&self) -> &C {
522 &self.child
523 }
524
525 pub fn child_mut(&mut self) -> &mut C {
527 &mut self.child
528 }
529
530 pub fn id(&self) -> &str {
532 self.child.id()
533 }
534
535 pub fn status(&self) -> Status {
537 self.child.status()
538 }
539
540 pub fn send_signal(&mut self, signal: Signal) -> SignalResponse {
542 let response = self.child.on_signal(&signal);
543
544 self.signal_log.push(SignalRecord {
545 kind: format!("{:?}", signal.kind),
546 response: format!("{:?}", response),
547 });
548
549 response
550 }
551
552 pub fn veto(&mut self) -> SignalResponse {
554 self.send_signal(Signal::veto(Principal::System))
555 }
556
557 pub fn cancel(&mut self) -> SignalResponse {
559 self.send_signal(Signal::cancel(ChannelId::new(), Principal::System))
560 }
561
562 pub fn abort(&mut self) {
564 self.child.abort();
565 }
566
567 pub fn signal_log(&self) -> &[SignalRecord] {
569 &self.signal_log
570 }
571
572 pub fn clear_logs(&mut self) {
574 self.signal_log.clear();
575 }
576}
577
578pub struct SyncChildTestHarness<C: RunnableChild> {
635 inner: ChildTestHarness<C>,
637 run_log: Vec<RunRecord>,
639 measure_time: bool,
641}
642
643impl<C: RunnableChild> SyncChildTestHarness<C> {
644 pub fn new(child: C) -> Self {
648 Self {
649 inner: ChildTestHarness::new(child),
650 run_log: Vec::new(),
651 measure_time: true,
652 }
653 }
654
655 pub fn without_time_measurement(mut self) -> Self {
657 self.measure_time = false;
658 self
659 }
660
661 pub fn run(&mut self, input: Value) -> ChildResult {
663 let start = if self.measure_time {
664 Some(Instant::now())
665 } else {
666 None
667 };
668
669 let result = self.inner.child.run(input.clone());
670
671 let elapsed_ms = start.map(|s| s.elapsed().as_millis() as u64);
672
673 self.run_log.push(RunRecord {
674 input,
675 result: RunResult::from(&result),
676 elapsed_ms,
677 });
678
679 result
680 }
681
682 pub fn run_json<T: Serialize>(&mut self, input: T) -> ChildResult {
684 let value = serde_json::to_value(input).unwrap_or(Value::Null);
685 self.run(value)
686 }
687
688 pub fn run_log(&self) -> &[RunRecord] {
690 &self.run_log
691 }
692
693 pub fn clear_all_logs(&mut self) {
695 self.run_log.clear();
696 self.inner.clear_logs();
697 }
698
699 pub fn child(&self) -> &C {
703 self.inner.child()
704 }
705
706 pub fn child_mut(&mut self) -> &mut C {
708 self.inner.child_mut()
709 }
710
711 pub fn id(&self) -> &str {
713 self.inner.id()
714 }
715
716 pub fn status(&self) -> Status {
718 self.inner.status()
719 }
720
721 pub fn send_signal(&mut self, signal: Signal) -> SignalResponse {
723 self.inner.send_signal(signal)
724 }
725
726 pub fn veto(&mut self) -> SignalResponse {
728 self.inner.veto()
729 }
730
731 pub fn cancel(&mut self) -> SignalResponse {
733 self.inner.cancel()
734 }
735
736 pub fn abort(&mut self) {
738 self.inner.abort();
739 }
740
741 pub fn signal_log(&self) -> &[SignalRecord] {
743 self.inner.signal_log()
744 }
745}
746
747pub struct AsyncChildTestHarness<C: AsyncRunnableChild> {
790 inner: ChildTestHarness<C>,
792 run_log: Vec<RunRecord>,
794 measure_time: bool,
796 default_timeout: Option<Duration>,
798}
799
800impl<C: AsyncRunnableChild> AsyncChildTestHarness<C> {
801 pub fn new(child: C) -> Self {
805 Self {
806 inner: ChildTestHarness::new(child),
807 run_log: Vec::new(),
808 measure_time: true,
809 default_timeout: None,
810 }
811 }
812
813 pub fn without_time_measurement(mut self) -> Self {
815 self.measure_time = false;
816 self
817 }
818
819 pub fn with_default_timeout(mut self, timeout: Duration) -> Self {
821 self.default_timeout = Some(timeout);
822 self
823 }
824
825 pub async fn run(&mut self, input: Value) -> ChildResult {
829 if let Some(timeout) = self.default_timeout {
830 match self.run_with_timeout(input, timeout).await {
831 Ok(result) => result,
832 Err(_) => ChildResult::Aborted,
833 }
834 } else {
835 self.run_inner(input).await
836 }
837 }
838
839 pub async fn run_with_timeout(
843 &mut self,
844 input: Value,
845 timeout: Duration,
846 ) -> Result<ChildResult, TimeoutError> {
847 let start = Instant::now();
848
849 match tokio::time::timeout(timeout, self.run_inner(input)).await {
850 Ok(result) => Ok(result),
851 Err(_) => Err(TimeoutError {
852 elapsed_ms: start.elapsed().as_millis() as u64,
853 timeout_ms: timeout.as_millis() as u64,
854 }),
855 }
856 }
857
858 async fn run_inner(&mut self, input: Value) -> ChildResult {
860 let start = if self.measure_time {
861 Some(Instant::now())
862 } else {
863 None
864 };
865
866 let result = self.inner.child.run(input.clone()).await;
867
868 let elapsed_ms = start.map(|s| s.elapsed().as_millis() as u64);
869
870 self.run_log.push(RunRecord {
871 input,
872 result: RunResult::from(&result),
873 elapsed_ms,
874 });
875
876 result
877 }
878
879 pub async fn run_json<T: Serialize>(&mut self, input: T) -> ChildResult {
881 let value = serde_json::to_value(input).unwrap_or(Value::Null);
882 self.run(value).await
883 }
884
885 pub fn run_log(&self) -> &[RunRecord] {
887 &self.run_log
888 }
889
890 pub fn clear_all_logs(&mut self) {
892 self.run_log.clear();
893 self.inner.clear_logs();
894 }
895
896 pub fn child(&self) -> &C {
900 self.inner.child()
901 }
902
903 pub fn child_mut(&mut self) -> &mut C {
905 self.inner.child_mut()
906 }
907
908 pub fn id(&self) -> &str {
910 self.inner.id()
911 }
912
913 pub fn status(&self) -> Status {
915 self.inner.status()
916 }
917
918 pub fn send_signal(&mut self, signal: Signal) -> SignalResponse {
920 self.inner.send_signal(signal)
921 }
922
923 pub fn veto(&mut self) -> SignalResponse {
925 self.inner.veto()
926 }
927
928 pub fn cancel(&mut self) -> SignalResponse {
930 self.inner.cancel()
931 }
932
933 pub fn abort(&mut self) {
935 self.inner.abort();
936 }
937
938 pub fn signal_log(&self) -> &[SignalRecord] {
940 self.inner.signal_log()
941 }
942}
943
944#[cfg(test)]
945mod tests {
946 use super::*;
947 use crate::{Identifiable, SignalReceiver, Status, Statusable};
948
949 struct TestComponent {
950 id: ComponentId,
951 status: Status,
952 request_count: usize,
953 }
954
955 impl TestComponent {
956 fn new(name: &str) -> Self {
957 Self {
958 id: ComponentId::builtin(name),
959 status: Status::Idle,
960 request_count: 0,
961 }
962 }
963 }
964
965 impl Component for TestComponent {
966 fn id(&self) -> &ComponentId {
967 &self.id
968 }
969
970 fn status(&self) -> Status {
971 self.status
972 }
973
974 fn on_request(&mut self, request: &orcs_event::Request) -> Result<Value, ComponentError> {
975 self.request_count += 1;
976 match request.operation.as_str() {
977 "echo" => Ok(request.payload.clone()),
978 "count" => Ok(Value::Number(self.request_count.into())),
979 _ => Err(ComponentError::NotSupported(request.operation.clone())),
980 }
981 }
982
983 fn on_signal(&mut self, signal: &Signal) -> SignalResponse {
984 if signal.is_veto() {
985 self.abort();
986 SignalResponse::Abort
987 } else if matches!(signal.kind, orcs_event::SignalKind::Cancel) {
988 SignalResponse::Handled
989 } else {
990 SignalResponse::Ignored
991 }
992 }
993
994 fn abort(&mut self) {
995 self.status = Status::Aborted;
996 }
997 }
998
999 #[test]
1000 fn harness_request() {
1001 let comp = TestComponent::new("test");
1002 let mut harness = ComponentTestHarness::new(comp);
1003
1004 let result = harness.request(
1005 EventCategory::Echo,
1006 "echo",
1007 serde_json::json!({"msg": "hello"}),
1008 );
1009 assert!(result.is_ok());
1010 assert_eq!(
1011 result.expect("echo request should succeed"),
1012 serde_json::json!({"msg": "hello"})
1013 );
1014
1015 assert_eq!(harness.request_log().len(), 1);
1016 assert_eq!(harness.request_log()[0].operation, "echo");
1017 }
1018
1019 #[test]
1020 fn harness_request_error() {
1021 let comp = TestComponent::new("test");
1022 let mut harness = ComponentTestHarness::new(comp);
1023
1024 let result = harness.request(EventCategory::Echo, "unknown", Value::Null);
1025 assert!(result.is_err());
1026
1027 assert_eq!(harness.request_log().len(), 1);
1028 assert!(matches!(
1029 harness.request_log()[0].result,
1030 RequestResult::Err(_)
1031 ));
1032 }
1033
1034 #[test]
1035 fn harness_veto() {
1036 let comp = TestComponent::new("test");
1037 let mut harness = ComponentTestHarness::new(comp);
1038
1039 assert_eq!(harness.status(), Status::Idle);
1040
1041 let response = harness.veto();
1042 assert_eq!(response, SignalResponse::Abort);
1043 assert_eq!(harness.status(), Status::Aborted);
1044
1045 assert_eq!(harness.signal_log().len(), 1);
1046 assert!(harness.signal_log()[0].kind.contains("Veto"));
1047 }
1048
1049 #[test]
1050 fn harness_cancel() {
1051 let comp = TestComponent::new("test");
1052 let mut harness = ComponentTestHarness::new(comp);
1053
1054 let response = harness.cancel();
1055 assert_eq!(response, SignalResponse::Handled);
1056 assert_eq!(harness.status(), Status::Idle);
1057 }
1058
1059 #[test]
1060 fn harness_component_access() {
1061 let comp = TestComponent::new("test");
1062 let mut harness = ComponentTestHarness::new(comp);
1063
1064 assert_eq!(harness.component().request_count, 0);
1065
1066 harness
1067 .request(EventCategory::Echo, "count", Value::Null)
1068 .expect("count request should succeed");
1069 assert_eq!(harness.component().request_count, 1);
1070
1071 harness.component_mut().request_count = 100;
1072 assert_eq!(harness.component().request_count, 100);
1073 }
1074
1075 #[test]
1076 fn harness_clear_logs() {
1077 let comp = TestComponent::new("test");
1078 let mut harness = ComponentTestHarness::new(comp);
1079
1080 harness
1081 .request(EventCategory::Echo, "echo", Value::Null)
1082 .expect("echo request should succeed for log clearing test");
1083 harness.cancel();
1084
1085 assert_eq!(harness.request_log().len(), 1);
1086 assert_eq!(harness.signal_log().len(), 1);
1087
1088 harness.clear_logs();
1089
1090 assert_eq!(harness.request_log().len(), 0);
1091 assert_eq!(harness.signal_log().len(), 0);
1092 }
1093
1094 #[test]
1095 fn harness_init_shutdown() {
1096 let comp = TestComponent::new("test");
1097 let mut harness = ComponentTestHarness::new(comp);
1098
1099 assert!(harness.init().is_ok());
1100 harness.shutdown();
1101 assert_eq!(harness.id().name, "test");
1103 }
1104
1105 use crate::{Child, ChildError, RunnableChild};
1110
1111 struct TestChild {
1112 id: String,
1113 status: Status,
1114 }
1115
1116 impl TestChild {
1117 fn new(id: &str) -> Self {
1118 Self {
1119 id: id.into(),
1120 status: Status::Idle,
1121 }
1122 }
1123 }
1124
1125 impl Identifiable for TestChild {
1126 fn id(&self) -> &str {
1127 &self.id
1128 }
1129 }
1130
1131 impl SignalReceiver for TestChild {
1132 fn on_signal(&mut self, signal: &Signal) -> SignalResponse {
1133 if signal.is_veto() {
1134 self.abort();
1135 SignalResponse::Abort
1136 } else {
1137 SignalResponse::Handled
1138 }
1139 }
1140
1141 fn abort(&mut self) {
1142 self.status = Status::Aborted;
1143 }
1144 }
1145
1146 impl Statusable for TestChild {
1147 fn status(&self) -> Status {
1148 self.status
1149 }
1150 }
1151
1152 impl Child for TestChild {}
1153
1154 #[test]
1157 fn child_harness_new() {
1158 let child = TestChild::new("test-child");
1159 let harness = ChildTestHarness::new(child);
1160
1161 assert_eq!(harness.id(), "test-child");
1162 assert_eq!(harness.status(), Status::Idle);
1163 assert!(harness.signal_log().is_empty());
1164 }
1165
1166 #[test]
1167 fn child_harness_veto() {
1168 let child = TestChild::new("test");
1169 let mut harness = ChildTestHarness::new(child);
1170
1171 let response = harness.veto();
1172 assert_eq!(response, SignalResponse::Abort);
1173 assert_eq!(harness.status(), Status::Aborted);
1174
1175 assert_eq!(harness.signal_log().len(), 1);
1176 assert!(harness.signal_log()[0].kind.contains("Veto"));
1177 }
1178
1179 #[test]
1180 fn child_harness_cancel() {
1181 let child = TestChild::new("test");
1182 let mut harness = ChildTestHarness::new(child);
1183
1184 let response = harness.cancel();
1185 assert_eq!(response, SignalResponse::Handled);
1186 assert_eq!(harness.status(), Status::Idle);
1187 }
1188
1189 #[test]
1190 fn child_harness_clear_logs() {
1191 let child = TestChild::new("test");
1192 let mut harness = ChildTestHarness::new(child);
1193
1194 harness.veto();
1195 harness.cancel();
1196 assert_eq!(harness.signal_log().len(), 2);
1197
1198 harness.clear_logs();
1199 assert!(harness.signal_log().is_empty());
1200 }
1201
1202 #[test]
1203 fn child_harness_access() {
1204 let child = TestChild::new("test");
1205 let mut harness = ChildTestHarness::new(child);
1206
1207 assert_eq!(harness.child().id, "test");
1208 harness.child_mut().status = Status::Running;
1209 assert_eq!(harness.status(), Status::Running);
1210 }
1211
1212 struct TestRunnableChild {
1215 id: String,
1216 status: Status,
1217 run_count: usize,
1218 }
1219
1220 impl TestRunnableChild {
1221 fn new(id: &str) -> Self {
1222 Self {
1223 id: id.into(),
1224 status: Status::Idle,
1225 run_count: 0,
1226 }
1227 }
1228 }
1229
1230 impl Identifiable for TestRunnableChild {
1231 fn id(&self) -> &str {
1232 &self.id
1233 }
1234 }
1235
1236 impl SignalReceiver for TestRunnableChild {
1237 fn on_signal(&mut self, signal: &Signal) -> SignalResponse {
1238 if signal.is_veto() {
1239 self.abort();
1240 SignalResponse::Abort
1241 } else {
1242 SignalResponse::Handled
1243 }
1244 }
1245
1246 fn abort(&mut self) {
1247 self.status = Status::Aborted;
1248 }
1249 }
1250
1251 impl Statusable for TestRunnableChild {
1252 fn status(&self) -> Status {
1253 self.status
1254 }
1255 }
1256
1257 impl Child for TestRunnableChild {}
1258
1259 impl RunnableChild for TestRunnableChild {
1260 fn run(&mut self, input: Value) -> ChildResult {
1261 self.status = Status::Running;
1262 self.run_count += 1;
1263
1264 if input.get("fail").is_some() {
1265 self.status = Status::Idle;
1266 return ChildResult::Err(ChildError::ExecutionFailed {
1267 reason: "requested failure".into(),
1268 });
1269 }
1270
1271 self.status = Status::Idle;
1272 ChildResult::Ok(serde_json::json!({
1273 "echo": input,
1274 "count": self.run_count
1275 }))
1276 }
1277 }
1278
1279 #[test]
1280 fn sync_child_harness_run() {
1281 let child = TestRunnableChild::new("worker");
1282 let mut harness = SyncChildTestHarness::new(child);
1283
1284 let result = harness.run(serde_json::json!({"task": "test"}));
1285 assert!(result.is_ok());
1286
1287 assert_eq!(harness.run_log().len(), 1);
1288 assert!(harness.run_log()[0].elapsed_ms.is_some());
1289
1290 if let RunResult::Ok { value } = &harness.run_log()[0].result {
1291 assert_eq!(value["count"], 1);
1292 } else {
1293 panic!("expected Ok result");
1294 }
1295 }
1296
1297 #[test]
1298 fn sync_child_harness_run_error() {
1299 let child = TestRunnableChild::new("worker");
1300 let mut harness = SyncChildTestHarness::new(child);
1301
1302 let result = harness.run(serde_json::json!({"fail": true}));
1303 assert!(result.is_err());
1304
1305 assert_eq!(harness.run_log().len(), 1);
1306 assert!(matches!(harness.run_log()[0].result, RunResult::Err { .. }));
1307 }
1308
1309 #[test]
1310 fn sync_child_harness_run_json() {
1311 #[derive(Serialize)]
1312 struct Input {
1313 task: String,
1314 }
1315
1316 let child = TestRunnableChild::new("worker");
1317 let mut harness = SyncChildTestHarness::new(child);
1318
1319 let result = harness.run_json(Input {
1320 task: "test".into(),
1321 });
1322 assert!(result.is_ok());
1323
1324 assert_eq!(harness.run_log()[0].input["task"], "test");
1325 }
1326
1327 #[test]
1328 fn sync_child_harness_without_time_measurement() {
1329 let child = TestRunnableChild::new("worker");
1330 let mut harness = SyncChildTestHarness::new(child).without_time_measurement();
1331
1332 harness.run(Value::Null);
1333
1334 assert!(harness.run_log()[0].elapsed_ms.is_none());
1335 }
1336
1337 #[test]
1338 fn sync_child_harness_veto() {
1339 let child = TestRunnableChild::new("worker");
1340 let mut harness = SyncChildTestHarness::new(child);
1341
1342 let response = harness.veto();
1343 assert_eq!(response, SignalResponse::Abort);
1344 assert_eq!(harness.status(), Status::Aborted);
1345 }
1346
1347 #[test]
1348 fn sync_child_harness_clear_all_logs() {
1349 let child = TestRunnableChild::new("worker");
1350 let mut harness = SyncChildTestHarness::new(child);
1351
1352 harness.run(Value::Null);
1353 harness.veto();
1354
1355 assert_eq!(harness.run_log().len(), 1);
1356 assert_eq!(harness.signal_log().len(), 1);
1357
1358 harness.clear_all_logs();
1359
1360 assert!(harness.run_log().is_empty());
1361 assert!(harness.signal_log().is_empty());
1362 }
1363
1364 #[test]
1365 fn sync_child_harness_multiple_runs() {
1366 let child = TestRunnableChild::new("worker");
1367 let mut harness = SyncChildTestHarness::new(child);
1368
1369 for i in 0..5 {
1370 harness.run(serde_json::json!({"iteration": i}));
1371 }
1372
1373 assert_eq!(harness.run_log().len(), 5);
1374 assert_eq!(harness.child().run_count, 5);
1375 }
1376
1377 use crate::AsyncRunnableChild;
1380 use async_trait::async_trait;
1381
1382 struct TestAsyncChild {
1383 id: String,
1384 status: Status,
1385 delay_ms: u64,
1386 }
1387
1388 impl TestAsyncChild {
1389 fn new(id: &str) -> Self {
1390 Self {
1391 id: id.into(),
1392 status: Status::Idle,
1393 delay_ms: 0,
1394 }
1395 }
1396
1397 fn with_delay(mut self, delay_ms: u64) -> Self {
1398 self.delay_ms = delay_ms;
1399 self
1400 }
1401 }
1402
1403 impl Identifiable for TestAsyncChild {
1404 fn id(&self) -> &str {
1405 &self.id
1406 }
1407 }
1408
1409 impl SignalReceiver for TestAsyncChild {
1410 fn on_signal(&mut self, signal: &Signal) -> SignalResponse {
1411 if signal.is_veto() {
1412 self.abort();
1413 SignalResponse::Abort
1414 } else {
1415 SignalResponse::Handled
1416 }
1417 }
1418
1419 fn abort(&mut self) {
1420 self.status = Status::Aborted;
1421 }
1422 }
1423
1424 impl Statusable for TestAsyncChild {
1425 fn status(&self) -> Status {
1426 self.status
1427 }
1428 }
1429
1430 impl Child for TestAsyncChild {}
1431
1432 #[async_trait]
1433 impl AsyncRunnableChild for TestAsyncChild {
1434 async fn run(&mut self, input: Value) -> ChildResult {
1435 self.status = Status::Running;
1436
1437 if self.delay_ms > 0 {
1438 tokio::time::sleep(Duration::from_millis(self.delay_ms)).await;
1439 }
1440
1441 self.status = Status::Idle;
1442 ChildResult::Ok(serde_json::json!({
1443 "async": true,
1444 "input": input
1445 }))
1446 }
1447 }
1448
1449 #[tokio::test]
1450 async fn async_child_harness_run() {
1451 let child = TestAsyncChild::new("async-worker");
1452 let mut harness = AsyncChildTestHarness::new(child);
1453
1454 let result = harness.run(serde_json::json!({"task": "async_test"})).await;
1455 assert!(result.is_ok());
1456
1457 assert_eq!(harness.run_log().len(), 1);
1458 assert!(harness.run_log()[0].elapsed_ms.is_some());
1459
1460 if let RunResult::Ok { value } = &harness.run_log()[0].result {
1461 assert_eq!(value["async"], true);
1462 }
1463 }
1464
1465 #[tokio::test]
1466 async fn async_child_harness_with_timeout_success() {
1467 let child = TestAsyncChild::new("async-worker").with_delay(10);
1468 let mut harness = AsyncChildTestHarness::new(child);
1469
1470 let result = harness
1471 .run_with_timeout(Value::Null, Duration::from_millis(100))
1472 .await;
1473 assert!(result.is_ok());
1474 }
1475
1476 #[tokio::test]
1477 async fn async_child_harness_with_timeout_failure() {
1478 let child = TestAsyncChild::new("async-worker").with_delay(100);
1479 let mut harness = AsyncChildTestHarness::new(child);
1480
1481 let result = harness
1482 .run_with_timeout(Value::Null, Duration::from_millis(10))
1483 .await;
1484 assert!(result.is_err());
1485
1486 let err = result.expect_err("async child with long delay should timeout");
1487 assert!(err.timeout_ms == 10);
1488 }
1489
1490 #[tokio::test]
1491 async fn async_child_harness_with_default_timeout() {
1492 let child = TestAsyncChild::new("async-worker").with_delay(100);
1493 let mut harness =
1494 AsyncChildTestHarness::new(child).with_default_timeout(Duration::from_millis(10));
1495
1496 let result = harness.run(Value::Null).await;
1498 assert!(result.is_aborted());
1499 }
1500
1501 #[tokio::test]
1502 async fn async_child_harness_veto() {
1503 let child = TestAsyncChild::new("async-worker");
1504 let mut harness = AsyncChildTestHarness::new(child);
1505
1506 let response = harness.veto();
1507 assert_eq!(response, SignalResponse::Abort);
1508 assert_eq!(harness.status(), Status::Aborted);
1509 }
1510
1511 #[tokio::test]
1512 async fn async_child_harness_clear_all_logs() {
1513 let child = TestAsyncChild::new("async-worker");
1514 let mut harness = AsyncChildTestHarness::new(child);
1515
1516 harness.run(Value::Null).await;
1517 harness.veto();
1518
1519 assert_eq!(harness.run_log().len(), 1);
1520 assert_eq!(harness.signal_log().len(), 1);
1521
1522 harness.clear_all_logs();
1523
1524 assert!(harness.run_log().is_empty());
1525 assert!(harness.signal_log().is_empty());
1526 }
1527
1528 #[test]
1531 fn run_result_serialization() {
1532 let ok_result = RunResult::Ok {
1533 value: serde_json::json!({"key": "value"}),
1534 };
1535 let json =
1536 serde_json::to_string(&ok_result).expect("RunResult::Ok should serialize to JSON");
1537 assert!(json.contains("\"type\":\"Ok\""));
1538
1539 let err_result = RunResult::Err {
1540 kind: "timeout".into(),
1541 message: "timed out".into(),
1542 };
1543 let json =
1544 serde_json::to_string(&err_result).expect("RunResult::Err should serialize to JSON");
1545 assert!(json.contains("\"type\":\"Err\""));
1546
1547 let aborted_result = RunResult::Aborted;
1548 let json = serde_json::to_string(&aborted_result)
1549 .expect("RunResult::Aborted should serialize to JSON");
1550 assert!(json.contains("\"type\":\"Aborted\""));
1551 }
1552
1553 #[test]
1554 fn run_record_serialization() {
1555 let record = RunRecord {
1556 input: serde_json::json!({"task": "test"}),
1557 result: RunResult::Ok {
1558 value: serde_json::json!({"done": true}),
1559 },
1560 elapsed_ms: Some(42),
1561 };
1562
1563 let json = serde_json::to_string(&record).expect("RunRecord should serialize to JSON");
1564 assert!(json.contains("\"elapsed_ms\":42"));
1565
1566 let restored: RunRecord =
1567 serde_json::from_str(&json).expect("RunRecord should deserialize from JSON");
1568 assert_eq!(restored.elapsed_ms, Some(42));
1569 }
1570}