Skip to main content

dpp/tests/utils/
mod.rs

1use anyhow::Result;
2use dashcore::block::Version;
3use dashcore::hashes::Hash;
4use dashcore::{Block, BlockHash, CompactTarget, Header, TxMerkleNode};
5use platform_value::Value;
6#[cfg(test)]
7use serde_json::Value as JsonValue;
8
9use crate::prelude::Identifier;
10
11#[cfg(test)]
12#[macro_export]
13macro_rules! assert_error_contains {
14    ($result:ident, $contains:expr) => {
15        match $result {
16            Ok(o) => {
17                panic!("expected error, but returned: {:?}", o);
18            }
19            Err(e) => {
20                let string_error = e.to_string();
21                if !string_error.contains($contains) {
22                    panic!(
23                        "assertion error: '{}' hasn't been found in '{}'",
24                        $contains, string_error
25                    );
26                }
27            }
28        }
29    };
30}
31
32/// Sets a key value pair in serde_json object, returns the modified object
33pub fn serde_set<T, S>(mut object: serde_json::Value, key: T, value: S) -> serde_json::Value
34where
35    T: Into<String>,
36    S: Into<serde_json::Value>,
37    serde_json::Value: From<S>,
38{
39    let map = object
40        .as_object_mut()
41        .expect("Expected value to be an JSON object");
42    map.insert(key.into(), serde_json::Value::from(value));
43
44    object
45}
46
47/// Sets a key value pair in serde_json object, returns the modified object
48pub fn platform_value_set_ref<T, S>(object: &mut Value, key: T, value: S)
49where
50    T: Into<Value>,
51    S: Into<Value>,
52    Value: From<S>,
53{
54    let map = object
55        .as_map_mut()
56        .expect("Expected value to be an JSON object");
57    map.push((key.into(), value.into()));
58}
59
60/// Recursively collapse sized integer variants in a `Value` tree to the
61/// shape JSON can preserve.
62///
63/// JSON has a single `Number` type, so a round-trip through `serde_json::Value`
64/// erases the distinction between `Value::U32` / `Value::U16` / `Value::U8` /
65/// `Value::I32` / `Value::I16` / `Value::I8` and lands on `Value::U64` (for
66/// non-negatives) or `Value::I64` (for negatives). This helper normalizes a
67/// `Value` tree to the same projection so that `assert_eq!(canonical(original),
68/// canonical(recovered))` is meaningful for JSON-round-trip tests of
69/// schema-bearing types like `DataContract`'s `document_schemas`.
70///
71/// The normalization is intentionally lossy on the same axis JSON itself is
72/// lossy on. Use only in tests where you need to compare values modulo
73/// sized-int distinction.
74pub fn normalize_integer_variants_for_json_round_trip(value: &mut Value) {
75    match value {
76        Value::U8(v) => *value = Value::U64(*v as u64),
77        Value::U16(v) => *value = Value::U64(*v as u64),
78        Value::U32(v) => *value = Value::U64(*v as u64),
79        Value::I8(v) => {
80            *value = if *v < 0 {
81                Value::I64(*v as i64)
82            } else {
83                Value::U64(*v as u64)
84            }
85        }
86        Value::I16(v) => {
87            *value = if *v < 0 {
88                Value::I64(*v as i64)
89            } else {
90                Value::U64(*v as u64)
91            }
92        }
93        Value::I32(v) => {
94            *value = if *v < 0 {
95                Value::I64(*v as i64)
96            } else {
97                Value::U64(*v as u64)
98            }
99        }
100        Value::I64(v) if *v >= 0 => *value = Value::U64(*v as u64),
101        Value::Array(items) => {
102            for item in items.iter_mut() {
103                normalize_integer_variants_for_json_round_trip(item);
104            }
105        }
106        Value::Map(entries) => {
107            for (k, v) in entries.iter_mut() {
108                normalize_integer_variants_for_json_round_trip(k);
109                normalize_integer_variants_for_json_round_trip(v);
110            }
111        }
112        _ => {}
113    }
114}
115
116pub fn generate_random_identifier_struct() -> Identifier {
117    let mut buffer = [0u8; 32];
118    getrandom::getrandom(&mut buffer).unwrap();
119    Identifier::from_bytes(&buffer).unwrap()
120}
121
122pub fn get_data_from_file(file_path: &str) -> Result<String> {
123    let current_dir = std::env::current_dir()?;
124    let file_path = format!("{}/{}", current_dir.display(), file_path);
125    let d = std::fs::read_to_string(file_path)?;
126    Ok(d)
127}
128
129#[cfg(test)]
130pub trait SerdeTestExtension {
131    fn remove_key(&mut self, key: impl Into<String>);
132    fn set_key_value<T, S>(&mut self, key: T, value: S)
133    where
134        T: Into<String>,
135        S: Into<serde_json::Value>,
136        serde_json::Value: From<S>;
137    fn get_value(&self, key: impl Into<String>) -> &serde_json::Value;
138    fn get_value_mut(&mut self, key: impl Into<String>) -> &mut serde_json::Value;
139}
140
141#[cfg(test)]
142impl SerdeTestExtension for serde_json::Value {
143    fn remove_key(&mut self, key: impl Into<String>) {
144        self.as_object_mut()
145            .expect("Expected value to be an JSON object")
146            .remove(&key.into());
147    }
148
149    fn set_key_value<T, S>(&mut self, key: T, value: S)
150    where
151        T: Into<String>,
152        S: Into<JsonValue>,
153        JsonValue: From<S>,
154    {
155        let map = self
156            .as_object_mut()
157            .expect("Expected value to be an JSON object");
158        map.insert(key.into(), serde_json::Value::from(value));
159    }
160
161    fn get_value(&self, key: impl Into<String>) -> &JsonValue {
162        self.as_object()
163            .expect("Expected key to exist")
164            .get(&key.into())
165            .expect("Expected key to exist")
166    }
167
168    fn get_value_mut(&mut self, key: impl Into<String>) -> &mut JsonValue {
169        self.as_object_mut()
170            .expect("Expected key to exist")
171            .get_mut(&key.into())
172            .expect("Expected key to exist")
173    }
174}
175
176// fn byte_to_hex(byte: &u8) -> String {
177//     format!("{:02x}", byte)
178// }
179//
180// /// Serializes bytes into a hex string
181// pub fn encode_hex<T: Clone + Into<Vec<u8>>>(bytes: &T) -> String {
182//     let hex_vec: Vec<String> = bytes.clone().into().iter().map(byte_to_hex).collect();
183//
184//     hex_vec.join("")
185// }
186
187/// Assert that all validation error belong to a certain enum variant and
188/// extracts all the errors from enum to a vector
189#[macro_export]
190macro_rules! assert_consensus_errors {
191    ($validation_result: expr, $variant: path, $expected_errors_count: expr) => {{
192        if $validation_result.errors.len() != $expected_errors_count {
193            for error in $validation_result.errors.iter() {
194                println!("{:?}", error);
195            }
196        }
197
198        assert_eq!($validation_result.errors.len(), $expected_errors_count);
199
200        let mut errors = Vec::new();
201
202        for error in &$validation_result.errors {
203            match error {
204                $variant(err) => errors.push(err),
205                err => {
206                    panic!("Got error that differs from what was expected: {:?}", err)
207                }
208            }
209        }
210
211        errors
212    }};
213}
214
215/// Assert that all validation error belong to a certain enum variant of basic consensus errors
216/// and extracts all the errors from enum to a vector
217#[macro_export]
218macro_rules! assert_basic_consensus_errors {
219    ($validation_result: expr, $variant: path, $expected_errors_count: expr) => {{
220        if $validation_result.errors.len() != $expected_errors_count {
221            for error in $validation_result.errors.iter() {
222                println!("{:?}", error);
223            }
224        }
225
226        assert_eq!($validation_result.errors.len(), $expected_errors_count);
227
228        let mut errors = Vec::new();
229
230        for error in &$validation_result.errors {
231            match error {
232                ConsensusError::BasicError($variant(err)) => errors.push(err),
233                err => {
234                    panic!("Got error that differs from what was expected: {:?}", err)
235                }
236            }
237        }
238
239        errors
240    }};
241}
242
243/// Assert that all validation error belong to a certain enum variant of state consensus errors
244/// and extracts all the errors from enum to a vector
245#[macro_export]
246macro_rules! assert_state_consensus_errors {
247    ($validation_result: expr, $variant: path, $expected_errors_count: expr) => {{
248        if $validation_result.errors.len() != $expected_errors_count {
249            for error in $validation_result.errors.iter() {
250                println!("{:?}", error);
251            }
252        }
253
254        assert_eq!($validation_result.errors.len(), $expected_errors_count);
255
256        let mut errors = Vec::new();
257
258        for error in &$validation_result.errors {
259            match error {
260                ConsensusError::StateError($variant(err)) => errors.push(err),
261                err => {
262                    panic!("Got error that differs from what was expected: {:?}", err)
263                }
264            }
265        }
266
267        errors
268    }};
269}
270
271pub fn create_empty_block(timestamp_secs: Option<u32>) -> Block {
272    Block {
273        txdata: vec![],
274        header: new_block_header(timestamp_secs),
275    }
276}
277
278pub fn new_block_header(timestamp_secs: Option<u32>) -> Header {
279    Header {
280        bits: CompactTarget::default(),
281        nonce: 0,
282        merkle_root: TxMerkleNode::all_zeros(),
283        prev_blockhash: BlockHash::all_zeros(),
284        version: Version::default(),
285        time: timestamp_secs.unwrap_or_default(),
286    }
287}