1use std::collections::BTreeSet;
2use std::str::FromStr;
3use std::time::Duration;
4
5use anyhow::anyhow;
6use digest::Update;
7use serde::{Deserialize, Serialize};
8use sha2::Sha256;
9use strum::{AsRefStr, Display, EnumDiscriminants, EnumIter, EnumString, IntoEnumIterator};
10
11use crate::{
12 StdResult,
13 crypto_helper::{TryFromBytes, TryToBytes},
14};
15
16use super::{BlockNumber, CardanoDbBeacon, Epoch};
17
18const ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION: usize = 0;
20
21const ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION: usize = 1;
23
24const ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL: usize = 2;
26
27const ENTITY_TYPE_CARDANO_TRANSACTIONS: usize = 3;
29
30const ENTITY_TYPE_CARDANO_DATABASE: usize = 4;
32
33const ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS: usize = 5;
35
36#[derive(Display, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, EnumDiscriminants)]
44#[strum(serialize_all = "PascalCase")]
45#[strum_discriminants(doc = "The discriminants of the SignedEntityType enum.")]
46#[strum_discriminants(derive(
47 Display,
48 EnumString,
49 AsRefStr,
50 Serialize,
51 Deserialize,
52 PartialOrd,
53 Ord,
54 EnumIter,
55))]
56pub enum SignedEntityType {
57 MithrilStakeDistribution(Epoch),
59
60 CardanoStakeDistribution(Epoch),
62
63 CardanoImmutableFilesFull(CardanoDbBeacon),
65
66 CardanoDatabase(CardanoDbBeacon),
68
69 CardanoTransactions(Epoch, BlockNumber),
71
72 CardanoBlocksTransactions(Epoch, BlockNumber),
74}
75
76impl SignedEntityType {
77 pub fn genesis(epoch: Epoch) -> Self {
79 Self::MithrilStakeDistribution(epoch)
80 }
81
82 pub fn get_epoch(&self) -> Epoch {
84 match self {
85 Self::CardanoImmutableFilesFull(b) | Self::CardanoDatabase(b) => b.epoch,
86 Self::CardanoStakeDistribution(e)
87 | Self::MithrilStakeDistribution(e)
88 | Self::CardanoTransactions(e, _)
89 | Self::CardanoBlocksTransactions(e, _) => *e,
90 }
91 }
92
93 pub fn get_epoch_when_signed_entity_type_is_signed(&self) -> Epoch {
95 match self {
96 Self::CardanoImmutableFilesFull(beacon) | Self::CardanoDatabase(beacon) => beacon.epoch,
97 Self::CardanoStakeDistribution(epoch) => epoch.next(),
98 Self::MithrilStakeDistribution(epoch)
99 | Self::CardanoTransactions(epoch, _)
100 | Self::CardanoBlocksTransactions(epoch, _) => *epoch,
101 }
102 }
103
104 pub fn index(&self) -> usize {
106 match self {
107 Self::MithrilStakeDistribution(..) => ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION,
108 Self::CardanoStakeDistribution(..) => ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION,
109 Self::CardanoImmutableFilesFull(..) => ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL,
110 Self::CardanoTransactions(..) => ENTITY_TYPE_CARDANO_TRANSACTIONS,
111 Self::CardanoBlocksTransactions(..) => ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS,
112 Self::CardanoDatabase(..) => ENTITY_TYPE_CARDANO_DATABASE,
113 }
114 }
115
116 pub fn get_json_beacon(&self) -> StdResult<String> {
118 let value = match self {
119 Self::CardanoImmutableFilesFull(value) | Self::CardanoDatabase(value) => {
120 serde_json::to_string(value)?
121 }
122 Self::CardanoStakeDistribution(value) | Self::MithrilStakeDistribution(value) => {
123 serde_json::to_string(value)?
124 }
125 Self::CardanoTransactions(epoch, block_number)
126 | Self::CardanoBlocksTransactions(epoch, block_number) => {
127 let json = serde_json::json!({
128 "epoch": epoch,
129 "block_number": block_number,
130 });
131 serde_json::to_string(&json)?
132 }
133 };
134
135 Ok(value)
136 }
137
138 pub fn get_open_message_timeout(&self) -> Option<Duration> {
140 match self {
141 Self::MithrilStakeDistribution(..) => Some(Duration::from_secs(3600)),
142 Self::CardanoImmutableFilesFull(..) => Some(Duration::from_secs(600)),
143 Self::CardanoStakeDistribution(..) => Some(Duration::from_secs(1800)),
144 Self::CardanoTransactions(..) => Some(Duration::from_secs(600)),
145 Self::CardanoBlocksTransactions(..) => Some(Duration::from_secs(600)),
146 Self::CardanoDatabase(..) => Some(Duration::from_secs(600)),
147 }
148 }
149
150 pub(crate) fn feed_hash(&self, hasher: &mut Sha256) {
151 if matches!(self, Self::CardanoBlocksTransactions(..)) {
153 hasher.update(&self.index().to_be_bytes());
155 }
156
157 match self {
158 SignedEntityType::MithrilStakeDistribution(epoch)
159 | SignedEntityType::CardanoStakeDistribution(epoch) => {
160 hasher.update(&epoch.to_be_bytes())
161 }
162 SignedEntityType::CardanoImmutableFilesFull(db_beacon)
163 | SignedEntityType::CardanoDatabase(db_beacon) => {
164 hasher.update(&db_beacon.epoch.to_be_bytes());
165 hasher.update(&db_beacon.immutable_file_number.to_be_bytes());
166 }
167 SignedEntityType::CardanoTransactions(epoch, block_number)
168 | SignedEntityType::CardanoBlocksTransactions(epoch, block_number) => {
169 hasher.update(&epoch.to_be_bytes());
170 hasher.update(&block_number.to_be_bytes())
171 }
172 }
173 }
174}
175
176impl TryFromBytes for SignedEntityType {
177 fn try_from_bytes(bytes: &[u8]) -> StdResult<Self> {
178 let (res, _) =
179 bincode::serde::decode_from_slice::<Self, _>(bytes, bincode::config::standard())?;
180
181 Ok(res)
182 }
183}
184
185impl TryToBytes for SignedEntityType {
186 fn to_bytes_vec(&self) -> StdResult<Vec<u8>> {
187 bincode::serde::encode_to_vec(self, bincode::config::standard()).map_err(|e| e.into())
188 }
189}
190
191impl SignedEntityTypeDiscriminants {
192 pub fn all() -> BTreeSet<Self> {
194 Self::iter_all().collect()
195 }
196
197 fn iter_all() -> impl Iterator<Item = Self> {
198 Self::iter()
199 }
200
201 pub fn index(&self) -> usize {
203 match self {
204 Self::MithrilStakeDistribution => ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION,
205 Self::CardanoStakeDistribution => ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION,
206 Self::CardanoImmutableFilesFull => ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL,
207 Self::CardanoTransactions => ENTITY_TYPE_CARDANO_TRANSACTIONS,
208 Self::CardanoBlocksTransactions => ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS,
209 Self::CardanoDatabase => ENTITY_TYPE_CARDANO_DATABASE,
210 }
211 }
212
213 pub fn from_id(signed_entity_type_id: usize) -> StdResult<SignedEntityTypeDiscriminants> {
215 match signed_entity_type_id {
216 ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION => Ok(Self::MithrilStakeDistribution),
217 ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION => Ok(Self::CardanoStakeDistribution),
218 ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL => Ok(Self::CardanoImmutableFilesFull),
219 ENTITY_TYPE_CARDANO_TRANSACTIONS => Ok(Self::CardanoTransactions),
220 ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS => Ok(Self::CardanoBlocksTransactions),
221 ENTITY_TYPE_CARDANO_DATABASE => Ok(Self::CardanoDatabase),
222 index => Err(anyhow!("Invalid entity_type_id {index}.")),
223 }
224 }
225
226 pub fn parse_list<T: AsRef<str>>(discriminants_string: T) -> StdResult<BTreeSet<Self>> {
231 let mut discriminants = BTreeSet::new();
232 let mut invalid_discriminants = Vec::new();
233
234 for name in discriminants_string
235 .as_ref()
236 .split(',')
237 .map(str::trim)
238 .filter(|s| !s.is_empty())
239 {
240 match Self::from_str(name) {
241 Ok(discriminant) => {
242 discriminants.insert(discriminant);
243 }
244 Err(_) => {
245 invalid_discriminants.push(name);
246 }
247 }
248 }
249
250 if invalid_discriminants.is_empty() {
251 Ok(discriminants)
252 } else {
253 Err(anyhow!(Self::format_parse_list_error(
254 invalid_discriminants
255 )))
256 }
257 }
258
259 fn format_parse_list_error(invalid_discriminants: Vec<&str>) -> String {
260 format!(
261 r#"Invalid signed entity types discriminants: {}.
262
263Accepted values are (case-sensitive): {}."#,
264 invalid_discriminants.join(", "),
265 Self::accepted_discriminants()
266 )
267 }
268
269 fn accepted_discriminants() -> String {
270 Self::iter_all().map(|d| d.to_string()).collect::<Vec<_>>().join(", ")
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use digest::Digest;
277
278 use crate::test::assert_same_json;
279
280 use super::*;
281
282 fn hash(signed_entity_type: SignedEntityType) -> String {
283 let mut hasher = Sha256::new();
284 signed_entity_type.feed_hash(&mut hasher);
285 hex::encode(hasher.finalize())
286 }
287
288 #[test]
289 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_stake_distribution_return_epoch_with_offset()
290 {
291 let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(3));
292
293 assert_eq!(
294 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
295 Epoch(4)
296 );
297 }
298
299 #[test]
300 fn get_epoch_when_signed_entity_type_is_signed_for_mithril_stake_distribution_return_epoch_stored_in_signed_entity_type()
301 {
302 let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(3));
303 assert_eq!(
304 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
305 Epoch(3)
306 );
307 }
308
309 #[test]
310 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_immutable_files_full_return_epoch_stored_in_signed_entity_type()
311 {
312 let signed_entity_type =
313 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(3, 100));
314 assert_eq!(
315 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
316 Epoch(3)
317 );
318 }
319
320 #[test]
321 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_transactions_return_epoch_stored_in_signed_entity_type()
322 {
323 let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(3), BlockNumber(77));
324 assert_eq!(
325 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
326 Epoch(3)
327 );
328 }
329
330 #[test]
331 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_blocks_transactions_return_epoch_stored_in_signed_entity_type()
332 {
333 let signed_entity_type =
334 SignedEntityType::CardanoBlocksTransactions(Epoch(5), BlockNumber(77));
335 assert_eq!(
336 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
337 Epoch(5)
338 );
339 }
340
341 #[test]
342 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_database_return_epoch_stored_in_signed_entity_type()
343 {
344 let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(12, 987));
345 assert_eq!(
346 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
347 Epoch(12)
348 );
349 }
350
351 #[test]
352 fn verify_signed_entity_type_properties_are_included_in_computed_hash() {
353 let reference_hash = hash(SignedEntityType::MithrilStakeDistribution(Epoch(5)));
354 assert_ne!(
355 reference_hash,
356 hash(SignedEntityType::MithrilStakeDistribution(Epoch(15)))
357 );
358
359 let reference_hash = hash(SignedEntityType::CardanoStakeDistribution(Epoch(5)));
360 assert_ne!(
361 reference_hash,
362 hash(SignedEntityType::CardanoStakeDistribution(Epoch(15)))
363 );
364
365 let reference_hash = hash(SignedEntityType::CardanoImmutableFilesFull(
366 CardanoDbBeacon::new(5, 100),
367 ));
368 assert_ne!(
369 reference_hash,
370 hash(SignedEntityType::CardanoImmutableFilesFull(
371 CardanoDbBeacon::new(20, 100)
372 ))
373 );
374 assert_ne!(
375 reference_hash,
376 hash(SignedEntityType::CardanoImmutableFilesFull(
377 CardanoDbBeacon::new(5, 507)
378 ))
379 );
380
381 let reference_hash = hash(SignedEntityType::CardanoTransactions(
382 Epoch(35),
383 BlockNumber(77),
384 ));
385 assert_ne!(
386 reference_hash,
387 hash(SignedEntityType::CardanoTransactions(
388 Epoch(3),
389 BlockNumber(77)
390 ))
391 );
392 assert_ne!(
393 reference_hash,
394 hash(SignedEntityType::CardanoTransactions(
395 Epoch(35),
396 BlockNumber(98765)
397 ))
398 );
399
400 let reference_hash = hash(SignedEntityType::CardanoBlocksTransactions(
401 Epoch(35),
402 BlockNumber(77),
403 ));
404 assert_ne!(
405 reference_hash,
406 hash(SignedEntityType::CardanoBlocksTransactions(
407 Epoch(3),
408 BlockNumber(77)
409 ))
410 );
411 assert_ne!(
412 reference_hash,
413 hash(SignedEntityType::CardanoBlocksTransactions(
414 Epoch(35),
415 BlockNumber(98765)
416 ))
417 );
418
419 let reference_hash = hash(SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
420 12, 987,
421 )));
422 assert_ne!(
423 reference_hash,
424 hash(SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
425 98, 987
426 )))
427 );
428 assert_ne!(
429 reference_hash,
430 hash(SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
431 12, 123
432 )))
433 );
434 }
435
436 #[test]
437 fn signed_entity_with_shared_beacons_computes_different_hashes() {
438 assert_ne!(
439 hash(SignedEntityType::CardanoTransactions(
440 Epoch(3),
441 BlockNumber(77)
442 )),
443 hash(SignedEntityType::CardanoBlocksTransactions(
444 Epoch(3),
445 BlockNumber(77)
446 ))
447 );
448
449 }
464
465 #[test]
466 fn get_open_message_timeout() {
467 assert_eq!(
468 SignedEntityType::MithrilStakeDistribution(Epoch(1)).get_open_message_timeout(),
469 Some(Duration::from_secs(3600))
470 );
471 assert_eq!(
472 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(1, 1))
473 .get_open_message_timeout(),
474 Some(Duration::from_secs(600))
475 );
476 assert_eq!(
477 SignedEntityType::CardanoStakeDistribution(Epoch(1)).get_open_message_timeout(),
478 Some(Duration::from_secs(1800))
479 );
480 assert_eq!(
481 SignedEntityType::CardanoTransactions(Epoch(1), BlockNumber(1))
482 .get_open_message_timeout(),
483 Some(Duration::from_secs(600))
484 );
485 assert_eq!(
486 SignedEntityType::CardanoBlocksTransactions(Epoch(1), BlockNumber(1))
487 .get_open_message_timeout(),
488 Some(Duration::from_secs(600))
489 );
490 assert_eq!(
491 SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(1, 1))
492 .get_open_message_timeout(),
493 Some(Duration::from_secs(600))
494 );
495 }
496
497 #[test]
498 fn serialize_beacon_to_json() {
499 let cardano_stake_distribution_json = SignedEntityType::CardanoStakeDistribution(Epoch(25))
500 .get_json_beacon()
501 .unwrap();
502 assert_same_json!("25", &cardano_stake_distribution_json);
503
504 let cardano_transactions_json =
505 SignedEntityType::CardanoTransactions(Epoch(35), BlockNumber(77))
506 .get_json_beacon()
507 .unwrap();
508 assert_same_json!(
509 r#"{"epoch":35,"block_number":77}"#,
510 &cardano_transactions_json
511 );
512
513 let cardano_transactions_json =
514 SignedEntityType::CardanoBlocksTransactions(Epoch(35), BlockNumber(77))
515 .get_json_beacon()
516 .unwrap();
517 assert_same_json!(
518 r#"{"epoch":35,"block_number":77}"#,
519 &cardano_transactions_json
520 );
521
522 let cardano_immutable_files_full_json =
523 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(5, 100))
524 .get_json_beacon()
525 .unwrap();
526 assert_same_json!(
527 r#"{"epoch":5,"immutable_file_number":100}"#,
528 &cardano_immutable_files_full_json
529 );
530
531 let msd_json = SignedEntityType::MithrilStakeDistribution(Epoch(15))
532 .get_json_beacon()
533 .unwrap();
534 assert_same_json!("15", &msd_json);
535
536 let cardano_database_full_json =
537 SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(12, 987))
538 .get_json_beacon()
539 .unwrap();
540 assert_same_json!(
541 r#"{"epoch":12,"immutable_file_number":987}"#,
542 &cardano_database_full_json
543 );
544 }
545
546 #[test]
547 fn bytes_encoding() {
548 let cardano_stake_distribution = SignedEntityType::CardanoStakeDistribution(Epoch(25));
549 let cardano_stake_distribution_bytes = cardano_stake_distribution.to_bytes_vec().unwrap();
550 let cardano_stake_distribution_from_bytes =
551 SignedEntityType::try_from_bytes(&cardano_stake_distribution_bytes).unwrap();
552
553 assert_eq!(
554 cardano_stake_distribution,
555 cardano_stake_distribution_from_bytes
556 );
557 }
558
559 #[test]
560 fn iter_all_discriminants() {
561 let discriminants: Vec<SignedEntityTypeDiscriminants> =
562 SignedEntityTypeDiscriminants::iter_all().collect();
563
564 assert_eq!(
565 discriminants,
566 vec![
567 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
568 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
569 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
570 SignedEntityTypeDiscriminants::CardanoDatabase,
571 SignedEntityTypeDiscriminants::CardanoTransactions,
572 SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
573 ]
574 );
575 }
576
577 #[test]
580 fn ordering_discriminant() {
581 let mut list = vec![
582 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
583 SignedEntityTypeDiscriminants::CardanoDatabase,
584 SignedEntityTypeDiscriminants::CardanoTransactions,
585 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
586 SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
587 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
588 ];
589 list.sort();
590
591 assert_eq!(
592 list,
593 vec![
594 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
595 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
596 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
597 SignedEntityTypeDiscriminants::CardanoDatabase,
598 SignedEntityTypeDiscriminants::CardanoTransactions,
599 SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
600 ]
601 );
602 }
603
604 #[test]
605 fn ordering_discriminant_with_duplicate() {
606 let mut list = vec![
607 SignedEntityTypeDiscriminants::CardanoDatabase,
608 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
609 SignedEntityTypeDiscriminants::CardanoDatabase,
610 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
611 SignedEntityTypeDiscriminants::CardanoTransactions,
612 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
613 SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
614 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
615 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
616 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
617 ];
618 list.sort();
619
620 assert_eq!(
621 list,
622 vec![
623 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
624 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
625 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
626 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
627 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
628 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
629 SignedEntityTypeDiscriminants::CardanoDatabase,
630 SignedEntityTypeDiscriminants::CardanoDatabase,
631 SignedEntityTypeDiscriminants::CardanoTransactions,
632 SignedEntityTypeDiscriminants::CardanoBlocksTransactions,
633 ]
634 );
635 }
636
637 #[test]
638 fn parse_signed_entity_types_discriminants_discriminant_without_values() {
639 let discriminants_str = "";
640 let discriminants = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap();
641
642 assert_eq!(BTreeSet::new(), discriminants);
643
644 let discriminants_str = " ";
645 let discriminants = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap();
646
647 assert_eq!(BTreeSet::new(), discriminants);
648 }
649
650 #[test]
651 fn parse_signed_entity_types_discriminants_with_correctly_formed_values() {
652 let discriminants_str = "MithrilStakeDistribution,CardanoImmutableFilesFull";
653 let discriminants = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap();
654
655 assert_eq!(
656 BTreeSet::from([
657 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
658 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
659 ]),
660 discriminants
661 );
662 }
663
664 #[test]
665 fn parse_signed_entity_types_discriminants_should_trim_values() {
666 let discriminants_str =
667 "MithrilStakeDistribution , CardanoImmutableFilesFull , CardanoTransactions ";
668 let discriminants = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap();
669
670 assert_eq!(
671 BTreeSet::from([
672 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
673 SignedEntityTypeDiscriminants::CardanoTransactions,
674 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
675 ]),
676 discriminants
677 );
678 }
679
680 #[test]
681 fn parse_signed_entity_types_discriminants_should_remove_duplicates() {
682 let discriminants_str =
683 "CardanoTransactions,CardanoTransactions,CardanoTransactions,CardanoTransactions";
684 let discriminant = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap();
685
686 assert_eq!(
687 BTreeSet::from([SignedEntityTypeDiscriminants::CardanoTransactions]),
688 discriminant
689 );
690 }
691
692 #[test]
693 fn parse_signed_entity_types_discriminants_should_be_case_sensitive() {
694 let discriminants_str = "mithrilstakedistribution,CARDANOIMMUTABLEFILESFULL";
695 let error = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap_err();
696
697 assert_eq!(
698 SignedEntityTypeDiscriminants::format_parse_list_error(vec![
699 "mithrilstakedistribution",
700 "CARDANOIMMUTABLEFILESFULL"
701 ]),
702 error.to_string()
703 );
704 }
705
706 #[test]
707 fn parse_signed_entity_types_discriminants_should_not_return_unknown_signed_entity_types() {
708 let discriminants_str = "Unknown";
709 let error = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap_err();
710
711 assert_eq!(
712 SignedEntityTypeDiscriminants::format_parse_list_error(vec!["Unknown"]),
713 error.to_string()
714 );
715 }
716
717 #[test]
718 fn parse_signed_entity_types_discriminants_should_fail_if_there_is_at_least_one_invalid_value()
719 {
720 let discriminants_str = "CardanoTransactions,Invalid,MithrilStakeDistribution";
721 let error = SignedEntityTypeDiscriminants::parse_list(discriminants_str).unwrap_err();
722
723 assert_eq!(
724 SignedEntityTypeDiscriminants::format_parse_list_error(vec!["Invalid"]),
725 error.to_string()
726 );
727 }
728
729 #[test]
730 fn parse_list_error_format_to_an_useful_message() {
731 let invalid_discriminants = vec!["Unknown", "Invalid"];
732 let error = SignedEntityTypeDiscriminants::format_parse_list_error(invalid_discriminants);
733
734 assert_eq!(
735 format!(
736 r#"Invalid signed entity types discriminants: Unknown, Invalid.
737
738Accepted values are (case-sensitive): {}."#,
739 SignedEntityTypeDiscriminants::accepted_discriminants()
740 ),
741 error
742 );
743 }
744
745 #[test]
746 fn discriminants_index_returns_correct_database_value() {
747 assert_eq!(
748 SignedEntityTypeDiscriminants::MithrilStakeDistribution.index(),
749 ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION
750 );
751 assert_eq!(
752 SignedEntityTypeDiscriminants::CardanoStakeDistribution.index(),
753 ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION
754 );
755 assert_eq!(
756 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull.index(),
757 ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL
758 );
759 assert_eq!(
760 SignedEntityTypeDiscriminants::CardanoTransactions.index(),
761 ENTITY_TYPE_CARDANO_TRANSACTIONS
762 );
763 assert_eq!(
764 SignedEntityTypeDiscriminants::CardanoBlocksTransactions.index(),
765 ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS
766 );
767 assert_eq!(
768 SignedEntityTypeDiscriminants::CardanoDatabase.index(),
769 ENTITY_TYPE_CARDANO_DATABASE
770 );
771 }
772}