diff --git a/storage/src/nullifier.rs b/storage/src/nullifier.rs index 050c180..5033fa6 100644 --- a/storage/src/nullifier.rs +++ b/storage/src/nullifier.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::merkle_tree_public::TreeHashType; //ToDo: Update Nullifier model, when it is clear -#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] ///General nullifier object pub struct UTXONullifier { pub utxo_hash: TreeHashType, diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs index d076598..834c4c1 100644 --- a/utxo/src/utxo_core.rs +++ b/utxo/src/utxo_core.rs @@ -6,7 +6,7 @@ use storage::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, Accoun ///Raw asset data pub type Asset = Vec; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] ///Container for raw utxo payload pub struct UTXO { pub hash: TreeHashType, @@ -53,3 +53,93 @@ impl UTXO { Ok(serde_json::from_slice(&self.asset)?) } } + +#[cfg(test)] +mod tests { + use super::*; + use storage::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, AccountId}; + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct TestAsset { + id: u32, + name: String, + } + + fn sample_account() -> AccountId { + AccountId::default() + } + + fn sample_nullifier() -> UTXONullifier { + UTXONullifier::default() + } + + fn sample_tree_hash() -> TreeHashType { + TreeHashType::default() + } + + fn sample_payload() -> UTXOPayload { + UTXOPayload { + owner: sample_account(), + asset: serde_json::to_vec(&TestAsset { + id: 1, + name: "Test".to_string(), + }) + .unwrap(), + } + } + + #[test] + fn test_create_utxo_from_payload() { + let payload = sample_payload(); + let utxo = UTXO::create_utxo_from_payload(payload.clone()); + + // Ensure hash is created and the UTXO fields are correctly assigned + assert_eq!(utxo.owner, payload.owner); + assert_eq!(utxo.asset, payload.asset); + assert!(utxo.nullifier.is_none()); + } + + #[test] + fn test_consume_utxo() { + let payload = sample_payload(); + let mut utxo = UTXO::create_utxo_from_payload(payload); + + let nullifier = sample_nullifier(); + + // First consumption should succeed + assert!(utxo.consume_utxo(nullifier.clone()).is_ok()); + assert_eq!(utxo.nullifier, Some(nullifier)); + + // Second consumption should fail + let result = utxo.consume_utxo(sample_nullifier()); + assert!(result.is_err()); + } + + #[test] + fn test_interpret_asset() { + let payload = sample_payload(); + let utxo = UTXO::create_utxo_from_payload(payload); + + // Interpret asset as TestAsset + let interpreted: TestAsset = utxo.interpret_asset().unwrap(); + + assert_eq!( + interpreted, + TestAsset { + id: 1, + name: "Test".to_string() + } + ); + } + + #[test] + fn test_interpret_invalid_asset() { + let mut payload = sample_payload(); + payload.asset = vec![0, 1, 2, 3]; // Invalid data for deserialization + let utxo = UTXO::create_utxo_from_payload(payload); + + // This should fail because the asset is not valid JSON for TestAsset + let result: Result = utxo.interpret_asset(); + assert!(result.is_err()); + } +} diff --git a/utxo/src/utxo_tree.rs b/utxo/src/utxo_tree.rs index 147c458..b391fc0 100644 --- a/utxo/src/utxo_tree.rs +++ b/utxo/src/utxo_tree.rs @@ -72,3 +72,118 @@ impl Default for UTXOSparseMerkleTree { Self::new() } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::utxo_core::{UTXOPayload, UTXO}; + use storage::{merkle_tree_public::TreeHashType, AccountId}; + + fn sample_utxo_payload() -> UTXOPayload { + UTXOPayload { + owner: AccountId::default(), + asset: vec![1, 2, 3], + } + } + + fn sample_utxo() -> UTXO { + UTXO::create_utxo_from_payload(sample_utxo_payload()) + } + + #[test] + fn test_utxo_sparse_merkle_tree_new() { + let smt = UTXOSparseMerkleTree::new(); + assert!(smt.curr_root.is_none()); + assert_eq!(smt.store.len(), 0); + } + + #[test] + fn test_insert_item() { + let mut smt = UTXOSparseMerkleTree::new(); + let utxo = sample_utxo(); + + let result = smt.insert_item(utxo.clone()); + + // Test insertion is successful + assert!(result.is_ok()); + + // Test UTXO is now stored in the tree + assert_eq!(smt.store.get(&utxo.hash).unwrap().hash, utxo.hash); + + // Test curr_root is updated + assert!(smt.curr_root.is_some()); + } + + #[test] + fn test_insert_items() { + let mut smt = UTXOSparseMerkleTree::new(); + let utxo1 = sample_utxo(); + let utxo2 = sample_utxo(); + + let result = smt.insert_items(vec![utxo1.clone(), utxo2.clone()]); + + // Test insertion of multiple items is successful + assert!(result.is_ok()); + + // Test UTXOs are now stored in the tree + assert!(smt.store.get(&utxo1.hash).is_some()); + assert!(smt.store.get(&utxo2.hash).is_some()); + + // Test curr_root is updated + assert!(smt.curr_root.is_some()); + } + + #[test] + fn test_get_item_exists() { + let mut smt = UTXOSparseMerkleTree::new(); + let utxo = sample_utxo(); + + smt.insert_item(utxo.clone()).unwrap(); + + // Test that the UTXO can be retrieved by hash + let retrieved_utxo = smt.get_item(utxo.hash).unwrap(); + assert!(retrieved_utxo.is_some()); + assert_eq!(retrieved_utxo.unwrap().hash, utxo.hash); + } + + #[test] + fn test_get_item_not_exists() { + let mut smt = UTXOSparseMerkleTree::new(); + let utxo = sample_utxo(); + + // Insert one UTXO and try to fetch a different hash + smt.insert_item(utxo).unwrap(); + + let non_existent_hash = TreeHashType::default(); + let result = smt.get_item(non_existent_hash).unwrap(); + + // Test that retrieval for a non-existent UTXO returns None + assert!(result.is_none()); + } + + #[test] + fn test_get_membership_proof() { + let mut smt = UTXOSparseMerkleTree::new(); + let utxo = sample_utxo(); + + smt.insert_item(utxo.clone()).unwrap(); + + // Fetch membership proof for the inserted UTXO + let proof = smt.get_membership_proof(utxo.hash).unwrap(); + + // Test proof is generated successfully + assert!(proof.is_some()); + } + + #[test] + fn test_get_membership_proof_not_exists() { + let mut smt = UTXOSparseMerkleTree::new(); + + // Try fetching proof for a non-existent UTXO hash + let non_existent_hash = TreeHashType::default(); + let proof = smt.get_membership_proof(non_existent_hash).unwrap(); + + // Test no proof is generated for a non-existent UTXO + assert!(proof.is_none()); + } +}