From 543db03dae94788eab055708a196b0851d066266 Mon Sep 17 00:00:00 2001 From: Mathieu Tricoire Date: Fri, 5 Dec 2025 19:38:34 +0100 Subject: [PATCH 1/3] fix(types): Serialize FieldViolation reason and localized_message --- .../richer_error/std_messages/bad_request.rs | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/tonic-types/src/richer_error/std_messages/bad_request.rs b/tonic-types/src/richer_error/std_messages/bad_request.rs index 078d09cdf..e6dcdb63a 100644 --- a/tonic-types/src/richer_error/std_messages/bad_request.rs +++ b/tonic-types/src/richer_error/std_messages/bad_request.rs @@ -53,7 +53,8 @@ impl From for pb::bad_request::FieldViolation { pb::bad_request::FieldViolation { field: value.field, description: value.description, - ..Default::default() + reason: value.reason, + localized_message: value.localized_message.map(Into::into), } } } @@ -159,7 +160,7 @@ impl From for pb::BadRequest { #[cfg(test)] mod tests { use super::super::super::{FromAny, IntoAny}; - use super::BadRequest; + use super::{BadRequest, FieldViolation, LocalizedMessage}; #[test] fn gen_bad_request() { @@ -218,4 +219,51 @@ mod tests { "BadRequest from Any differs from expected result" ); } + + #[test] + fn gen_bad_request_with_verbose_field_violation() { + let field_violations = vec![FieldViolation { + field: "field".to_string(), + description: "description".to_string(), + reason: "REASON".to_string(), + localized_message: Some(LocalizedMessage::new("en-US", "localized error")), + }]; + + let br_details = BadRequest::new(field_violations); + let formatted = format!("{:?}", br_details); + + let expected_filled = "BadRequest { field_violations: [FieldViolation { field: \"field\", description: \"description\", reason: \"REASON\", localized_message: Some(LocalizedMessage { locale: \"en-US\", message: \"localized error\" }) }] }"; + + assert!( + formatted.eq(expected_filled), + "filled BadRequest differs from expected result" + ); + + assert!( + !br_details.is_empty(), + "filled BadRequest returns 'true' from .is_empty()" + ); + + let gen_any = br_details.into_any(); + let formatted = format!("{:?}", gen_any); + + let expected = "Any { type_url: \"type.googleapis.com/google.rpc.BadRequest\", value: [10, 54, 10, 5, 102, 105, 101, 108, 100, 18, 11, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 26, 6, 82, 69, 65, 83, 79, 78, 34, 24, 10, 5, 101, 110, 45, 85, 83, 18, 15, 108, 111, 99, 97, 108, 105, 122, 101, 100, 32, 101, 114, 114, 111, 114] }"; + + assert!( + formatted.eq(expected), + "Any from filled BadRequest differs from expected result" + ); + + let br_details = match BadRequest::from_any(gen_any) { + Err(error) => panic!("Error generating BadRequest from Any: {:?}", error), + Ok(from_any) => from_any, + }; + + let formatted = format!("{:?}", br_details); + + assert!( + formatted.eq(expected_filled), + "BadRequest from Any differs from expected result" + ); + } } From b52cbc15940a431d39ef2df2468193eefa4e1e33 Mon Sep 17 00:00:00 2001 From: Mathieu Tricoire Date: Fri, 5 Dec 2025 21:00:04 +0100 Subject: [PATCH 2/3] chore(types): fix clippy issues --- tonic-types/src/richer_error/std_messages/bad_request.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tonic-types/src/richer_error/std_messages/bad_request.rs b/tonic-types/src/richer_error/std_messages/bad_request.rs index e6dcdb63a..e791733b9 100644 --- a/tonic-types/src/richer_error/std_messages/bad_request.rs +++ b/tonic-types/src/richer_error/std_messages/bad_request.rs @@ -230,7 +230,7 @@ mod tests { }]; let br_details = BadRequest::new(field_violations); - let formatted = format!("{:?}", br_details); + let formatted = format!("{br_details:?}"); let expected_filled = "BadRequest { field_violations: [FieldViolation { field: \"field\", description: \"description\", reason: \"REASON\", localized_message: Some(LocalizedMessage { locale: \"en-US\", message: \"localized error\" }) }] }"; @@ -245,7 +245,7 @@ mod tests { ); let gen_any = br_details.into_any(); - let formatted = format!("{:?}", gen_any); + let formatted = format!("{gen_any:?}"); let expected = "Any { type_url: \"type.googleapis.com/google.rpc.BadRequest\", value: [10, 54, 10, 5, 102, 105, 101, 108, 100, 18, 11, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 26, 6, 82, 69, 65, 83, 79, 78, 34, 24, 10, 5, 101, 110, 45, 85, 83, 18, 15, 108, 111, 99, 97, 108, 105, 122, 101, 100, 32, 101, 114, 114, 111, 114] }"; @@ -255,11 +255,11 @@ mod tests { ); let br_details = match BadRequest::from_any(gen_any) { - Err(error) => panic!("Error generating BadRequest from Any: {:?}", error), + Err(error) => panic!("Error generating BadRequest from Any: {error:?}"), Ok(from_any) => from_any, }; - let formatted = format!("{:?}", br_details); + let formatted = format!("{br_details:?}"); assert!( formatted.eq(expected_filled), From 0d75d59aefcb911b4d481f12b950d63ddc9a6760 Mon Sep 17 00:00:00 2001 From: Mathieu Tricoire Date: Mon, 8 Dec 2025 19:46:22 +0100 Subject: [PATCH 3/3] test(types): extend BadRequest test to cover verbose FieldViolation --- .../richer_error/std_messages/bad_request.rs | 64 ++++--------------- 1 file changed, 11 insertions(+), 53 deletions(-) diff --git a/tonic-types/src/richer_error/std_messages/bad_request.rs b/tonic-types/src/richer_error/std_messages/bad_request.rs index e791733b9..78923c87f 100644 --- a/tonic-types/src/richer_error/std_messages/bad_request.rs +++ b/tonic-types/src/richer_error/std_messages/bad_request.rs @@ -164,8 +164,8 @@ mod tests { #[test] fn gen_bad_request() { - let mut br_details = BadRequest::new(Vec::new()); - let formatted = format!("{br_details:?}"); + let empty_br_details = BadRequest::new(Vec::new()); + let formatted = format!("{empty_br_details:?}"); let expected = "BadRequest { field_violations: [] }"; @@ -175,64 +175,22 @@ mod tests { ); assert!( - br_details.is_empty(), + empty_br_details.is_empty(), "empty BadRequest returns 'false' from .is_empty()" ); - br_details - .add_violation("field_a", "description_a") - .add_violation("field_b", "description_b"); - - let formatted = format!("{br_details:?}"); - - let expected_filled = "BadRequest { field_violations: [FieldViolation { field: \"field_a\", description: \"description_a\", reason: \"\", localized_message: None }, FieldViolation { field: \"field_b\", description: \"description_b\", reason: \"\", localized_message: None }] }"; - - assert!( - formatted.eq(expected_filled), - "filled BadRequest differs from expected result" - ); - - assert!( - !br_details.is_empty(), - "filled BadRequest returns 'true' from .is_empty()" - ); - - let gen_any = br_details.into_any(); - let formatted = format!("{gen_any:?}"); - - let expected = "Any { type_url: \"type.googleapis.com/google.rpc.BadRequest\", value: [10, 24, 10, 7, 102, 105, 101, 108, 100, 95, 97, 18, 13, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 97, 10, 24, 10, 7, 102, 105, 101, 108, 100, 95, 98, 18, 13, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 98] }"; - - assert!( - formatted.eq(expected), - "Any from filled BadRequest differs from expected result" - ); - - let br_details = match BadRequest::from_any(gen_any) { - Err(error) => panic!("Error generating BadRequest from Any: {error:?}"), - Ok(from_any) => from_any, - }; - - let formatted = format!("{br_details:?}"); - - assert!( - formatted.eq(expected_filled), - "BadRequest from Any differs from expected result" - ); - } - - #[test] - fn gen_bad_request_with_verbose_field_violation() { - let field_violations = vec![FieldViolation { - field: "field".to_string(), - description: "description".to_string(), + let mut br_details = BadRequest::new(vec![FieldViolation { + field: "field_a".to_string(), + description: "description_a".to_string(), reason: "REASON".to_string(), localized_message: Some(LocalizedMessage::new("en-US", "localized error")), - }]; + }]); + + br_details.add_violation("field_b", "description_b"); - let br_details = BadRequest::new(field_violations); let formatted = format!("{br_details:?}"); - let expected_filled = "BadRequest { field_violations: [FieldViolation { field: \"field\", description: \"description\", reason: \"REASON\", localized_message: Some(LocalizedMessage { locale: \"en-US\", message: \"localized error\" }) }] }"; + let expected_filled = "BadRequest { field_violations: [FieldViolation { field: \"field_a\", description: \"description_a\", reason: \"REASON\", localized_message: Some(LocalizedMessage { locale: \"en-US\", message: \"localized error\" }) }, FieldViolation { field: \"field_b\", description: \"description_b\", reason: \"\", localized_message: None }] }"; assert!( formatted.eq(expected_filled), @@ -247,7 +205,7 @@ mod tests { let gen_any = br_details.into_any(); let formatted = format!("{gen_any:?}"); - let expected = "Any { type_url: \"type.googleapis.com/google.rpc.BadRequest\", value: [10, 54, 10, 5, 102, 105, 101, 108, 100, 18, 11, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 26, 6, 82, 69, 65, 83, 79, 78, 34, 24, 10, 5, 101, 110, 45, 85, 83, 18, 15, 108, 111, 99, 97, 108, 105, 122, 101, 100, 32, 101, 114, 114, 111, 114] }"; + let expected = "Any { type_url: \"type.googleapis.com/google.rpc.BadRequest\", value: [10, 58, 10, 7, 102, 105, 101, 108, 100, 95, 97, 18, 13, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 97, 26, 6, 82, 69, 65, 83, 79, 78, 34, 24, 10, 5, 101, 110, 45, 85, 83, 18, 15, 108, 111, 99, 97, 108, 105, 122, 101, 100, 32, 101, 114, 114, 111, 114, 10, 24, 10, 7, 102, 105, 101, 108, 100, 95, 98, 18, 13, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 95, 98] }"; assert!( formatted.eq(expected),