1use http::StatusCode;
4
5pub use modkit_errors::problem::{
6 APPLICATION_PROBLEM_JSON, Problem, ValidationError, ValidationErrorResponse,
7 ValidationViolation,
8};
9
10pub fn bad_request(detail: impl Into<String>) -> Problem {
12 Problem::new(StatusCode::BAD_REQUEST, "Bad Request", detail)
13}
14
15pub fn not_found(detail: impl Into<String>) -> Problem {
16 Problem::new(StatusCode::NOT_FOUND, "Not Found", detail)
17}
18
19pub fn conflict(detail: impl Into<String>) -> Problem {
20 Problem::new(StatusCode::CONFLICT, "Conflict", detail)
21}
22
23pub fn internal_error(detail: impl Into<String>) -> Problem {
24 Problem::new(
25 StatusCode::INTERNAL_SERVER_ERROR,
26 "Internal Server Error",
27 detail,
28 )
29}
30
31#[cfg(test)]
32#[cfg_attr(coverage_nightly, coverage(off))]
33mod tests {
34 use super::*;
35 use axum::response::IntoResponse;
36
37 #[test]
38 fn problem_into_response_sets_status_and_content_type() {
39 use axum::http::StatusCode;
40
41 let p = Problem::new(StatusCode::BAD_REQUEST, "Bad Request", "invalid payload");
42 let resp = p.into_response();
43 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
44 let ct = resp
45 .headers()
46 .get(axum::http::header::CONTENT_TYPE)
47 .and_then(|v| v.to_str().ok())
48 .unwrap_or("");
49 assert_eq!(ct, APPLICATION_PROBLEM_JSON);
50 }
51
52 #[test]
53 fn problem_builder_pattern() {
54 use http::StatusCode;
55
56 let p = Problem::new(
57 StatusCode::UNPROCESSABLE_ENTITY,
58 "Validation Failed",
59 "Input validation errors",
60 )
61 .with_code("VALIDATION_ERROR")
62 .with_instance("/users/123")
63 .with_trace_id("req-456")
64 .with_errors(vec![ValidationViolation {
65 message: "Email is required".to_owned(),
66 field: "email".to_owned(),
67 code: None,
68 }]);
69
70 assert_eq!(p.status, StatusCode::UNPROCESSABLE_ENTITY);
71 assert_eq!(p.code, "VALIDATION_ERROR");
72 assert_eq!(p.instance, "/users/123");
73 assert_eq!(p.trace_id, Some("req-456".to_owned()));
74 assert!(p.errors.is_some());
75 assert_eq!(p.errors.as_ref().unwrap().len(), 1);
76 }
77
78 #[test]
79 fn convenience_constructors() {
80 use http::StatusCode;
81
82 let bad_req = bad_request("Invalid input");
83 assert_eq!(bad_req.status, StatusCode::BAD_REQUEST);
84 assert_eq!(bad_req.title, "Bad Request");
85
86 let not_found_resp = not_found("User not found");
87 assert_eq!(not_found_resp.status, StatusCode::NOT_FOUND);
88 assert_eq!(not_found_resp.title, "Not Found");
89
90 let conflict_resp = conflict("Email already exists");
91 assert_eq!(conflict_resp.status, StatusCode::CONFLICT);
92 assert_eq!(conflict_resp.title, "Conflict");
93
94 let internal_resp = internal_error("Database connection failed");
95 assert_eq!(internal_resp.status, StatusCode::INTERNAL_SERVER_ERROR);
96 assert_eq!(internal_resp.title, "Internal Server Error");
97 }
98}