mithril_client/cardano_database_client/
fetch.rs

1use std::sync::Arc;
2
3use mithril_common::entities::EpochSpecifier;
4
5use crate::{
6    CardanoDatabaseSnapshot, CardanoDatabaseSnapshotListItem, MithrilResult,
7    cardano_database_client::CardanoDatabaseAggregatorRequest, common::Epoch,
8};
9
10pub struct InternalArtifactRetriever {
11    pub(super) aggregator_requester: Arc<dyn CardanoDatabaseAggregatorRequest>,
12}
13
14impl InternalArtifactRetriever {
15    /// Constructs a new `InternalArtifactRetriever`
16    pub fn new(aggregator_requester: Arc<dyn CardanoDatabaseAggregatorRequest>) -> Self {
17        Self {
18            aggregator_requester,
19        }
20    }
21
22    /// Fetch a list of signed CardanoDatabase
23    pub async fn list(&self) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
24        self.aggregator_requester.list_latest().await
25    }
26
27    /// Fetch a list of signed CardanoDatabase for a given epoch
28    pub async fn list_by_epoch(
29        &self,
30        epoch: Epoch,
31    ) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
32        self.aggregator_requester
33            .list_by_epoch(EpochSpecifier::Number(epoch))
34            .await
35    }
36
37    /// Fetch a list of signed CardanoDatabase for the latest epoch
38    pub async fn list_for_latest_epoch(
39        &self,
40    ) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
41        self.aggregator_requester.list_by_epoch(EpochSpecifier::Latest).await
42    }
43
44    /// Fetch a list of signed CardanoDatabase for the latest epoch minus the given offset
45    pub async fn list_for_latest_epoch_with_offset(
46        &self,
47        offset: u64,
48    ) -> MithrilResult<Vec<CardanoDatabaseSnapshotListItem>> {
49        self.aggregator_requester
50            .list_by_epoch(EpochSpecifier::LatestMinusOffset(offset))
51            .await
52    }
53
54    /// Get the given Cardano database data by hash.
55    pub async fn get(&self, hash: &str) -> MithrilResult<Option<CardanoDatabaseSnapshot>> {
56        self.aggregator_requester.get_by_hash(hash).await
57    }
58}
59
60#[cfg(test)]
61mod tests {
62
63    use anyhow::anyhow;
64    use chrono::{DateTime, Utc};
65    use mockall::predicate::eq;
66
67    use mithril_common::entities::{CardanoDbBeacon, Epoch};
68
69    use crate::cardano_database_client::CardanoDatabaseClientDependencyInjector;
70    use crate::common::test::Dummy;
71
72    use super::*;
73
74    fn fake_messages() -> Vec<CardanoDatabaseSnapshotListItem> {
75        vec![
76            CardanoDatabaseSnapshotListItem {
77                hash: "hash-123".to_string(),
78                merkle_root: "mkroot-123".to_string(),
79                beacon: CardanoDbBeacon {
80                    epoch: Epoch(1),
81                    immutable_file_number: 123,
82                },
83                certificate_hash: "cert-hash-123".to_string(),
84                total_db_size_uncompressed: 800796318,
85                created_at: DateTime::parse_from_rfc3339("2025-01-19T13:43:05.618857482Z")
86                    .unwrap()
87                    .with_timezone(&Utc),
88                cardano_node_version: "0.0.1".to_string(),
89            },
90            CardanoDatabaseSnapshotListItem {
91                hash: "hash-456".to_string(),
92                merkle_root: "mkroot-456".to_string(),
93                beacon: CardanoDbBeacon {
94                    epoch: Epoch(2),
95                    immutable_file_number: 456,
96                },
97                certificate_hash: "cert-hash-456".to_string(),
98                total_db_size_uncompressed: 2960713808,
99                created_at: DateTime::parse_from_rfc3339("2025-01-27T15:22:05.618857482Z")
100                    .unwrap()
101                    .with_timezone(&Utc),
102                cardano_node_version: "0.0.1".to_string(),
103            },
104        ]
105    }
106
107    mod list {
108
109        use super::*;
110
111        #[tokio::test]
112        async fn list_cardano_database_snapshots_returns_messages() {
113            let message = fake_messages();
114            let client = CardanoDatabaseClientDependencyInjector::new()
115                .with_aggregator_requester_mock_config(|requester| {
116                    requester.expect_list_latest().return_once(move || Ok(message));
117                })
118                .build_cardano_database_client();
119
120            let messages = client.list().await.unwrap();
121
122            assert_eq!(2, messages.len());
123            assert_eq!("hash-123".to_string(), messages[0].hash);
124            assert_eq!("hash-456".to_string(), messages[1].hash);
125        }
126
127        #[tokio::test]
128        async fn list_cardano_database_snapshots_by_epoch_returns_messages() {
129            let message = fake_messages();
130            let client = CardanoDatabaseClientDependencyInjector::new()
131                .with_aggregator_requester_mock_config(|requester| {
132                    requester
133                        .expect_list_by_epoch()
134                        .with(eq(EpochSpecifier::Number(Epoch(4))))
135                        .return_once(move |_| Ok(message));
136                })
137                .build_cardano_database_client();
138
139            let messages = client.list_by_epoch(Epoch(4)).await.unwrap();
140
141            assert_eq!(2, messages.len());
142            assert_eq!("hash-123".to_string(), messages[0].hash);
143            assert_eq!("hash-456".to_string(), messages[1].hash);
144        }
145
146        #[tokio::test]
147        async fn list_cardano_database_snapshots_for_latest_epoch_returns_messages() {
148            let message = fake_messages();
149            let client = CardanoDatabaseClientDependencyInjector::new()
150                .with_aggregator_requester_mock_config(|requester| {
151                    requester
152                        .expect_list_by_epoch()
153                        .with(eq(EpochSpecifier::Latest))
154                        .return_once(move |_| Ok(message));
155                })
156                .build_cardano_database_client();
157
158            let messages = client.list_for_latest_epoch().await.unwrap();
159
160            assert_eq!(2, messages.len());
161            assert_eq!("hash-123".to_string(), messages[0].hash);
162            assert_eq!("hash-456".to_string(), messages[1].hash);
163        }
164
165        #[tokio::test]
166        async fn list_cardano_database_snapshots_for_latest_epoch_with_offset_returns_messages() {
167            let message = fake_messages();
168            let client = CardanoDatabaseClientDependencyInjector::new()
169                .with_aggregator_requester_mock_config(|requester| {
170                    requester
171                        .expect_list_by_epoch()
172                        .with(eq(EpochSpecifier::LatestMinusOffset(42)))
173                        .return_once(move |_| Ok(message));
174                })
175                .build_cardano_database_client();
176
177            let messages = client.list_for_latest_epoch_with_offset(42).await.unwrap();
178
179            assert_eq!(2, messages.len());
180            assert_eq!("hash-123".to_string(), messages[0].hash);
181            assert_eq!("hash-456".to_string(), messages[1].hash);
182        }
183    }
184
185    mod get {
186        use super::*;
187
188        #[tokio::test]
189        async fn get_cardano_database_snapshot_returns_message() {
190            let expected_cardano_database_snapshot = CardanoDatabaseSnapshot {
191                hash: "hash-123".to_string(),
192                ..CardanoDatabaseSnapshot::dummy()
193            };
194            let message = expected_cardano_database_snapshot.clone();
195            let client = CardanoDatabaseClientDependencyInjector::new()
196                .with_aggregator_requester_mock_config(|requester| {
197                    requester
198                        .expect_get_by_hash()
199                        .with(eq("hash-123".to_string()))
200                        .return_once(move |_| Ok(Some(message)));
201                })
202                .build_cardano_database_client();
203
204            let cardano_database = client
205                .get("hash-123")
206                .await
207                .unwrap()
208                .expect("should return a Cardano database");
209
210            assert_eq!(expected_cardano_database_snapshot, cardano_database);
211        }
212
213        #[tokio::test]
214        async fn get_cardano_database_snapshot_returns_error() {
215            let client = CardanoDatabaseClientDependencyInjector::new()
216                .with_aggregator_requester_mock_config(|requester| {
217                    requester
218                        .expect_get_by_hash()
219                        .return_once(move |_| Err(anyhow!("error")));
220                })
221                .build_cardano_database_client();
222
223            client
224                .get("hash-123")
225                .await
226                .expect_err("Get Cardano database should return an error");
227        }
228    }
229}