mithril_common/test_utils/
mod.rs

1//! Test utilities
2//!
3//! They contains:
4//! * A Open Api Spec tester
5//! * Some precomputed fake data and keys
6//! * A builder of [MithrilFixture] to generate signers alongside a stake distribution
7//!
8
9#[cfg(feature = "apispec")]
10#[cfg_attr(docsrs, doc(cfg(feature = "apispec")))]
11pub mod apispec;
12
13pub mod fake_data;
14pub mod fake_keys;
15
16mod cardano_transactions_builder;
17mod certificate_chain_builder;
18mod dir_eq;
19mod fixture_builder;
20mod mithril_fixture;
21mod precomputed_kes_key;
22mod temp_dir;
23
24#[cfg(feature = "test_http_server")]
25#[cfg_attr(docsrs, doc(cfg(feature = "test_http_server")))]
26pub mod test_http_server;
27
28pub use cardano_transactions_builder::CardanoTransactionsBuilder;
29pub use certificate_chain_builder::{
30    CertificateChainBuilder, CertificateChainBuilderContext, CertificateChainingMethod,
31};
32pub use dir_eq::*;
33pub use fixture_builder::{MithrilFixtureBuilder, StakeDistributionGenerationMethod};
34pub use mithril_fixture::{MithrilFixture, SignerFixture};
35pub use temp_dir::*;
36#[cfg(test)]
37pub(crate) use utils::*;
38
39/// Compare two json strings ignoring keys order
40#[macro_export]
41macro_rules! assert_same_json {
42    ( $expected:expr, $actual:expr ) => {
43        assert_eq!(
44            serde_json::from_str::<serde_json::Value>($expected).unwrap(),
45            serde_json::from_str::<serde_json::Value>($actual).unwrap()
46        )
47    };
48}
49pub use assert_same_json;
50
51/// Compare two iterators ignoring the order
52pub fn equivalent_to<T, I1, I2>(a: I1, b: I2) -> bool
53where
54    T: PartialEq + Ord,
55    I1: IntoIterator<Item = T> + Clone,
56    I2: IntoIterator<Item = T> + Clone,
57{
58    let a = as_sorted_vec(a);
59    let b = as_sorted_vec(b);
60    a == b
61}
62
63/// Assert that two iterators are equivalent
64pub fn assert_equivalent<T, I1, I2>(a: I1, b: I2)
65where
66    T: PartialEq + Ord + std::fmt::Debug,
67    I1: IntoIterator<Item = T> + Clone,
68    I2: IntoIterator<Item = T> + Clone,
69{
70    let a = as_sorted_vec(a);
71    let b = as_sorted_vec(b);
72    assert_eq!(a, b);
73}
74
75fn as_sorted_vec<T: Ord, I: IntoIterator<Item = T> + Clone>(iter: I) -> Vec<T> {
76    let mut list: Vec<T> = iter.clone().into_iter().collect();
77    list.sort();
78    list
79}
80
81/// Return the path of the given function.
82/// If the last function is `f`, it is removed.
83/// The last `{{closure}}` is also removed.
84pub fn format_current_function_module<T>(f: T) -> &'static str {
85    fn type_name_of<T>(_: T) -> &'static str {
86        std::any::type_name::<T>()
87    }
88
89    let name = type_name_of(f);
90    let name = name.strip_suffix("::f").unwrap_or(name);
91    name.strip_suffix("::{{closure}}").unwrap_or(name)
92}
93
94/// Return a string representing the path of the given function.
95pub fn format_current_function_path<T>(f: T) -> String {
96    let name = format_current_function_module(f);
97    name.replace("::", "/")
98}
99
100/// Returns the name of the function that called this macro.
101#[macro_export]
102macro_rules! current_function {
103    () => {{
104        fn f() {}
105        let name = $crate::test_utils::format_current_function_module(f);
106        // The index found is the beginning of the '..', this is why we add 2.
107        let function_name_index = name.rfind("::").map(|index| index + 2).unwrap_or(0);
108
109        &name[function_name_index..]
110    }};
111}
112pub use current_function;
113
114/// Returns the path of the function that called this macro.
115#[macro_export]
116macro_rules! current_function_path {
117    () => {{
118        fn f() {}
119
120        std::path::PathBuf::from($crate::test_utils::format_current_function_path(f))
121    }};
122}
123pub use current_function_path;
124
125#[cfg(test)]
126mod utils {
127    use std::fs::File;
128    use std::io;
129    use std::sync::Arc;
130    use std::{collections::HashSet, path::Path};
131
132    use slog::{Drain, Logger};
133    use slog_async::Async;
134    use slog_term::{CompactFormat, PlainDecorator};
135
136    use super::*;
137
138    pub struct TestLogger;
139
140    #[cfg(test)]
141    impl TestLogger {
142        fn from_writer<W: io::Write + Send + 'static>(writer: W) -> Logger {
143            let decorator = PlainDecorator::new(writer);
144            let drain = CompactFormat::new(decorator).build().fuse();
145            let drain = Async::new(drain).build().fuse();
146            Logger::root(Arc::new(drain), slog::o!())
147        }
148
149        pub fn stdout() -> Logger {
150            Self::from_writer(slog_term::TestStdoutWriter)
151        }
152
153        pub fn file(filepath: &std::path::Path) -> Logger {
154            Self::from_writer(File::create(filepath).unwrap())
155        }
156    }
157
158    #[test]
159    fn test_equivalent_to() {
160        assert!(equivalent_to(vec![1, 2, 3], vec![3, 2, 1]));
161        assert!(equivalent_to(vec![1, 2, 3], vec![2, 1, 3]));
162        assert!(!equivalent_to(vec![1, 2, 3], vec![3, 2, 1, 4]));
163        assert!(!equivalent_to(vec![1, 2, 3], vec![3, 2]));
164
165        assert!(equivalent_to([1, 2, 3], vec![3, 2, 1]));
166        assert!(equivalent_to(&[1, 2, 3], &vec![3, 2, 1]));
167        assert!(equivalent_to([1, 2, 3], HashSet::from([3, 2, 1])));
168        assert!(equivalent_to(vec![1, 2, 3], HashSet::from([3, 2, 1])));
169        assert!(equivalent_to(&vec![1, 2, 3], &HashSet::from([3, 2, 1])));
170
171        assert_equivalent(vec![1, 2, 3], vec![3, 2, 1]);
172        assert_equivalent(vec![1, 2, 3], vec![2, 1, 3]);
173
174        assert_equivalent([1, 2, 3], vec![3, 2, 1]);
175        assert_equivalent(&[1, 2, 3], &vec![3, 2, 1]);
176        assert_equivalent([1, 2, 3], HashSet::from([3, 2, 1]));
177        assert_equivalent(vec![1, 2, 3], HashSet::from([3, 2, 1]));
178        assert_equivalent(&vec![1, 2, 3], &HashSet::from([3, 2, 1]));
179    }
180
181    #[test]
182    fn test_current_function_extract_function_name() {
183        let name = current_function!();
184
185        assert_eq!("test_current_function_extract_function_name", name);
186    }
187
188    #[tokio::test]
189    async fn test_current_function_extract_async_function_name() {
190        let name = current_function!();
191
192        assert_eq!("test_current_function_extract_async_function_name", name);
193    }
194
195    #[test]
196    fn test_format_function_path_from_given_function() {
197        assert_eq!(
198            "mithril_common/test_utils/utils/test_format_function_path_from_given_function",
199            format_current_function_path(test_format_function_path_from_given_function)
200        );
201    }
202
203    #[test]
204    fn test_format_function_path_from_given_pseudo_function_f() {
205        fn f() {}
206        assert_eq!(
207            "mithril_common/test_utils/utils/test_format_function_path_from_given_pseudo_function_f",
208            format_current_function_path(f)
209        );
210    }
211
212    #[tokio::test]
213    async fn test_format_function_path_from_given_async_function_f() {
214        fn f() {}
215        assert_eq!(
216            "mithril_common/test_utils/utils/test_format_function_path_from_given_async_function_f",
217            format_current_function_path(f)
218        );
219    }
220
221    #[test]
222    fn test_build_current_function_path_using_macros() {
223        assert_eq!(
224            Path::new("mithril_common")
225                .join("test_utils")
226                .join("utils")
227                .join("test_build_current_function_path_using_macros"),
228            current_function_path!()
229        );
230    }
231
232    #[tokio::test]
233    async fn test_build_current_async_function_path_using_macros() {
234        assert_eq!(
235            Path::new("mithril_common")
236                .join("test_utils")
237                .join("utils")
238                .join("test_build_current_async_function_path_using_macros"),
239            current_function_path!()
240        );
241    }
242}