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().filter(|&d| d != Self::CardanoBlocksTransactions)
200 }
201
202 pub fn index(&self) -> usize {
204 match self {
205 Self::MithrilStakeDistribution => ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION,
206 Self::CardanoStakeDistribution => ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION,
207 Self::CardanoImmutableFilesFull => ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL,
208 Self::CardanoTransactions => ENTITY_TYPE_CARDANO_TRANSACTIONS,
209 Self::CardanoBlocksTransactions => ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS,
210 Self::CardanoDatabase => ENTITY_TYPE_CARDANO_DATABASE,
211 }
212 }
213
214 pub fn from_id(signed_entity_type_id: usize) -> StdResult<SignedEntityTypeDiscriminants> {
216 match signed_entity_type_id {
217 ENTITY_TYPE_MITHRIL_STAKE_DISTRIBUTION => Ok(Self::MithrilStakeDistribution),
218 ENTITY_TYPE_CARDANO_STAKE_DISTRIBUTION => Ok(Self::CardanoStakeDistribution),
219 ENTITY_TYPE_CARDANO_IMMUTABLE_FILES_FULL => Ok(Self::CardanoImmutableFilesFull),
220 ENTITY_TYPE_CARDANO_TRANSACTIONS => Ok(Self::CardanoTransactions),
221 ENTITY_TYPE_CARDANO_BLOCKS_TRANSACTIONS => Ok(Self::CardanoBlocksTransactions),
222 ENTITY_TYPE_CARDANO_DATABASE => Ok(Self::CardanoDatabase),
223 index => Err(anyhow!("Invalid entity_type_id {index}.")),
224 }
225 }
226
227 pub fn parse_list<T: AsRef<str>>(discriminants_string: T) -> StdResult<BTreeSet<Self>> {
232 let mut discriminants = BTreeSet::new();
233 let mut invalid_discriminants = Vec::new();
234
235 for name in discriminants_string
236 .as_ref()
237 .split(',')
238 .map(str::trim)
239 .filter(|s| !s.is_empty())
240 {
241 match Self::from_str(name) {
242 Ok(discriminant) => {
243 discriminants.insert(discriminant);
244 }
245 Err(_) => {
246 invalid_discriminants.push(name);
247 }
248 }
249 }
250
251 if invalid_discriminants.is_empty() {
252 Ok(discriminants)
253 } else {
254 Err(anyhow!(Self::format_parse_list_error(
255 invalid_discriminants
256 )))
257 }
258 }
259
260 fn format_parse_list_error(invalid_discriminants: Vec<&str>) -> String {
261 format!(
262 r#"Invalid signed entity types discriminants: {}.
263
264Accepted values are (case-sensitive): {}."#,
265 invalid_discriminants.join(", "),
266 Self::accepted_discriminants()
267 )
268 }
269
270 fn accepted_discriminants() -> String {
271 Self::iter_all().map(|d| d.to_string()).collect::<Vec<_>>().join(", ")
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use digest::Digest;
278
279 use crate::test::assert_same_json;
280
281 use super::*;
282
283 fn hash(signed_entity_type: SignedEntityType) -> String {
284 let mut hasher = Sha256::new();
285 signed_entity_type.feed_hash(&mut hasher);
286 hex::encode(hasher.finalize())
287 }
288
289 #[test]
290 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_stake_distribution_return_epoch_with_offset()
291 {
292 let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(3));
293
294 assert_eq!(
295 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
296 Epoch(4)
297 );
298 }
299
300 #[test]
301 fn get_epoch_when_signed_entity_type_is_signed_for_mithril_stake_distribution_return_epoch_stored_in_signed_entity_type()
302 {
303 let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(3));
304 assert_eq!(
305 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
306 Epoch(3)
307 );
308 }
309
310 #[test]
311 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_immutable_files_full_return_epoch_stored_in_signed_entity_type()
312 {
313 let signed_entity_type =
314 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(3, 100));
315 assert_eq!(
316 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
317 Epoch(3)
318 );
319 }
320
321 #[test]
322 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_transactions_return_epoch_stored_in_signed_entity_type()
323 {
324 let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(3), BlockNumber(77));
325 assert_eq!(
326 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
327 Epoch(3)
328 );
329 }
330
331 #[test]
332 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_blocks_transactions_return_epoch_stored_in_signed_entity_type()
333 {
334 let signed_entity_type =
335 SignedEntityType::CardanoBlocksTransactions(Epoch(5), BlockNumber(77));
336 assert_eq!(
337 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
338 Epoch(5)
339 );
340 }
341
342 #[test]
343 fn get_epoch_when_signed_entity_type_is_signed_for_cardano_database_return_epoch_stored_in_signed_entity_type()
344 {
345 let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(12, 987));
346 assert_eq!(
347 signed_entity_type.get_epoch_when_signed_entity_type_is_signed(),
348 Epoch(12)
349 );
350 }
351
352 #[test]
353 fn verify_signed_entity_type_properties_are_included_in_computed_hash() {
354 let reference_hash = hash(SignedEntityType::MithrilStakeDistribution(Epoch(5)));
355 assert_ne!(
356 reference_hash,
357 hash(SignedEntityType::MithrilStakeDistribution(Epoch(15)))
358 );
359
360 let reference_hash = hash(SignedEntityType::CardanoStakeDistribution(Epoch(5)));
361 assert_ne!(
362 reference_hash,
363 hash(SignedEntityType::CardanoStakeDistribution(Epoch(15)))
364 );
365
366 let reference_hash = hash(SignedEntityType::CardanoImmutableFilesFull(
367 CardanoDbBeacon::new(5, 100),
368 ));
369 assert_ne!(
370 reference_hash,
371 hash(SignedEntityType::CardanoImmutableFilesFull(
372 CardanoDbBeacon::new(20, 100)
373 ))
374 );
375 assert_ne!(
376 reference_hash,
377 hash(SignedEntityType::CardanoImmutableFilesFull(
378 CardanoDbBeacon::new(5, 507)
379 ))
380 );
381
382 let reference_hash = hash(SignedEntityType::CardanoTransactions(
383 Epoch(35),
384 BlockNumber(77),
385 ));
386 assert_ne!(
387 reference_hash,
388 hash(SignedEntityType::CardanoTransactions(
389 Epoch(3),
390 BlockNumber(77)
391 ))
392 );
393 assert_ne!(
394 reference_hash,
395 hash(SignedEntityType::CardanoTransactions(
396 Epoch(35),
397 BlockNumber(98765)
398 ))
399 );
400
401 let reference_hash = hash(SignedEntityType::CardanoBlocksTransactions(
402 Epoch(35),
403 BlockNumber(77),
404 ));
405 assert_ne!(
406 reference_hash,
407 hash(SignedEntityType::CardanoBlocksTransactions(
408 Epoch(3),
409 BlockNumber(77)
410 ))
411 );
412 assert_ne!(
413 reference_hash,
414 hash(SignedEntityType::CardanoBlocksTransactions(
415 Epoch(35),
416 BlockNumber(98765)
417 ))
418 );
419
420 let reference_hash = hash(SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
421 12, 987,
422 )));
423 assert_ne!(
424 reference_hash,
425 hash(SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
426 98, 987
427 )))
428 );
429 assert_ne!(
430 reference_hash,
431 hash(SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(
432 12, 123
433 )))
434 );
435 }
436
437 #[test]
438 fn signed_entity_with_shared_beacons_computes_different_hashes() {
439 assert_ne!(
440 hash(SignedEntityType::CardanoTransactions(
441 Epoch(3),
442 BlockNumber(77)
443 )),
444 hash(SignedEntityType::CardanoBlocksTransactions(
445 Epoch(3),
446 BlockNumber(77)
447 ))
448 );
449
450 }
465
466 #[test]
467 fn get_open_message_timeout() {
468 assert_eq!(
469 SignedEntityType::MithrilStakeDistribution(Epoch(1)).get_open_message_timeout(),
470 Some(Duration::from_secs(3600))
471 );
472 assert_eq!(
473 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(1, 1))
474 .get_open_message_timeout(),
475 Some(Duration::from_secs(600))
476 );
477 assert_eq!(
478 SignedEntityType::CardanoStakeDistribution(Epoch(1)).get_open_message_timeout(),
479 Some(Duration::from_secs(1800))
480 );
481 assert_eq!(
482 SignedEntityType::CardanoTransactions(Epoch(1), BlockNumber(1))
483 .get_open_message_timeout(),
484 Some(Duration::from_secs(600))
485 );
486 assert_eq!(
487 SignedEntityType::CardanoBlocksTransactions(Epoch(1), BlockNumber(1))
488 .get_open_message_timeout(),
489 Some(Duration::from_secs(600))
490 );
491 assert_eq!(
492 SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(1, 1))
493 .get_open_message_timeout(),
494 Some(Duration::from_secs(600))
495 );
496 }
497
498 #[test]
499 fn serialize_beacon_to_json() {
500 let cardano_stake_distribution_json = SignedEntityType::CardanoStakeDistribution(Epoch(25))
501 .get_json_beacon()
502 .unwrap();
503 assert_same_json!("25", &cardano_stake_distribution_json);
504
505 let cardano_transactions_json =
506 SignedEntityType::CardanoTransactions(Epoch(35), BlockNumber(77))
507 .get_json_beacon()
508 .unwrap();
509 assert_same_json!(
510 r#"{"epoch":35,"block_number":77}"#,
511 &cardano_transactions_json
512 );
513
514 let cardano_transactions_json =
515 SignedEntityType::CardanoBlocksTransactions(Epoch(35), BlockNumber(77))
516 .get_json_beacon()
517 .unwrap();
518 assert_same_json!(
519 r#"{"epoch":35,"block_number":77}"#,
520 &cardano_transactions_json
521 );
522
523 let cardano_immutable_files_full_json =
524 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::new(5, 100))
525 .get_json_beacon()
526 .unwrap();
527 assert_same_json!(
528 r#"{"epoch":5,"immutable_file_number":100}"#,
529 &cardano_immutable_files_full_json
530 );
531
532 let msd_json = SignedEntityType::MithrilStakeDistribution(Epoch(15))
533 .get_json_beacon()
534 .unwrap();
535 assert_same_json!("15", &msd_json);
536
537 let cardano_database_full_json =
538 SignedEntityType::CardanoDatabase(CardanoDbBeacon::new(12, 987))
539 .get_json_beacon()
540 .unwrap();
541 assert_same_json!(
542 r#"{"epoch":12,"immutable_file_number":987}"#,
543 &cardano_database_full_json
544 );
545 }
546
547 #[test]
548 fn bytes_encoding() {
549 let cardano_stake_distribution = SignedEntityType::CardanoStakeDistribution(Epoch(25));
550 let cardano_stake_distribution_bytes = cardano_stake_distribution.to_bytes_vec().unwrap();
551 let cardano_stake_distribution_from_bytes =
552 SignedEntityType::try_from_bytes(&cardano_stake_distribution_bytes).unwrap();
553
554 assert_eq!(
555 cardano_stake_distribution,
556 cardano_stake_distribution_from_bytes
557 );
558 }
559
560 #[test]
561 fn iter_all_discriminants() {
562 let discriminants: Vec<SignedEntityTypeDiscriminants> =
563 SignedEntityTypeDiscriminants::iter_all().collect();
564
565 assert_eq!(
566 discriminants,
567 vec![
568 SignedEntityTypeDiscriminants::MithrilStakeDistribution,
569 SignedEntityTypeDiscriminants::CardanoStakeDistribution,
570 SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
571 SignedEntityTypeDiscriminants::CardanoDatabase,
572 SignedEntityTypeDiscriminants::CardanoTransactions,
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}