Skip to content

Commit 6607cb0

Browse files
committed
test(aggregator-client): add aggregatorClientError tests
imported from mithril-signer client
1 parent bfc1b82 commit 6607cb0

File tree

1 file changed

+171
-5
lines changed
  • internal/mithril-aggregator-client/src

1 file changed

+171
-5
lines changed

internal/mithril-aggregator-client/src/error.rs

Lines changed: 171 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ use crate::JSON_CONTENT_TYPE;
1010
/// Error structure for the Aggregator Client.
1111
#[derive(Error, Debug)]
1212
pub enum AggregatorClientError {
13-
/// The aggregator host has returned a technical error.
14-
#[error("remote server technical error")]
13+
/// Error raised when querying the aggregator returned a 5XX error.
14+
#[error("Internal error of the Aggregator")]
1515
RemoteServerTechnical(#[source] StdError),
1616

17-
/// The aggregator host responded it cannot fulfill our request.
18-
#[error("remote server logical error")]
17+
/// Error raised when querying the aggregator returned a 4XX error.
18+
#[error("Invalid request to the Aggregator")]
1919
RemoteServerLogical(#[source] StdError),
2020

2121
/// Could not reach aggregator.
@@ -33,6 +33,10 @@ pub enum AggregatorClientError {
3333
/// Failed to join the query endpoint to the aggregator url
3434
#[error("Invalid endpoint")]
3535
InvalidEndpoint(#[source] StdError),
36+
37+
/// No signer registration round opened yet
38+
#[error("A signer registration round is not opened yet, please try again later")]
39+
RegistrationRoundNotYetOpened(#[source] StdError),
3640
}
3741

3842
impl AggregatorClientError {
@@ -49,7 +53,10 @@ impl AggregatorClientError {
4953
Self::RemoteServerLogical(anyhow!(root_cause))
5054
} else if error_code.is_server_error() {
5155
let root_cause = Self::get_root_cause(response).await;
52-
Self::RemoteServerTechnical(anyhow!(root_cause))
56+
match error_code.as_u16() {
57+
550 => Self::RegistrationRoundNotYetOpened(anyhow!(root_cause)),
58+
_ => Self::RemoteServerTechnical(anyhow!(root_cause)),
59+
}
5360
} else {
5461
let response_text = response.text().await.unwrap_or_default();
5562
Self::UnhandledStatusCode(error_code, response_text)
@@ -87,3 +94,162 @@ impl AggregatorClientError {
8794
}
8895
}
8996
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use http::response::Builder as HttpResponseBuilder;
101+
use serde_json::json;
102+
103+
use super::*;
104+
105+
macro_rules! assert_error_text_contains {
106+
($error: expr, $expect_contains: expr) => {
107+
let error = &$error;
108+
assert!(
109+
error.contains($expect_contains),
110+
"Expected error message to contain '{}'\ngot '{error:?}'",
111+
$expect_contains,
112+
);
113+
};
114+
}
115+
116+
fn build_text_response<T: Into<String>>(status_code: StatusCode, body: T) -> Response {
117+
HttpResponseBuilder::new()
118+
.status(status_code)
119+
.body(body.into())
120+
.unwrap()
121+
.into()
122+
}
123+
124+
fn build_json_response<T: serde::Serialize>(status_code: StatusCode, body: &T) -> Response {
125+
HttpResponseBuilder::new()
126+
.status(status_code)
127+
.header(header::CONTENT_TYPE, JSON_CONTENT_TYPE)
128+
.body(serde_json::to_string(&body).unwrap())
129+
.unwrap()
130+
.into()
131+
}
132+
133+
#[tokio::test]
134+
async fn test_4xx_errors_are_handled_as_remote_server_logical() {
135+
let response = build_text_response(StatusCode::BAD_REQUEST, "error text");
136+
let handled_error = AggregatorClientError::from_response(response).await;
137+
138+
assert!(
139+
matches!(
140+
handled_error,
141+
AggregatorClientError::RemoteServerLogical(..)
142+
),
143+
"Expected error to be RemoteServerLogical\ngot '{handled_error:?}'",
144+
);
145+
}
146+
147+
#[tokio::test]
148+
async fn test_5xx_errors_are_handled_as_remote_server_technical() {
149+
let response = build_text_response(StatusCode::INTERNAL_SERVER_ERROR, "error text");
150+
let handled_error = AggregatorClientError::from_response(response).await;
151+
152+
assert!(
153+
matches!(
154+
handled_error,
155+
AggregatorClientError::RemoteServerTechnical(..)
156+
),
157+
"Expected error to be RemoteServerLogical\ngot '{handled_error:?}'",
158+
);
159+
}
160+
161+
#[tokio::test]
162+
async fn test_550_error_is_handled_as_registration_round_not_yet_opened() {
163+
let response = build_text_response(StatusCode::from_u16(550).unwrap(), "Not yet available");
164+
let handled_error = AggregatorClientError::from_response(response).await;
165+
166+
assert!(
167+
matches!(
168+
handled_error,
169+
AggregatorClientError::RegistrationRoundNotYetOpened(..)
170+
),
171+
"Expected error to be RegistrationRoundNotYetOpened\ngot '{handled_error:?}'",
172+
);
173+
}
174+
175+
#[tokio::test]
176+
async fn test_non_4xx_or_5xx_errors_are_handled_as_unhandled_status_code_and_contains_response_text()
177+
{
178+
let response = build_text_response(StatusCode::OK, "ok text");
179+
let handled_error = AggregatorClientError::from_response(response).await;
180+
181+
assert!(
182+
matches!(
183+
handled_error,
184+
AggregatorClientError::UnhandledStatusCode(..) if format!("{handled_error:?}").contains("ok text")
185+
),
186+
"Expected error to be UnhandledStatusCode with 'ok text' in error text\ngot '{handled_error:?}'",
187+
);
188+
}
189+
190+
#[tokio::test]
191+
async fn test_root_cause_of_non_json_response_contains_response_plain_text() {
192+
let error_text = "An error occurred; please try again later.";
193+
let response = build_text_response(StatusCode::EXPECTATION_FAILED, error_text);
194+
195+
assert_error_text_contains!(
196+
AggregatorClientError::get_root_cause(response).await,
197+
"expectation failed: An error occurred; please try again later."
198+
);
199+
}
200+
201+
#[tokio::test]
202+
async fn test_root_cause_of_json_formatted_client_error_response_contains_error_label_and_message()
203+
{
204+
let client_error = ClientError::new("label", "message");
205+
let response = build_json_response(StatusCode::BAD_REQUEST, &client_error);
206+
207+
assert_error_text_contains!(
208+
AggregatorClientError::get_root_cause(response).await,
209+
"bad request: label: message"
210+
);
211+
}
212+
213+
#[tokio::test]
214+
async fn test_root_cause_of_json_formatted_server_error_response_contains_error_label_and_message()
215+
{
216+
let server_error = ServerError::new("message");
217+
let response = build_json_response(StatusCode::BAD_REQUEST, &server_error);
218+
219+
assert_error_text_contains!(
220+
AggregatorClientError::get_root_cause(response).await,
221+
"bad request: message"
222+
);
223+
}
224+
225+
#[tokio::test]
226+
async fn test_root_cause_of_unknown_formatted_json_response_contains_json_key_value_pairs() {
227+
let response = build_json_response(
228+
StatusCode::INTERNAL_SERVER_ERROR,
229+
&json!({ "second": "unknown", "first": "foreign" }),
230+
);
231+
232+
assert_error_text_contains!(
233+
AggregatorClientError::get_root_cause(response).await,
234+
r#"internal server error: {"first":"foreign","second":"unknown"}"#
235+
);
236+
}
237+
238+
#[tokio::test]
239+
async fn test_root_cause_with_invalid_json_response_still_contains_response_status_name() {
240+
let response = HttpResponseBuilder::new()
241+
.status(StatusCode::BAD_REQUEST)
242+
.header(header::CONTENT_TYPE, JSON_CONTENT_TYPE)
243+
.body(r#"{"invalid":"unexpected dot", "key": "value".}"#)
244+
.unwrap()
245+
.into();
246+
247+
let root_cause = AggregatorClientError::get_root_cause(response).await;
248+
249+
assert_error_text_contains!(root_cause, "bad request");
250+
assert!(
251+
!root_cause.contains("bad request: "),
252+
"Expected error message should not contain additional information \ngot '{root_cause:?}'"
253+
);
254+
}
255+
}

0 commit comments

Comments
 (0)