Skip to content

Commit 531cac1

Browse files
Add error status code mapping tests (#755)
1 parent 2e25706 commit 531cac1

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

crates/trusted-server-core/src/error.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,164 @@ impl IntoHttpResponse for TrustedServerError {
153153
mod tests {
154154
use super::*;
155155

156+
#[test]
157+
fn status_code_returns_expected_http_status_for_each_variant() {
158+
let cases = [
159+
(
160+
TrustedServerError::BadRequest {
161+
message: String::from("missing field"),
162+
},
163+
StatusCode::BAD_REQUEST,
164+
),
165+
(
166+
TrustedServerError::Configuration {
167+
message: String::from("missing setting"),
168+
},
169+
StatusCode::INTERNAL_SERVER_ERROR,
170+
),
171+
(
172+
TrustedServerError::Auction {
173+
message: String::from("bid timeout"),
174+
},
175+
StatusCode::BAD_GATEWAY,
176+
),
177+
(
178+
TrustedServerError::Gam {
179+
message: String::from("request failed"),
180+
},
181+
StatusCode::BAD_GATEWAY,
182+
),
183+
(
184+
TrustedServerError::GdprConsent {
185+
message: String::from("missing consent string"),
186+
},
187+
StatusCode::BAD_REQUEST,
188+
),
189+
(
190+
TrustedServerError::InvalidUtf8 {
191+
message: String::from("invalid byte sequence"),
192+
},
193+
StatusCode::INTERNAL_SERVER_ERROR,
194+
),
195+
(
196+
TrustedServerError::RequestTooLarge {
197+
message: String::from("body too large"),
198+
},
199+
StatusCode::PAYLOAD_TOO_LARGE,
200+
),
201+
(
202+
TrustedServerError::InvalidHeaderValue {
203+
message: String::from("non-ascii header"),
204+
},
205+
StatusCode::BAD_REQUEST,
206+
),
207+
(
208+
TrustedServerError::KvStore {
209+
store_name: String::from("sessions"),
210+
message: String::from("timeout"),
211+
},
212+
StatusCode::SERVICE_UNAVAILABLE,
213+
),
214+
(
215+
TrustedServerError::Prebid {
216+
message: String::from("adapter error"),
217+
},
218+
StatusCode::BAD_GATEWAY,
219+
),
220+
(
221+
TrustedServerError::Integration {
222+
integration: String::from("example-integration"),
223+
message: String::from("request failed"),
224+
},
225+
StatusCode::BAD_GATEWAY,
226+
),
227+
(
228+
TrustedServerError::Proxy {
229+
message: String::from("upstream failed"),
230+
},
231+
StatusCode::BAD_GATEWAY,
232+
),
233+
(
234+
TrustedServerError::Forbidden {
235+
message: String::from("missing permission"),
236+
},
237+
StatusCode::FORBIDDEN,
238+
),
239+
(
240+
TrustedServerError::AllowlistViolation {
241+
host: String::from("example.com"),
242+
},
243+
StatusCode::FORBIDDEN,
244+
),
245+
(
246+
TrustedServerError::Settings {
247+
message: String::from("parse failed"),
248+
},
249+
StatusCode::INTERNAL_SERVER_ERROR,
250+
),
251+
(
252+
TrustedServerError::EdgeCookie {
253+
message: String::from("generation failed"),
254+
},
255+
StatusCode::INTERNAL_SERVER_ERROR,
256+
),
257+
(
258+
TrustedServerError::PartnerNotFound {
259+
partner_id: String::from("example-partner"),
260+
},
261+
StatusCode::NOT_FOUND,
262+
),
263+
(
264+
TrustedServerError::InsecureDefault {
265+
field: String::from("example.secret"),
266+
},
267+
StatusCode::INTERNAL_SERVER_ERROR,
268+
),
269+
];
270+
271+
// `mapped_status` is an exhaustive match with no `_` arm, so adding a
272+
// new `TrustedServerError` variant fails to compile here until its
273+
// status is declared — the per-variant coverage can't silently go
274+
// stale. Cross-checking it against the independent `cases` literals
275+
// above guards both encodings against drift.
276+
fn mapped_status(error: &TrustedServerError) -> StatusCode {
277+
match error {
278+
TrustedServerError::BadRequest { .. } => StatusCode::BAD_REQUEST,
279+
TrustedServerError::Configuration { .. } | TrustedServerError::Settings { .. } => {
280+
StatusCode::INTERNAL_SERVER_ERROR
281+
}
282+
TrustedServerError::Auction { .. } => StatusCode::BAD_GATEWAY,
283+
TrustedServerError::Gam { .. } => StatusCode::BAD_GATEWAY,
284+
TrustedServerError::GdprConsent { .. } => StatusCode::BAD_REQUEST,
285+
TrustedServerError::InvalidUtf8 { .. } => StatusCode::INTERNAL_SERVER_ERROR,
286+
TrustedServerError::RequestTooLarge { .. } => StatusCode::PAYLOAD_TOO_LARGE,
287+
TrustedServerError::InvalidHeaderValue { .. } => StatusCode::BAD_REQUEST,
288+
TrustedServerError::KvStore { .. } => StatusCode::SERVICE_UNAVAILABLE,
289+
TrustedServerError::Prebid { .. } => StatusCode::BAD_GATEWAY,
290+
TrustedServerError::Integration { .. } => StatusCode::BAD_GATEWAY,
291+
TrustedServerError::Proxy { .. } => StatusCode::BAD_GATEWAY,
292+
TrustedServerError::Forbidden { .. } => StatusCode::FORBIDDEN,
293+
TrustedServerError::AllowlistViolation { .. } => StatusCode::FORBIDDEN,
294+
TrustedServerError::EdgeCookie { .. } => StatusCode::INTERNAL_SERVER_ERROR,
295+
TrustedServerError::PartnerNotFound { .. } => StatusCode::NOT_FOUND,
296+
TrustedServerError::InsecureDefault { .. } => StatusCode::INTERNAL_SERVER_ERROR,
297+
}
298+
}
299+
300+
for (error, expected_status) in cases {
301+
assert_eq!(
302+
error.status_code(),
303+
expected_status,
304+
"should map {error:?} to {expected_status}",
305+
);
306+
assert_eq!(
307+
mapped_status(&error),
308+
expected_status,
309+
"exhaustive mapping should agree with the table for {error:?}",
310+
);
311+
}
312+
}
313+
156314
#[test]
157315
fn server_errors_return_generic_message() {
158316
let cases = [

0 commit comments

Comments
 (0)