avalanche_types/choices/
test_decidable.rs

1//! Test for decidable consensus operations.
2use crate::{
3    choices::{decidable::Decidable, status::Status},
4    errors::{Error, Result},
5    ids::Id,
6};
7
8#[derive(Clone, Debug)]
9pub struct TestDecidable {
10    pub id: Id,
11
12    /// "Status" enum uses String
13    /// so cannot implement/derive "Copy" to use "Cell"
14    /// ref. <https://stackoverflow.com/questions/38215753/how-do-i-implement-copy-and-clone-for-a-type-that-contains-a-string-or-any-type>
15    ///
16    /// Use "Box" instead to overwrite.
17    pub status: Box<Status>,
18
19    pub accept_result: Result<()>,
20    pub reject_result: Result<()>,
21}
22
23impl Default for TestDecidable {
24    fn default() -> Self {
25        Self {
26            id: Id::empty(),
27
28            status: Box::new(Status::Processing),
29
30            accept_result: Ok(()),
31            reject_result: Ok(()),
32        }
33    }
34}
35
36impl TestDecidable {
37    pub fn new(id: Id, status: Status) -> Self {
38        Self {
39            id,
40            status: Box::new(status),
41            accept_result: Ok(()),
42            reject_result: Ok(()),
43        }
44    }
45
46    pub fn set_accept_result(&mut self, rs: Result<()>) {
47        self.accept_result = rs;
48    }
49
50    pub fn set_reject_result(&mut self, rs: Result<()>) {
51        self.reject_result = rs;
52    }
53
54    pub fn create_decidable(
55        id: Id,
56        status: Status,
57        accept_result: Result<()>,
58        reject_result: Result<()>,
59    ) -> impl Decidable {
60        Self {
61            id,
62            status: Box::new(status),
63            accept_result,
64            reject_result,
65        }
66    }
67}
68
69impl Decidable for TestDecidable {
70    fn id(&self) -> Id {
71        self.id
72    }
73
74    fn status(&self) -> Status {
75        Status::from(self.status.as_str())
76    }
77
78    fn accept(&mut self) -> Result<()> {
79        let status = self.status.as_ref();
80        if matches!(status, Status::Unknown(_) | Status::Rejected) {
81            return Err(Error::Other {
82                message: format!(
83                    "invalid state transaction from {} to {}",
84                    status,
85                    Status::Accepted
86                ),
87                retryable: false,
88            });
89        }
90        if self.accept_result.is_ok() {
91            self.status = Box::new(Status::Accepted);
92        }
93
94        self.accept_result.clone()
95    }
96
97    fn reject(&mut self) -> Result<()> {
98        let status = self.status.as_ref();
99        if matches!(status, Status::Unknown(_) | Status::Accepted) {
100            return Err(Error::Other {
101                message: format!(
102                    "invalid state transaction from {} to {}",
103                    status,
104                    Status::Rejected
105                ),
106                retryable: false,
107            });
108        }
109        if self.reject_result.is_ok() {
110            self.status = Box::new(Status::Rejected);
111        }
112
113        self.reject_result.clone()
114    }
115}
116
117/// RUST_LOG=debug cargo test --package avalanche-consensus --lib -- decidable::test_decidable::test_decidable --exact --show-output
118#[test]
119fn test_decidable() {
120    let id = Id::from_slice(&[1, 2, 3]);
121
122    let mut decidable = TestDecidable::create_decidable(id, Status::Processing, Ok(()), Ok(()));
123    assert_eq!(decidable.id(), id);
124    assert_eq!(decidable.status(), Status::Processing);
125    assert!(decidable.accept().is_ok());
126    assert_eq!(decidable.status(), Status::Accepted);
127
128    let mut decidable = TestDecidable::create_decidable(id, Status::Processing, Ok(()), Ok(()));
129    assert_eq!(decidable.id(), id);
130    assert_eq!(decidable.status(), Status::Processing);
131    assert!(decidable.reject().is_ok());
132    assert_eq!(decidable.status(), Status::Rejected);
133
134    let mut decidable = TestDecidable::new(id, Status::Processing);
135    decidable.set_accept_result(Err(Error::Other {
136        message: "test error".to_string(),
137        retryable: false,
138    }));
139    assert_eq!(decidable.id(), id);
140    assert_eq!(decidable.status(), Status::Processing);
141    assert!(decidable.accept().is_err());
142    assert_eq!(decidable.status(), Status::Processing);
143
144    let mut decidable = TestDecidable::create_decidable(
145        id,
146        Status::Processing,
147        Ok(()),
148        Err(Error::Other {
149            message: "test error".to_string(),
150            retryable: false,
151        }),
152    );
153    assert_eq!(decidable.id(), id);
154    assert_eq!(decidable.status(), Status::Processing);
155    assert!(decidable.reject().is_err());
156    assert_eq!(decidable.status(), Status::Processing);
157}