mithril_aggregator_client/query/get/
get_cardano_transaction_proof.rs

1use async_trait::async_trait;
2use reqwest::StatusCode;
3
4use mithril_common::messages::CardanoTransactionsProofsMessage;
5
6use crate::AggregatorHttpClientResult;
7use crate::query::{AggregatorQuery, QueryContext, QueryMethod, ResponseExt};
8
9/// Query to get a proof of membership for a list of Cardano transaction hashes
10pub struct GetCardanoTransactionProofQuery {
11    transactions_hashes: Vec<String>,
12}
13
14impl GetCardanoTransactionProofQuery {
15    /// Instantiate a query to get a proof of membership for a list of Cardano transaction hashes
16    pub fn for_hashes<H: ToString>(hashes: &[H]) -> Self {
17        Self {
18            transactions_hashes: hashes.iter().map(|h| h.to_string()).collect(),
19        }
20    }
21}
22
23#[cfg_attr(target_family = "wasm", async_trait(?Send))]
24#[cfg_attr(not(target_family = "wasm"), async_trait)]
25impl AggregatorQuery for GetCardanoTransactionProofQuery {
26    type Response = Option<CardanoTransactionsProofsMessage>;
27    type Body = ();
28
29    fn method() -> QueryMethod {
30        QueryMethod::Get
31    }
32
33    fn route(&self) -> String {
34        format!(
35            "proof/cardano-transaction?transaction_hashes={}",
36            self.transactions_hashes.join(",")
37        )
38    }
39
40    async fn handle_response(
41        &self,
42        context: QueryContext,
43    ) -> AggregatorHttpClientResult<Self::Response> {
44        match context.response.status() {
45            StatusCode::OK => context.response.parse_json_option().await,
46            StatusCode::NOT_FOUND => Ok(None),
47            _ => Err(context.unhandled_status_code().await),
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use serde_json::json;
55
56    use crate::AggregatorHttpClientError;
57    use crate::test::{assert_error_matches, setup_server_and_client};
58
59    use super::*;
60
61    #[tokio::test]
62    async fn test_cardano_transaction_proof_ok_200() {
63        let (server, client) = setup_server_and_client();
64        let expected_message = CardanoTransactionsProofsMessage {
65            certificate_hash: "whatever".to_string(),
66            certified_transactions: vec![],
67            non_certified_transactions: vec![],
68            latest_block_number: Default::default(),
69        };
70        let _server_mock = server.mock(|when, then| {
71            when.path("/proof/cardano-transaction")
72                .query_param("transaction_hashes", "hash1,hash3,hash2");
73            then.status(200).body(json!(expected_message).to_string());
74        });
75
76        let fetched_message = client
77            .send(GetCardanoTransactionProofQuery::for_hashes(&[
78                "hash1", "hash3", "hash2",
79            ]))
80            .await
81            .unwrap();
82
83        assert_eq!(Some(expected_message), fetched_message);
84    }
85
86    #[tokio::test]
87    async fn test_cardano_transaction_proof_ok_404() {
88        let (server, client) = setup_server_and_client();
89        let _server_mock = server.mock(|when, then| {
90            when.any_request();
91            then.status(404);
92        });
93
94        let fetched_message = client
95            .send(GetCardanoTransactionProofQuery::for_hashes(&["whatever"]))
96            .await
97            .unwrap();
98
99        assert_eq!(None, fetched_message);
100    }
101
102    #[tokio::test]
103    async fn test_cardano_transaction_proof_ko_500() {
104        let (server, client) = setup_server_and_client();
105        let _server_mock = server.mock(|when, then| {
106            when.any_request();
107            then.status(500).body("an error occurred");
108        });
109
110        let error = client
111            .send(GetCardanoTransactionProofQuery::for_hashes(&["whatever"]))
112            .await
113            .unwrap_err();
114
115        assert_error_matches!(error, AggregatorHttpClientError::RemoteServerTechnical(_));
116    }
117}