xenith_core/testing.rs
1/// Generates the standard transport compliance test suite for any
2/// [`crate::MessagingTransport`] implementation.
3///
4/// Invoke this macro inside a `#[cfg(test)] mod tests { ... }` block,
5/// passing an expression that constructs the transport under test.
6/// The constructed transport **must** include [`crate::ChainId`]`(1)` in its
7/// set of supported chains; all compliance tests use chain 1 as the canonical
8/// supported destination.
9///
10/// # Example
11///
12/// ```rust,ignore
13/// #[cfg(test)]
14/// mod tests {
15/// use super::*;
16/// use xenith_core::transport_compliance_tests;
17///
18/// transport_compliance_tests!(MyTransport::new(
19/// [0u8; 20],
20/// vec![(xenith_core::ChainId(1), 101u32)],
21/// ));
22/// }
23/// ```
24///
25/// # Generated tests
26///
27/// | Test name | What it checks |
28/// |---|---|
29/// | `compliance_send_to_supported_chain` | `send_message` to ChainId(1) returns `Ok` |
30/// | `compliance_send_to_unsupported_chain` | `send_message` to ChainId(999_999) returns `UnsupportedChain` |
31/// | `compliance_estimate_fee_returns_u128` | `estimate_fee` returns `Ok(fee)` with `fee > 0` |
32/// | `compliance_message_status_is_valid_variant` | `message_status` returns a recognised variant |
33/// | `compliance_transport_is_send_sync` | The transport type satisfies `Send + Sync` |
34#[macro_export]
35macro_rules! transport_compliance_tests {
36 ($constructor:expr) => {
37 #[tokio::test]
38 async fn compliance_send_to_supported_chain() {
39 let t = $constructor;
40 let result = t
41 .send_message(
42 $crate::ChainId(1),
43 ::bytes::Bytes::new(),
44 $crate::SendOptions::default(),
45 )
46 .await;
47 assert!(
48 result.is_ok(),
49 "send_message to a supported chain must return Ok(MessageId), got {result:?}"
50 );
51 }
52
53 #[tokio::test]
54 async fn compliance_send_to_unsupported_chain() {
55 let t = $constructor;
56 let result = t
57 .send_message(
58 $crate::ChainId(999_999),
59 ::bytes::Bytes::new(),
60 $crate::SendOptions::default(),
61 )
62 .await;
63 assert!(
64 matches!(result, Err($crate::XenithError::UnsupportedChain(_))),
65 "send_message to ChainId(999_999) must return Err(UnsupportedChain), got {result:?}"
66 );
67 }
68
69 #[tokio::test]
70 async fn compliance_estimate_fee_returns_u128() {
71 let t = $constructor;
72 let fee = t
73 .estimate_fee($crate::ChainId(1), ::bytes::Bytes::new())
74 .await
75 .expect("estimate_fee must succeed for a supported chain");
76 assert!(fee > 0, "estimate_fee must return a non-zero value, got {fee}");
77 }
78
79 #[tokio::test]
80 async fn compliance_message_status_is_valid_variant() {
81 let t = $constructor;
82 let id = t
83 .send_message(
84 $crate::ChainId(1),
85 ::bytes::Bytes::new(),
86 $crate::SendOptions::default(),
87 )
88 .await
89 .expect("send_message must succeed before checking message_status");
90 let status = t
91 .message_status(id)
92 .await
93 .expect("message_status must return Ok");
94 assert!(
95 matches!(
96 status,
97 $crate::MessageStatus::Pending
98 | $crate::MessageStatus::InFlight
99 | $crate::MessageStatus::Delivered
100 | $crate::MessageStatus::Failed { .. }
101 ),
102 "message_status returned an unexpected variant: {status:?}"
103 );
104 }
105
106 #[test]
107 fn compliance_transport_is_send_sync() {
108 fn assert_send_sync<T: Send + Sync>(_: &T) {}
109 let t = $constructor;
110 assert_send_sync(&t);
111 }
112
113 #[test]
114 fn compliance_sender_address_is_option() {
115 let t = $constructor;
116 // sender_address() must compile and return Some or None — both are valid.
117 let _addr: Option<[u8; 20]> = t.sender_address();
118 }
119
120 #[tokio::test]
121 async fn compliance_poll_incoming_returns_vec() {
122 let t = $constructor;
123 let result = t.poll_incoming().await;
124 assert!(
125 result.is_ok(),
126 "poll_incoming must return Ok(vec), got {result:?}"
127 );
128 }
129 };
130}