Skip to main content

telltale_runtime/effects/
interpreter_testing.rs

1use super::{
2    async_trait, ChoreoHandler, ChoreoResult, ChoreographyError, DeserializeOwned, RoleId,
3    Serialize,
4};
5use std::collections::VecDeque;
6
7/// A mock handler that records operations and provides scripted responses
8pub struct MockHandler<R: RoleId> {
9    /// The role this handler represents (kept for debugging/future use)
10    _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}