klauthed_testing/
assertions.rs1use klauthed_error::{DomainError, ErrorCategory};
36
37#[track_caller]
42pub fn assert_category<E: DomainError + ?Sized>(err: &E, expected: ErrorCategory) {
43 let actual = err.category();
44 assert!(
45 actual == expected,
46 "expected category {expected:?}, got {actual:?} (code: {}, error: {err})",
47 err.code()
48 );
49}
50
51#[track_caller]
56pub fn assert_code<E: DomainError + ?Sized>(err: &E, expected: &str) {
57 let actual = err.code();
58 assert!(
59 actual.as_str() == expected,
60 "expected code '{expected}', got '{actual}' (category: {:?}, error: {err})",
61 err.category()
62 );
63}
64
65#[track_caller]
70pub fn assert_http_status<E: DomainError + ?Sized>(err: &E, expected: u16) {
71 let actual = err.http_status();
72 assert!(
73 actual == expected,
74 "expected HTTP status {expected}, got {actual} (code: {}, error: {err})",
75 err.code()
76 );
77}
78
79#[track_caller]
84pub fn assert_retryable<E: DomainError + ?Sized>(err: &E, expected: bool) {
85 let actual = err.is_retryable();
86 assert!(
87 actual == expected,
88 "expected is_retryable = {expected}, got {actual} (code: {}, error: {err})",
89 err.code()
90 );
91}
92
93pub trait DomainErrorExt: DomainError {
95 #[track_caller]
97 fn assert_category(&self, expected: ErrorCategory) -> &Self {
98 assert_category(self, expected);
99 self
100 }
101
102 #[track_caller]
104 fn assert_code(&self, expected: &str) -> &Self {
105 assert_code(self, expected);
106 self
107 }
108
109 #[track_caller]
111 fn assert_http_status(&self, expected: u16) -> &Self {
112 assert_http_status(self, expected);
113 self
114 }
115
116 #[track_caller]
118 fn assert_retryable(&self, expected: bool) -> &Self {
119 assert_retryable(self, expected);
120 self
121 }
122}
123
124impl<E: DomainError + ?Sized> DomainErrorExt for E {}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use klauthed_error::ErrorCode;
130
131 #[derive(Debug)]
132 struct Sample;
133 impl std::fmt::Display for Sample {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 f.write_str("sample error")
136 }
137 }
138 impl std::error::Error for Sample {}
139 impl DomainError for Sample {
140 fn category(&self) -> ErrorCategory {
141 ErrorCategory::Unavailable
142 }
143 fn code(&self) -> ErrorCode {
144 ErrorCode::new("sample.down")
145 }
146 }
147
148 #[test]
149 fn free_functions_pass_on_match() {
150 let err = Sample;
151 assert_category(&err, ErrorCategory::Unavailable);
152 assert_code(&err, "sample.down");
153 assert_http_status(&err, 503);
154 assert_retryable(&err, true);
155 }
156
157 #[test]
158 fn extension_trait_chains() {
159 Sample
160 .assert_category(ErrorCategory::Unavailable)
161 .assert_code("sample.down")
162 .assert_http_status(503)
163 .assert_retryable(true);
164 }
165
166 #[test]
167 #[should_panic(expected = "expected category")]
168 fn category_mismatch_panics() {
169 assert_category(&Sample, ErrorCategory::NotFound);
170 }
171
172 #[test]
173 #[should_panic(expected = "expected code")]
174 fn code_mismatch_panics() {
175 assert_code(&Sample, "sample.up");
176 }
177}