mithril_aggregator_client/query/get/
get_cardano_stake_distribution.rs1use async_trait::async_trait;
2use reqwest::StatusCode;
3use std::fmt::{Display, Formatter};
4
5use mithril_common::entities::EpochSpecifier;
6use mithril_common::messages::CardanoStakeDistributionMessage;
7
8use crate::AggregatorHttpClientResult;
9use crate::query::{AggregatorQuery, QueryContext, QueryMethod, ResponseExt};
10
11pub struct GetCardanoStakeDistributionQuery {
13 scope: QueryScope,
14}
15
16enum QueryScope {
17 Hash(String),
19 Epoch(EpochSpecifier),
21}
22
23impl Display for QueryScope {
24 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25 match &self {
26 QueryScope::Hash(hash) => write!(f, "hash({})", hash),
27 QueryScope::Epoch(specifier) => {
28 write!(f, "epoch({specifier})")
29 }
30 }
31 }
32}
33
34impl GetCardanoStakeDistributionQuery {
35 pub fn by_hash<H: Into<String>>(hash: H) -> Self {
37 Self {
38 scope: QueryScope::Hash(hash.into()),
39 }
40 }
41
42 pub fn for_epoch(epoch_specifier: EpochSpecifier) -> Self {
44 Self {
45 scope: QueryScope::Epoch(epoch_specifier),
46 }
47 }
48}
49
50#[cfg_attr(target_family = "wasm", async_trait(?Send))]
51#[cfg_attr(not(target_family = "wasm"), async_trait)]
52impl AggregatorQuery for GetCardanoStakeDistributionQuery {
53 type Response = Option<CardanoStakeDistributionMessage>;
54 type Body = ();
55
56 fn method() -> QueryMethod {
57 QueryMethod::Get
58 }
59
60 fn route(&self) -> String {
61 match &self.scope {
62 QueryScope::Hash(hash) => format!("artifact/cardano-stake-distribution/{hash}"),
63 QueryScope::Epoch(specifier) => {
64 format!("artifact/cardano-stake-distribution/epoch/{specifier}")
65 }
66 }
67 }
68
69 async fn handle_response(
70 &self,
71 context: QueryContext,
72 ) -> AggregatorHttpClientResult<Self::Response> {
73 match context.response.status() {
74 StatusCode::OK => context.response.parse_json_option().await,
75 StatusCode::NOT_FOUND => Ok(None),
76 _ => Err(context.unhandled_status_code().await),
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use serde_json::json;
84
85 use mithril_common::entities::Epoch;
86 use mithril_common::test::double::Dummy;
87
88 use crate::AggregatorHttpClientError;
89 use crate::test::{assert_error_matches, setup_server_and_client};
90
91 use super::*;
92
93 #[tokio::test]
94 async fn test_cardano_stake_distribution_by_hash_ok_200() {
95 let (server, client) = setup_server_and_client();
96 let expected_message = CardanoStakeDistributionMessage::dummy();
97 let _server_mock = server.mock(|when, then| {
98 when.path(format!(
99 "/artifact/cardano-stake-distribution/{}",
100 expected_message.hash
101 ));
102 then.status(200).body(json!(expected_message).to_string());
103 });
104
105 let fetched_message = client
106 .send(GetCardanoStakeDistributionQuery::by_hash(
107 &expected_message.hash,
108 ))
109 .await
110 .unwrap();
111
112 assert_eq!(Some(expected_message), fetched_message);
113 }
114
115 #[tokio::test]
116 async fn test_cardano_stake_distribution_by_epoch_ok_200() {
117 let (server, client) = setup_server_and_client();
118 let expected_message = CardanoStakeDistributionMessage::dummy();
119 let _server_mock = server.mock(|when, then| {
120 when.path("/artifact/cardano-stake-distribution/epoch/6");
121 then.status(200).body(json!(expected_message).to_string());
122 });
123
124 let fetched_message = client
125 .send(GetCardanoStakeDistributionQuery::for_epoch(
126 EpochSpecifier::Number(Epoch(6)),
127 ))
128 .await
129 .unwrap();
130
131 assert_eq!(Some(expected_message), fetched_message);
132 }
133
134 #[tokio::test]
135 async fn test_cardano_stake_distribution_for_latest_epoch_ok_200() {
136 let (server, client) = setup_server_and_client();
137 let expected_message = CardanoStakeDistributionMessage::dummy();
138 let _server_mock = server.mock(|when, then| {
139 when.path("/artifact/cardano-stake-distribution/epoch/latest");
140 then.status(200).body(json!(expected_message).to_string());
141 });
142
143 let fetched_message = client
144 .send(GetCardanoStakeDistributionQuery::for_epoch(
145 EpochSpecifier::Latest,
146 ))
147 .await
148 .unwrap();
149
150 assert_eq!(Some(expected_message), fetched_message);
151 }
152
153 #[tokio::test]
154 async fn test_cardano_stake_distribution_for_latest_epoch_with_offset_ok_200() {
155 let (server, client) = setup_server_and_client();
156 let expected_message = CardanoStakeDistributionMessage::dummy();
157 let _server_mock = server.mock(|when, then| {
158 when.path("/artifact/cardano-stake-distribution/epoch/latest-5");
159 then.status(200).body(json!(expected_message).to_string());
160 });
161
162 let fetched_message = client
163 .send(GetCardanoStakeDistributionQuery::for_epoch(
164 EpochSpecifier::LatestMinusOffset(5),
165 ))
166 .await
167 .unwrap();
168
169 assert_eq!(Some(expected_message), fetched_message);
170 }
171
172 #[tokio::test]
173 async fn test_cardano_stake_distribution_details_ok_404() {
174 let (server, client) = setup_server_and_client();
175 let _server_mock = server.mock(|when, then| {
176 when.any_request();
177 then.status(404);
178 });
179
180 let fetched_message = client
181 .send(GetCardanoStakeDistributionQuery::by_hash("whatever"))
182 .await
183 .unwrap();
184
185 assert_eq!(None, fetched_message);
186 }
187
188 #[tokio::test]
189 async fn test_cardano_stake_distribution_details_ko_500() {
190 let (server, client) = setup_server_and_client();
191 let _server_mock = server.mock(|when, then| {
192 when.any_request();
193 then.status(500).body("an error occurred");
194 });
195
196 let error = client
197 .send(GetCardanoStakeDistributionQuery::by_hash("whatever"))
198 .await
199 .unwrap_err();
200
201 assert_error_matches!(error, AggregatorHttpClientError::RemoteServerTechnical(_));
202 }
203}