telltale_runtime/effects/
interpreter_testing.rs1use super::{
2 async_trait, ChoreoHandler, ChoreoResult, ChoreographyError, DeserializeOwned, RoleId,
3 Serialize,
4};
5use std::collections::VecDeque;
6
7pub struct MockHandler<R: RoleId> {
9 _role: R,
11 recorded_operations: Vec<MockOperation<R>>,
12 scripted_responses: VecDeque<MockResponse<<R as RoleId>::Label>>,
13}
14
15#[derive(Debug, Clone, PartialEq)]
16pub enum MockOperation<R: RoleId> {
17 Send { to: R, msg_type: String },
18 Recv { from: R },
19 Choose { at: R, label: <R as RoleId>::Label },
20 Offer { from: R },
21}
22
23#[derive(Debug, Clone)]
24pub enum MockResponse<L> {
25 Message(Vec<u8>),
26 Label(L),
27 Error(String),
28}
29
30impl<R: RoleId> MockHandler<R> {
31 pub fn new(role: R) -> Self {
32 Self {
33 _role: role,
34 recorded_operations: Vec::new(),
35 scripted_responses: VecDeque::new(),
36 }
37 }
38
39 pub fn add_response(&mut self, response: MockResponse<<R as RoleId>::Label>) {
40 self.scripted_responses.push_back(response);
41 }
42
43 pub fn operations(&self) -> &[MockOperation<R>] {
44 &self.recorded_operations
45 }
46
47 pub fn clear_operations(&mut self) {
48 self.recorded_operations.clear();
49 }
50}
51
52#[async_trait]
53impl<R: RoleId + 'static> ChoreoHandler for MockHandler<R> {
54 type Role = R;
55 type Endpoint = ();
56
57 async fn send<M: Serialize + Send + Sync>(
58 &mut self,
59 _ep: &mut Self::Endpoint,
60 to: Self::Role,
61 _msg: &M,
62 ) -> ChoreoResult<()> {
63 self.recorded_operations.push(MockOperation::Send {
64 to,
65 msg_type: std::any::type_name::<M>().to_string(),
66 });
67 Ok(())
68 }
69
70 async fn recv<M: DeserializeOwned + Send>(
71 &mut self,
72 _ep: &mut Self::Endpoint,
73 from: Self::Role,
74 ) -> ChoreoResult<M> {
75 self.recorded_operations.push(MockOperation::Recv { from });
76
77 if let Some(MockResponse::Message(bytes)) = self.scripted_responses.pop_front() {
78 bincode::deserialize(&bytes)
79 .map_err(|e| ChoreographyError::Serialization(e.to_string()))
80 } else {
81 Err(ChoreographyError::Transport(
82 "No scripted response available".into(),
83 ))
84 }
85 }
86
87 async fn choose(
88 &mut self,
89 _ep: &mut Self::Endpoint,
90 at: Self::Role,
91 label: <Self::Role as RoleId>::Label,
92 ) -> ChoreoResult<()> {
93 self.recorded_operations
94 .push(MockOperation::Choose { at, label });
95 Ok(())
96 }
97
98 async fn offer(
99 &mut self,
100 _ep: &mut Self::Endpoint,
101 from: Self::Role,
102 ) -> ChoreoResult<<Self::Role as RoleId>::Label> {
103 self.recorded_operations.push(MockOperation::Offer { from });
104
105 if let Some(MockResponse::Label(label)) = self.scripted_responses.pop_front() {
106 Ok(label)
107 } else {
108 Err(ChoreographyError::Transport(
109 "No scripted label available".into(),
110 ))
111 }
112 }
113
114 async fn with_timeout<F, T>(
115 &mut self,
116 _ep: &mut Self::Endpoint,
117 _at: Self::Role,
118 _dur: std::time::Duration,
119 body: F,
120 ) -> ChoreoResult<T>
121 where
122 F: std::future::Future<Output = ChoreoResult<T>> + Send,
123 {
124 body.await
125 }
126}