b3-users 0.1.4

A simple user management system for the Internet Computer
Documentation
// chain.rs
use super::transaction::UserTransactionData;
use crate::error::UserStateError;
use ic_cdk::export::{candid::CandidType, serde::Deserialize};

#[derive(Debug, CandidType, Deserialize, Clone, PartialEq)]
pub struct UserChainData {
    pub nonce: u64,
    pub transactions: Vec<UserTransactionData>,
}

impl UserChainData {
    /// Creates a default ChainData with nonce 0 and an empty transactions vector.
    pub fn default() -> Self {
        UserChainData {
            nonce: 0,
            transactions: vec![],
        }
    }

    pub fn new(transaction: UserTransactionData) -> Self {
        UserChainData {
            nonce: 0,
            transactions: vec![transaction],
        }
    }

    /// Adds a new transaction to the chain and updates the nonce.
    pub fn add(&mut self, nonce: u64, transaction: UserTransactionData) {
        self.nonce = nonce;
        self.transactions.push(transaction);
    }

    /// Removes a transaction from the specified chain and updates the ChainData.
    /// Returns an error if the chain_id is not found.
    /// Returns an error if the transaction index is not found.
    pub fn remove(&mut self, index: usize) -> Result<&UserTransactionData, UserStateError> {
        if index >= self.transactions.len() {
            return Err(UserStateError::TransactionNotFound);
        }

        self.transactions.remove(index);

        self.get_transaction(index)
    }

    /// Returns a reference to the transaction at the specified index.
    pub fn get_transaction(&self, index: usize) -> Result<&UserTransactionData, UserStateError> {
        self.transactions
            .get(index)
            .ok_or(UserStateError::TransactionNotFound)
    }

    pub fn get_transactions(&self) -> &Vec<UserTransactionData> {
        &self.transactions
    }

    /// Adds a transaction to the specified chain and updates the ChainData.
    /// Returns an error if the chain_id is not found.
    pub fn add_transaction(
        &mut self,
        nonce: u64,
        transaction: UserTransactionData,
    ) -> Result<&UserTransactionData, UserStateError> {
        self.add(nonce, transaction);

        self.get_transaction(self.transactions.len() - 1)
    }

    /// Clears the transactions vector for the specified chain.
    /// Returns an error if the chain_id is not found.
    pub fn clear_transactions(&mut self) {
        self.transactions.clear();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn test_add_transaction(nonce: u64, transaction: UserTransactionData) {
            let mut chain_data = UserChainData::default();

            chain_data.add(nonce, transaction.clone());

            assert_eq!(chain_data.nonce, nonce);
            assert_eq!(chain_data.transactions, vec![transaction]);
        }

        #[test]
        fn test_clear_transactions(transaction: Vec<UserTransactionData>) {
            let mut chain_data = UserChainData::default();

            chain_data.transactions = transaction.clone();

            chain_data.clear_transactions();

            assert_eq!(chain_data.nonce, 0);

            assert_eq!(chain_data.transactions, vec![]);
        }

        #[test]
        fn test_get_transaction(transaction: Vec<UserTransactionData>) {
            let mut chain_data = UserChainData::default();

            chain_data.transactions = transaction.clone();

            for i in 0..chain_data.transactions.len() {
                assert_eq!(chain_data.get_transaction(i).unwrap(), &transaction[i]);
            }
        }

        #[test]
        fn test_get_transactions(transaction: Vec<UserTransactionData>) {
            let mut chain_data = UserChainData::default();

            chain_data.transactions = transaction.clone();

            assert_eq!(chain_data.get_transactions(), &transaction);
        }

        #[test]
        fn test_add_transaction_error(transaction: Vec<UserTransactionData>) {
            let mut chain_data = UserChainData::default();

            chain_data.transactions = transaction.clone();

            let result = chain_data.get_transaction(chain_data.transactions.len());

            match result {
                Err(UserStateError::TransactionNotFound) => (),
                _ => panic!("Expected TransactionNotFound error"),
            }
        }

        #[test]
        fn test_add_transaction_to_chain(nonce: u64, transaction: UserTransactionData) {
            let mut chain_data = UserChainData::default();

            chain_data.add_transaction(nonce, transaction.clone()).unwrap();

            assert_eq!(chain_data.nonce, nonce);
            assert_eq!(chain_data.transactions, vec![transaction]);
        }

        #[test]
        fn test_add_transaction_to_chain_error(nonce: u64, transaction: UserTransactionData) {
            let mut chain_data = UserChainData::default();

            chain_data.add_transaction(nonce, transaction.clone()).unwrap();

            let result = chain_data.get_transaction(chain_data.transactions.len());

            match result {
                Err(UserStateError::TransactionNotFound) => (),
                _ => panic!("Expected TransactionNotFound error"),
            }
        }

        #[test]
        fn test_clear_transactions_error(transaction: Vec<UserTransactionData>) {
            let mut chain_data = UserChainData::default();

            chain_data.transactions = transaction.clone();

            chain_data.clear_transactions();

            let result = chain_data.get_transaction(chain_data.transactions.len());

            match result {
                Err(UserStateError::TransactionNotFound) => (),
                _ => panic!("Expected TransactionNotFound error"),
            }
        }
    }
}