Skip to main content

openlark_core/testing/
assertions.rs

1//! 类型安全的断言宏系统
2//!
3//! 提供零 unwrap() 的断言宏,确保测试失败时提供清晰的错误消息。
4
5/// 断言 Result 为 Ok,返回值
6///
7/// # 示例
8///
9/// ```rust,ignore
10/// let result: SDKResult<String> = Ok("test".to_string());
11/// let value = assert_res_ok!(result, "test_case");
12/// assert_eq!(value, "test");
13/// ```
14#[macro_export]
15macro_rules! assert_res_ok {
16    ($result:expr, $test_name:expr) => {
17        match $result {
18            Ok(value) => value,
19            Err(e) => {
20                panic!(
21                    "{}\n  ➜ 预期: Ok(_)\n  ➜ 实际: Err({})\n  ➜ 位置: {}:{}",
22                    $test_name,
23                    e,
24                    file!(),
25                    line!()
26                );
27            }
28        }
29    };
30}
31
32/// 断言 Result 为 Err,匹配错误模式并返回错误
33///
34/// # 示例
35///
36/// ```rust,ignore
37/// let result: SDKResult<()> = Err(CoreError::validation_error("field", "error"));
38/// let err = assert_res_err!(result, CoreError::Validation { .. }, "test_validation");
39/// ```
40#[macro_export]
41macro_rules! assert_res_err {
42    ($result:expr, $error_pattern:pat, $test_name:expr) => {
43        match $result {
44            Err($error_pattern) => true,
45            Ok(v) => {
46                panic!(
47                    "{}\n  ➜ 预期: Err({})\n  ➜ 实际: Ok({:?})\n  ➜ 位置: {}:{}",
48                    $test_name,
49                    stringify!($error_pattern),
50                    v,
51                    file!(),
52                    line!()
53                );
54            }
55            _ => false,
56        }
57    };
58}
59
60/// 断言 Result 为 Err,且错误消息包含指定文本
61///
62/// # 示例
63///
64/// ```rust,ignore
65/// let result: SDKResult<()> = Err(CoreError::validation_error("app_token", "不能为空"));
66/// let err = assert_err_contains!(result, "app_token", "test_empty_token");
67/// ```
68#[macro_export]
69macro_rules! assert_err_contains {
70    ($result:expr, $msg:expr, $test_name:expr) => {
71        match $result {
72            Err(e) if e.to_string().contains($msg) => e,
73            Err(e) => {
74                panic!(
75                    "{}\n  ➜ 预期错误包含: '{}'\n  ➜ 实际错误: '{}'\n  ➜ 位置: {}:{}",
76                    $test_name,
77                    $msg,
78                    e,
79                    file!(),
80                    line!()
81                );
82            }
83            Ok(v) => {
84                panic!(
85                    "{}\n  ➜ 预期: Err(_)\n  ➜ 实际: Ok({:?})\n  ➜ 位置: {}:{}",
86                    $test_name,
87                    v,
88                    file!(),
89                    line!()
90                );
91            }
92        }
93    };
94}
95
96/// 断言 Option 为 Some,返回值
97///
98/// # 示例
99///
100/// ```rust,ignore
101/// let value: Option<String> = Some("test".to_string());
102/// let inner = assert_some!(value, "test_some");
103/// assert_eq!(inner, "test");
104/// ```
105#[macro_export]
106macro_rules! assert_some {
107    ($opt:expr) => {
108        match $opt {
109            Some(v) => v,
110            None => panic!("Expected Some, got None\n  ➜ 位置: {}:{}", file!(), line!()),
111        }
112    };
113
114    ($opt:expr, $msg:expr) => {
115        match $opt {
116            Some(v) => v,
117            None => panic!("{}: got None\n  ➜ 位置: {}:{}", $msg, file!(), line!()),
118        }
119    };
120}
121
122/// 断言 Option 为 None
123///
124/// # 示例
125///
126/// ```rust,ignore
127/// let value: Option<String> = None;
128/// assert_none!(value, "test_none");
129/// ```
130#[macro_export]
131macro_rules! assert_none {
132    ($opt:expr) => {
133        match $opt {
134            None => true,
135            Some(v) => panic!(
136                "Expected None, got Some({:?})\n  ➜ 位置: {}:{}",
137                v,
138                file!(),
139                line!()
140            ),
141        }
142    };
143
144    ($opt:expr, $msg:expr) => {
145        match $opt {
146            None => true,
147            Some(v) => panic!(
148                "{}: got Some({:?})\n  ➜ 位置: {}:{}",
149                $msg,
150                v,
151                file!(),
152                line!()
153            ),
154        }
155    };
156}
157
158#[cfg(test)]
159mod tests {
160    #[test]
161    fn test_assert_res_ok_success() {
162        let result: Result<String, &str> = Ok("test".to_string());
163        let value = assert_res_ok!(result, "test_ok");
164        assert_eq!(value, "test");
165    }
166
167    #[test]
168    #[should_panic(expected = "预期: Ok(_)")]
169    fn test_assert_res_ok_panic() {
170        let result: Result<String, &str> = Err("error");
171        assert_res_ok!(result, "test_ok_panic");
172    }
173
174    #[test]
175    fn test_assert_some_success() {
176        let value: Option<String> = Some("test".to_string());
177        let inner = assert_some!(value);
178        assert_eq!(inner, "test");
179    }
180
181    #[test]
182    #[should_panic(expected = "Expected Some, got None")]
183    fn test_assert_some_panic() {
184        let value: Option<String> = None;
185        assert_some!(value);
186    }
187
188    #[test]
189    fn test_assert_none_success() {
190        let value: Option<String> = None;
191        assert_none!(value);
192    }
193
194    #[test]
195    #[should_panic(expected = "Expected None, got Some")]
196    fn test_assert_none_panic() {
197        let value: Option<String> = Some("test".to_string());
198        assert_none!(value);
199    }
200}