mithril_client/certificate_client/
fetch.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use std::sync::Arc;
4
5use mithril_common::certificate_chain::{CertificateRetriever, CertificateRetrieverError};
6use mithril_common::entities::Certificate;
7
8use crate::certificate_client::{CertificateAggregatorRequest, CertificateClient};
9use crate::{MithrilCertificate, MithrilCertificateListItem, MithrilResult};
10
11#[inline]
12pub(super) async fn list(
13    client: &CertificateClient,
14) -> MithrilResult<Vec<MithrilCertificateListItem>> {
15    client.aggregator_requester.list_latest().await
16}
17
18#[inline]
19pub(super) async fn get(
20    client: &CertificateClient,
21    certificate_hash: &str,
22) -> MithrilResult<Option<MithrilCertificate>> {
23    client.retriever.get(certificate_hash).await
24}
25
26/// Internal type to implement the [InternalCertificateRetriever] trait and avoid a circular
27/// dependency between the [CertificateClient] and the [CommonMithrilCertificateVerifier] that need
28/// a [CertificateRetriever] as a dependency.
29pub(super) struct InternalCertificateRetriever {
30    aggregator_requester: Arc<dyn CertificateAggregatorRequest>,
31}
32
33impl InternalCertificateRetriever {
34    pub(super) fn new(
35        aggregator_requester: Arc<dyn CertificateAggregatorRequest>,
36    ) -> InternalCertificateRetriever {
37        InternalCertificateRetriever {
38            aggregator_requester,
39        }
40    }
41
42    pub(super) async fn get(
43        &self,
44        certificate_hash: &str,
45    ) -> MithrilResult<Option<MithrilCertificate>> {
46        self.aggregator_requester.get_by_hash(certificate_hash).await
47    }
48}
49
50#[cfg_attr(target_family = "wasm", async_trait(?Send))]
51#[cfg_attr(not(target_family = "wasm"), async_trait)]
52impl CertificateRetriever for InternalCertificateRetriever {
53    async fn get_certificate_details(
54        &self,
55        certificate_hash: &str,
56    ) -> Result<Certificate, CertificateRetrieverError> {
57        self.get(certificate_hash)
58            .await
59            .map_err(CertificateRetrieverError)?
60            .map(|message| message.try_into())
61            .transpose()
62            .map_err(CertificateRetrieverError)?
63            .with_context(|| format!("Certificate does not exist: '{certificate_hash}'"))
64            .map_err(CertificateRetrieverError)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use anyhow::anyhow;
71
72    use mithril_common::test::double::{Dummy, fake_data};
73
74    use crate::certificate_client::tests_utils::CertificateClientTestBuilder;
75
76    use super::*;
77
78    #[tokio::test]
79    async fn get_certificate_list() {
80        let expected = vec![
81            MithrilCertificateListItem {
82                hash: "cert-hash-123".to_string(),
83                ..MithrilCertificateListItem::dummy()
84            },
85            MithrilCertificateListItem {
86                hash: "cert-hash-456".to_string(),
87                ..MithrilCertificateListItem::dummy()
88            },
89        ];
90        let message = expected.clone();
91        let certificate_client = CertificateClientTestBuilder::default()
92            .config_aggregator_requester_mock(|mock| {
93                mock.expect_list_latest().return_once(move || Ok(message));
94            })
95            .build();
96        let items = certificate_client.list().await.unwrap();
97
98        assert_eq!(expected, items);
99    }
100
101    #[tokio::test]
102    async fn get_certificate_empty_list() {
103        let certificate_client = CertificateClientTestBuilder::default()
104            .config_aggregator_requester_mock(|mock| {
105                mock.expect_list_latest().return_once(move || Ok(Vec::new()));
106            })
107            .build();
108        let items = certificate_client.list().await.unwrap();
109
110        assert!(items.is_empty());
111    }
112
113    #[tokio::test]
114    async fn test_show_ok_some() {
115        let certificate_hash = "cert-hash-123".to_string();
116        let certificate = fake_data::certificate(certificate_hash.clone());
117        let expected_certificate = certificate.clone();
118
119        let certificate_client = CertificateClientTestBuilder::default()
120            .config_aggregator_requester_mock(|mock| {
121                mock.expect_get_by_hash()
122                    .return_once(move |_| {
123                        let message: MithrilCertificate = certificate.try_into().unwrap();
124                        Ok(Some(message))
125                    })
126                    .times(1);
127            })
128            .build();
129
130        let cert = certificate_client
131            .get("cert-hash-123")
132            .await
133            .unwrap()
134            .expect("The certificate should be found")
135            .try_into()
136            .unwrap();
137
138        assert_eq!(expected_certificate, cert);
139    }
140
141    #[tokio::test]
142    async fn test_show_ok_none() {
143        let certificate_client = CertificateClientTestBuilder::default()
144            .config_aggregator_requester_mock(|mock| {
145                mock.expect_get_by_hash().return_once(move |_| Ok(None)).times(1);
146            })
147            .build();
148
149        assert!(certificate_client.get("cert-hash-123").await.unwrap().is_none());
150    }
151
152    #[tokio::test]
153    async fn test_show_ko() {
154        let certificate_client = CertificateClientTestBuilder::default()
155            .config_aggregator_requester_mock(|mock| {
156                mock.expect_get_by_hash()
157                    .return_once(move |_| Err(anyhow!("an error")))
158                    .times(1);
159            })
160            .build();
161
162        certificate_client
163            .get("cert-hash-123")
164            .await
165            .expect_err("The certificate client should fail here.");
166    }
167}