...
This commit is contained in:
parent
f993d74ea1
commit
6443c6b647
@ -1,297 +0,0 @@
|
||||
//! Integration tests for the zaz database module
|
||||
|
||||
use crate::core::{DB, DBBuilder, SledDBResult, Storable, SledModel, SledDB};
|
||||
use crate::zaz::models::user::User;
|
||||
use crate::zaz::models::company::{Company, BusinessType, CompanyStatus};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tempfile::tempdir;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Test the basic database functionality
|
||||
#[test]
|
||||
fn test_basic_database_operations() {
|
||||
match run_comprehensive_test() {
|
||||
Ok(_) => println!("All tests passed successfully!"),
|
||||
Err(e) => panic!("Error running tests: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_comprehensive_test() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create a temporary directory for testing
|
||||
let temp_dir = tempdir()?;
|
||||
println!("Using temporary directory: {:?}", temp_dir.path());
|
||||
|
||||
println!("\n--- Testing User operations ---");
|
||||
test_user_operations(temp_dir.path())?;
|
||||
|
||||
println!("\n--- Testing Company operations ---");
|
||||
test_company_operations(temp_dir.path())?;
|
||||
|
||||
println!("\n--- Testing Transaction Simulation ---");
|
||||
test_transaction_simulation(temp_dir.path())?;
|
||||
|
||||
// Clean up
|
||||
drop(temp_dir);
|
||||
|
||||
println!("All comprehensive tests completed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_user_operations(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Open the user database
|
||||
let db = SledDB::<User>::open(base_path.join("users"))?;
|
||||
println!("Opened user database at: {:?}", base_path.join("users"));
|
||||
|
||||
// Create a test user
|
||||
let user = User::new(
|
||||
100,
|
||||
"Test User".to_string(),
|
||||
"test@example.com".to_string(),
|
||||
"password123".to_string(),
|
||||
"Test Company".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
|
||||
// Insert the user
|
||||
db.insert(&user)?;
|
||||
println!("Inserted user: {}", user.name);
|
||||
|
||||
// Retrieve the user
|
||||
let retrieved_user = db.get(&user.id.to_string())?;
|
||||
println!("Retrieved user: {}", retrieved_user.name);
|
||||
assert_eq!(user.name, retrieved_user.name);
|
||||
assert_eq!(user.email, retrieved_user.email);
|
||||
|
||||
// Update the user
|
||||
let updated_user = User::new(
|
||||
100,
|
||||
"Updated User".to_string(),
|
||||
"updated@example.com".to_string(),
|
||||
"newpassword".to_string(),
|
||||
"New Company".to_string(),
|
||||
"SuperAdmin".to_string(),
|
||||
);
|
||||
|
||||
db.insert(&updated_user)?;
|
||||
println!("Updated user: {}", updated_user.name);
|
||||
|
||||
// Retrieve the updated user
|
||||
let retrieved_user = db.get(&user.id.to_string())?;
|
||||
println!("Retrieved updated user: {}", retrieved_user.name);
|
||||
assert_eq!(updated_user.name, retrieved_user.name);
|
||||
assert_eq!(updated_user.email, retrieved_user.email);
|
||||
|
||||
// Delete the user
|
||||
db.delete(&user.id.to_string())?;
|
||||
println!("Deleted user: {}", user.name);
|
||||
|
||||
// Try to retrieve the deleted user (should fail)
|
||||
let result = db.get(&user.id.to_string());
|
||||
assert!(result.is_err(), "User should be deleted");
|
||||
println!("Verified user was deleted");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_company_operations(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Open the company database
|
||||
let db = SledDB::<Company>::open(base_path.join("companies"))?;
|
||||
println!("Opened company database at: {:?}", base_path.join("companies"));
|
||||
|
||||
// Create a test company
|
||||
let company = Company::new(
|
||||
100,
|
||||
"Test Corp".to_string(),
|
||||
"TEST123".to_string(),
|
||||
Utc::now(),
|
||||
"12-31".to_string(),
|
||||
"test@corp.com".to_string(),
|
||||
"123-456-7890".to_string(),
|
||||
"www.testcorp.com".to_string(),
|
||||
"123 Test St".to_string(),
|
||||
BusinessType::new(BusinessType::GLOBAL.to_string())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Warning: {}", e);
|
||||
BusinessType::new_unchecked(BusinessType::GLOBAL.to_string())
|
||||
}),
|
||||
"Technology".to_string(),
|
||||
"A test company".to_string(),
|
||||
CompanyStatus::Active,
|
||||
);
|
||||
|
||||
// Insert the company
|
||||
db.insert(&company)?;
|
||||
println!("Inserted company: {}", company.name);
|
||||
|
||||
// Retrieve the company
|
||||
let retrieved_company = db.get(&company.id.to_string())?;
|
||||
println!("Retrieved company: {}", retrieved_company.name);
|
||||
assert_eq!(company.name, retrieved_company.name);
|
||||
|
||||
// List all companies
|
||||
let companies = db.list()?;
|
||||
println!("Found {} companies", companies.len());
|
||||
assert_eq!(companies.len(), 1);
|
||||
|
||||
// Delete the company
|
||||
db.delete(&company.id.to_string())?;
|
||||
println!("Deleted company: {}", company.name);
|
||||
|
||||
// List companies again (should be empty)
|
||||
let companies = db.list()?;
|
||||
assert_eq!(companies.len(), 0);
|
||||
println!("Verified company was deleted");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_transaction_simulation(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create a DB instance with User model registered
|
||||
let mut db = DB::new(base_path.join("transaction"))?;
|
||||
db.register::<User>()?;
|
||||
|
||||
println!("Created DB with User model registered at: {:?}", base_path.join("transaction"));
|
||||
|
||||
// Add a user outside of transaction
|
||||
let user = User::new(
|
||||
200,
|
||||
"Transaction Test".to_string(),
|
||||
"tx@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"TX Corp".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
// Add the user without a transaction
|
||||
db.set(&user)?;
|
||||
println!("Added initial user: {}", user.name);
|
||||
|
||||
// Begin a transaction
|
||||
db.begin_transaction()?;
|
||||
println!("Transaction started");
|
||||
|
||||
// Update user in transaction
|
||||
let updated_user = User::new(
|
||||
200,
|
||||
"Updated in TX".to_string(),
|
||||
"updated@example.com".to_string(),
|
||||
"newpass".to_string(),
|
||||
"New Corp".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
db.set(&updated_user)?;
|
||||
println!("Updated user in transaction");
|
||||
|
||||
// Add new user in transaction
|
||||
let new_user = User::new(
|
||||
201,
|
||||
"New in TX".to_string(),
|
||||
"new@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"New Corp".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
db.set(&new_user)?;
|
||||
println!("Added new user in transaction");
|
||||
|
||||
// Verify transaction changes are visible within transaction
|
||||
let tx_user: User = db.get(&user.id.to_string())?;
|
||||
assert_eq!(tx_user.name, "Updated in TX");
|
||||
println!("Verified transaction changes are visible");
|
||||
|
||||
// Commit the transaction
|
||||
db.commit_transaction()?;
|
||||
println!("Transaction committed");
|
||||
|
||||
// Verify changes persisted after commit
|
||||
let committed_user: User = db.get(&user.id.to_string())?;
|
||||
assert_eq!(committed_user.name, "Updated in TX");
|
||||
println!("Verified changes persisted after commit");
|
||||
|
||||
// Test transaction rollback
|
||||
|
||||
// Begin another transaction
|
||||
db.begin_transaction()?;
|
||||
println!("New transaction started");
|
||||
|
||||
// Make changes that will be rolled back
|
||||
let rollback_user = User::new(
|
||||
200,
|
||||
"Will Be Rolled Back".to_string(),
|
||||
"rollback@example.com".to_string(),
|
||||
"temppass".to_string(),
|
||||
"Temp Corp".to_string(),
|
||||
"TempAdmin".to_string(),
|
||||
);
|
||||
db.set(&rollback_user)?;
|
||||
println!("Updated user in transaction that will be rolled back");
|
||||
|
||||
// Rollback the transaction
|
||||
db.rollback_transaction()?;
|
||||
println!("Transaction rolled back");
|
||||
|
||||
// Verify original data is intact
|
||||
let after_rollback: User = db.get(&user.id.to_string())?;
|
||||
assert_eq!(after_rollback.name, "Updated in TX");
|
||||
println!("Verified data is intact after rollback: {}", after_rollback.name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_db() {
|
||||
// A simpler test that uses a basic DB setup
|
||||
|
||||
// Test model
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct User {
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn new(id: u32, name: String, email: String) -> Self {
|
||||
Self { id, name, email }
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for User {}
|
||||
|
||||
impl SledModel for User {
|
||||
fn get_id(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
|
||||
fn db_prefix() -> &'static str {
|
||||
"test_simple_user"
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary directory for the test
|
||||
let dir = tempdir().expect("Failed to create temp dir");
|
||||
|
||||
// Create a DB with the builder
|
||||
let db = DBBuilder::new(dir.path())
|
||||
.register_model::<User>()
|
||||
.build()
|
||||
.expect("Failed to build DB");
|
||||
|
||||
// Create a test user
|
||||
let user = User::new(1, "Simple Test User".to_string(), "simple@example.com".to_string());
|
||||
|
||||
// Set the user
|
||||
db.set(&user).expect("Failed to set user");
|
||||
|
||||
// Get the user
|
||||
let retrieved: User = db.get(&user.id.to_string()).expect("Failed to get user");
|
||||
|
||||
// Check that it matches
|
||||
assert_eq!(user.name, retrieved.name);
|
||||
assert_eq!(user.email, retrieved.email);
|
||||
|
||||
println!("Simple DB test passed!");
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
//! Tests for the Zaz module
|
||||
|
||||
// Re-export the test modules
|
||||
pub mod model_db_test;
|
||||
pub mod db_integration_test;
|
||||
pub mod transaction_test;
|
@ -1,194 +0,0 @@
|
||||
//! Tests for the new model-DB architecture
|
||||
|
||||
use crate::core::{DB, DBBuilder, SledDBResult, Storable, SledModel};
|
||||
use crate::zaz::factory::create_zaz_db;
|
||||
use crate::zaz::models::user::User;
|
||||
use crate::zaz::models::company::{Company, BusinessType, CompanyStatus};
|
||||
use crate::zaz::models::product::{Product, ProductStatus, ProductType, Currency};
|
||||
use chrono::Utc;
|
||||
use std::path::Path;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_zaz_db_factory() {
|
||||
// Create a temporary directory for testing
|
||||
let temp_dir = tempdir().expect("Failed to create temp directory");
|
||||
println!("Created temporary directory at: {:?}", temp_dir.path());
|
||||
|
||||
// Create a DB with all zaz models registered using the factory
|
||||
let db = create_zaz_db(temp_dir.path()).expect("Failed to create zaz DB");
|
||||
|
||||
// Test with a user model
|
||||
let user = User::new(
|
||||
1,
|
||||
"Factory Test User".to_string(),
|
||||
"factory@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Test Company".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
// Insert the user
|
||||
db.set(&user).expect("Failed to insert user");
|
||||
|
||||
// Retrieve the user
|
||||
let retrieved: User = db.get(&user.id.to_string()).expect("Failed to retrieve user");
|
||||
|
||||
// Verify the user is correct
|
||||
assert_eq!(user.name, retrieved.name);
|
||||
assert_eq!(user.email, retrieved.email);
|
||||
|
||||
// Test with a company model
|
||||
let company = Company::new(
|
||||
1,
|
||||
"Factory Test Corp".to_string(),
|
||||
"FTC-123".to_string(),
|
||||
Utc::now(),
|
||||
"12-31".to_string(),
|
||||
"info@ftc.com".to_string(),
|
||||
"123-456-7890".to_string(),
|
||||
"www.ftc.com".to_string(),
|
||||
"123 Factory St".to_string(),
|
||||
BusinessType::new(BusinessType::GLOBAL.to_string())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Warning: {}", e);
|
||||
BusinessType::new_unchecked(BusinessType::GLOBAL.to_string())
|
||||
}),
|
||||
"Technology".to_string(),
|
||||
"A test company for the factory pattern".to_string(),
|
||||
CompanyStatus::Active,
|
||||
);
|
||||
|
||||
// Insert the company
|
||||
db.set(&company).expect("Failed to insert company");
|
||||
|
||||
// Retrieve the company
|
||||
let retrieved: Company = db.get(&company.id.to_string()).expect("Failed to retrieve company");
|
||||
|
||||
// Verify the company is correct
|
||||
assert_eq!(company.name, retrieved.name);
|
||||
assert_eq!(company.registration_number, retrieved.registration_number);
|
||||
|
||||
println!("All zaz DB factory tests passed successfully!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_db_builder() {
|
||||
// Create a temporary directory for testing
|
||||
let temp_dir = tempdir().expect("Failed to create temp directory");
|
||||
println!("Created temporary directory at: {:?}", temp_dir.path());
|
||||
|
||||
// Create a DB with selectively registered models using the builder pattern
|
||||
let db = DBBuilder::new(temp_dir.path())
|
||||
.register_model::<User>()
|
||||
.register_model::<Company>()
|
||||
.build()
|
||||
.expect("Failed to build DB");
|
||||
|
||||
// Test with a user model
|
||||
let user = User::new(
|
||||
2,
|
||||
"Builder Test User".to_string(),
|
||||
"builder@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Test Company".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
// Insert the user
|
||||
db.set(&user).expect("Failed to insert user");
|
||||
|
||||
// Retrieve the user
|
||||
let retrieved: User = db.get(&user.id.to_string()).expect("Failed to retrieve user");
|
||||
|
||||
// Verify the user is correct
|
||||
assert_eq!(user.name, retrieved.name);
|
||||
assert_eq!(user.email, retrieved.email);
|
||||
|
||||
// Test that unregistered models cause an error
|
||||
let product = Product::new(
|
||||
1,
|
||||
"Unregistered Product".to_string(),
|
||||
"A test product".to_string(),
|
||||
Currency::new(100.0, "USD".to_string()),
|
||||
ProductType::Product,
|
||||
"Test".to_string(),
|
||||
ProductStatus::Available,
|
||||
10, // max_amount
|
||||
30, // validity_days
|
||||
);
|
||||
|
||||
// This should fail because Product was not registered
|
||||
let result = db.set(&product);
|
||||
assert!(result.is_err(), "Setting unregistered model should fail");
|
||||
|
||||
println!("All DB builder tests passed successfully!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dynamic_registration() {
|
||||
// Create a temporary directory for testing
|
||||
let temp_dir = tempdir().expect("Failed to create temp directory");
|
||||
println!("Created temporary directory at: {:?}", temp_dir.path());
|
||||
|
||||
// Create an empty DB
|
||||
let mut db = DB::new(temp_dir.path()).expect("Failed to create empty DB");
|
||||
|
||||
// Register User model dynamically
|
||||
db.register::<User>().expect("Failed to register User");
|
||||
|
||||
// Test with a user model
|
||||
let user = User::new(
|
||||
3,
|
||||
"Dynamic Test User".to_string(),
|
||||
"dynamic@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Test Company".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
// Insert the user
|
||||
db.set(&user).expect("Failed to insert user");
|
||||
|
||||
// Retrieve the user
|
||||
let retrieved: User = db.get(&user.id.to_string()).expect("Failed to retrieve user");
|
||||
|
||||
// Verify the user is correct
|
||||
assert_eq!(user.name, retrieved.name);
|
||||
assert_eq!(user.email, retrieved.email);
|
||||
|
||||
// Now dynamically register Company
|
||||
db.register::<Company>().expect("Failed to register Company");
|
||||
|
||||
// Test with a company model
|
||||
let company = Company::new(
|
||||
3,
|
||||
"Dynamic Test Corp".to_string(),
|
||||
"DTC-123".to_string(),
|
||||
Utc::now(),
|
||||
"12-31".to_string(),
|
||||
"info@dtc.com".to_string(),
|
||||
"123-456-7890".to_string(),
|
||||
"www.dtc.com".to_string(),
|
||||
"123 Dynamic St".to_string(),
|
||||
BusinessType::new(BusinessType::GLOBAL.to_string())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Warning: {}", e);
|
||||
BusinessType::new_unchecked(BusinessType::GLOBAL.to_string())
|
||||
}),
|
||||
"Technology".to_string(),
|
||||
"A test company for dynamic registration".to_string(),
|
||||
CompanyStatus::Active,
|
||||
);
|
||||
|
||||
// Insert the company
|
||||
db.set(&company).expect("Failed to insert company");
|
||||
|
||||
// Retrieve the company
|
||||
let retrieved: Company = db.get(&company.id.to_string()).expect("Failed to retrieve company");
|
||||
|
||||
// Verify the company is correct
|
||||
assert_eq!(company.name, retrieved.name);
|
||||
|
||||
println!("All dynamic registration tests passed successfully!");
|
||||
}
|
@ -1,265 +0,0 @@
|
||||
//! Transaction tests for the zaz database module
|
||||
|
||||
use sled;
|
||||
use bincode;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use tempfile::tempdir;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Test the transaction-like behavior capabilities
|
||||
#[test]
|
||||
fn test_transaction_operations() {
|
||||
match run_transaction_test() {
|
||||
Ok(_) => println!("All transaction tests passed successfully!"),
|
||||
Err(e) => panic!("Error in transaction tests: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_transaction_test() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create a temporary directory for testing
|
||||
let temp_dir = tempdir()?;
|
||||
println!("Using temporary directory: {:?}", temp_dir.path());
|
||||
|
||||
test_basic_transactions(temp_dir.path())?;
|
||||
test_rollback_behavior(temp_dir.path())?;
|
||||
test_concurrent_operations(temp_dir.path())?;
|
||||
|
||||
// Clean up
|
||||
drop(temp_dir);
|
||||
|
||||
println!("All transaction tests completed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// User model for testing
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct User {
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
balance: f64,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn new(id: u32, name: String, email: String, balance: f64) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
balance,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test basic transaction functionality
|
||||
fn test_basic_transactions(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Open the test database
|
||||
let db = sled::open(base_path.join("basic_tx"))?;
|
||||
println!("Opened basic transaction test database at: {:?}", base_path.join("basic_tx"));
|
||||
|
||||
// Create initial users
|
||||
let user1 = User::new(
|
||||
1,
|
||||
"User One".to_string(),
|
||||
"one@example.com".to_string(),
|
||||
100.0,
|
||||
);
|
||||
|
||||
let user2 = User::new(
|
||||
2,
|
||||
"User Two".to_string(),
|
||||
"two@example.com".to_string(),
|
||||
50.0,
|
||||
);
|
||||
|
||||
// Insert initial users
|
||||
db.insert(user1.id.to_string().as_bytes(), bincode::serialize(&user1)?)?;
|
||||
db.insert(user2.id.to_string().as_bytes(), bincode::serialize(&user2)?)?;
|
||||
db.flush()?;
|
||||
println!("Inserted initial users");
|
||||
|
||||
// Simulate a transaction - transfer 25.0 from user1 to user2
|
||||
println!("Starting transaction simulation: transfer 25.0 from user1 to user2");
|
||||
|
||||
// Create transaction workspace
|
||||
let mut tx_workspace = HashMap::new();
|
||||
|
||||
// Retrieve current state
|
||||
if let Some(data) = db.get(user1.id.to_string().as_bytes())? {
|
||||
let user: User = bincode::deserialize(&data)?;
|
||||
tx_workspace.insert(user1.id.to_string(), user);
|
||||
} else {
|
||||
return Err("Failed to find user1".into());
|
||||
}
|
||||
|
||||
if let Some(data) = db.get(user2.id.to_string().as_bytes())? {
|
||||
let user: User = bincode::deserialize(&data)?;
|
||||
tx_workspace.insert(user2.id.to_string(), user);
|
||||
} else {
|
||||
return Err("Failed to find user2".into());
|
||||
}
|
||||
|
||||
// Modify both users in the transaction
|
||||
let mut updated_user1 = tx_workspace.get(&user1.id.to_string()).unwrap().clone();
|
||||
let mut updated_user2 = tx_workspace.get(&user2.id.to_string()).unwrap().clone();
|
||||
|
||||
updated_user1.balance -= 25.0;
|
||||
updated_user2.balance += 25.0;
|
||||
|
||||
// Update the workspace
|
||||
tx_workspace.insert(user1.id.to_string(), updated_user1);
|
||||
tx_workspace.insert(user2.id.to_string(), updated_user2);
|
||||
|
||||
// Commit the transaction
|
||||
println!("Committing transaction");
|
||||
for (key, user) in tx_workspace {
|
||||
let user_bytes = bincode::serialize(&user)?;
|
||||
db.insert(key.as_bytes(), user_bytes)?;
|
||||
}
|
||||
db.flush()?;
|
||||
|
||||
// Verify the results
|
||||
if let Some(data) = db.get(user1.id.to_string().as_bytes())? {
|
||||
let final_user1: User = bincode::deserialize(&data)?;
|
||||
assert_eq!(final_user1.balance, 75.0, "User1 balance should be 75.0");
|
||||
println!("Verified user1 balance is now {}", final_user1.balance);
|
||||
} else {
|
||||
return Err("Failed to find user1 after transaction".into());
|
||||
}
|
||||
|
||||
if let Some(data) = db.get(user2.id.to_string().as_bytes())? {
|
||||
let final_user2: User = bincode::deserialize(&data)?;
|
||||
assert_eq!(final_user2.balance, 75.0, "User2 balance should be 75.0");
|
||||
println!("Verified user2 balance is now {}", final_user2.balance);
|
||||
} else {
|
||||
return Err("Failed to find user2 after transaction".into());
|
||||
}
|
||||
|
||||
// Clean up
|
||||
drop(db);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test transaction rollback functionality
|
||||
fn test_rollback_behavior(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Open the test database
|
||||
let db = sled::open(base_path.join("rollback_tx"))?;
|
||||
println!("Opened rollback test database at: {:?}", base_path.join("rollback_tx"));
|
||||
|
||||
// Create initial user
|
||||
let user = User::new(
|
||||
1,
|
||||
"Rollback Test".to_string(),
|
||||
"rollback@example.com".to_string(),
|
||||
100.0,
|
||||
);
|
||||
|
||||
// Insert initial user
|
||||
db.insert(user.id.to_string().as_bytes(), bincode::serialize(&user)?)?;
|
||||
db.flush()?;
|
||||
println!("Inserted initial user with balance: {}", user.balance);
|
||||
|
||||
// Simulate a transaction that shouldn't be committed
|
||||
println!("Starting transaction that will be rolled back");
|
||||
|
||||
// Create transaction workspace (we'd track in memory)
|
||||
let mut updated_user = user.clone();
|
||||
updated_user.balance = 0.0; // Drastic change
|
||||
|
||||
// Do NOT commit changes to the database (simulating rollback)
|
||||
println!("Rolling back transaction (by not writing changes)");
|
||||
|
||||
// Verify the original data is intact
|
||||
if let Some(data) = db.get(user.id.to_string().as_bytes())? {
|
||||
let final_user: User = bincode::deserialize(&data)?;
|
||||
assert_eq!(final_user.balance, 100.0, "User balance should remain 100.0");
|
||||
println!("Verified user balance is still {} after rollback", final_user.balance);
|
||||
} else {
|
||||
return Err("Failed to find user after rollback".into());
|
||||
}
|
||||
|
||||
// Clean up
|
||||
drop(db);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test multiple operations that might happen concurrently
|
||||
fn test_concurrent_operations(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Open the test database
|
||||
let db = sled::open(base_path.join("concurrent_tx"))?;
|
||||
println!("Opened concurrent operations test database at: {:?}", base_path.join("concurrent_tx"));
|
||||
|
||||
// Create initial user
|
||||
let user = User::new(
|
||||
1,
|
||||
"Concurrent Test".to_string(),
|
||||
"concurrent@example.com".to_string(),
|
||||
100.0,
|
||||
);
|
||||
|
||||
// Insert initial user
|
||||
db.insert(user.id.to_string().as_bytes(), bincode::serialize(&user)?)?;
|
||||
db.flush()?;
|
||||
println!("Inserted initial user with balance: {}", user.balance);
|
||||
|
||||
// Simulate two concurrent transactions
|
||||
// Transaction 1: Add 50 to balance
|
||||
println!("Starting simulated concurrent transaction 1: Add 50 to balance");
|
||||
|
||||
// Read current state for TX1
|
||||
let mut tx1_user = user.clone();
|
||||
if let Some(data) = db.get(user.id.to_string().as_bytes())? {
|
||||
tx1_user = bincode::deserialize(&data)?;
|
||||
}
|
||||
|
||||
// Transaction 2: Subtract 30 from balance
|
||||
println!("Starting simulated concurrent transaction 2: Subtract 30 from balance");
|
||||
|
||||
// Read current state for TX2 (same starting point)
|
||||
let mut tx2_user = user.clone();
|
||||
if let Some(data) = db.get(user.id.to_string().as_bytes())? {
|
||||
tx2_user = bincode::deserialize(&data)?;
|
||||
}
|
||||
|
||||
// Modify in TX1
|
||||
tx1_user.balance += 50.0;
|
||||
|
||||
// Modify in TX2
|
||||
tx2_user.balance -= 30.0;
|
||||
|
||||
// Commit TX1 first
|
||||
println!("Committing TX1");
|
||||
db.insert(user.id.to_string().as_bytes(), bincode::serialize(&tx1_user)?)?;
|
||||
db.flush()?;
|
||||
|
||||
// Now commit TX2 (would overwrite TX1 in naive implementation)
|
||||
println!("Committing TX2");
|
||||
db.insert(user.id.to_string().as_bytes(), bincode::serialize(&tx2_user)?)?;
|
||||
db.flush()?;
|
||||
|
||||
// Verify the final state (last write wins, so should be TX2's value)
|
||||
if let Some(data) = db.get(user.id.to_string().as_bytes())? {
|
||||
let final_user: User = bincode::deserialize(&data)?;
|
||||
assert_eq!(final_user.balance, 70.0, "Final balance should be 70.0 (TX2 overwrote TX1)");
|
||||
println!("Final user balance is {} after both transactions", final_user.balance);
|
||||
|
||||
// In a real implementation with better concurrency control, you'd expect:
|
||||
// println!("In a proper ACID system, this would have been 120.0 (100.0 - 30.0 + 50.0)");
|
||||
} else {
|
||||
return Err("Failed to find user after concurrent transactions".into());
|
||||
}
|
||||
|
||||
// Clean up
|
||||
drop(db);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
// Examples for using the Zaz database
|
||||
|
||||
use crate::zaz::models::*;
|
||||
use crate::zaz::factory::create_zaz_db;
|
||||
use std::path::PathBuf;
|
||||
use chrono::Utc;
|
||||
|
||||
/// Run a simple example of the DB operations
|
||||
pub fn run_db_examples() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Running Zaz DB examples...");
|
||||
|
||||
// Create a temp DB path
|
||||
let db_path = PathBuf::from("/tmp/zaz-examples");
|
||||
std::fs::create_dir_all(&db_path)?;
|
||||
|
||||
// Create DB instance
|
||||
let db = create_zaz_db(&db_path)?;
|
||||
|
||||
// Example 1: User operations
|
||||
println!("\n--- User Examples ---");
|
||||
let user = User::new(
|
||||
1,
|
||||
"John Doe".to_string(),
|
||||
"john@example.com".to_string(),
|
||||
"secure123".to_string(),
|
||||
"Example Corp".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
db.set(&user)?;
|
||||
println!("Inserted user: {}", user.name);
|
||||
|
||||
let retrieved_user = db.get::<User>(&user.id.to_string())?;
|
||||
println!("Retrieved user: {} ({})", retrieved_user.name, retrieved_user.email);
|
||||
|
||||
// Example 2: Company operations
|
||||
println!("\n--- Company Examples ---");
|
||||
let company = Company::new(
|
||||
1,
|
||||
"Example Corp".to_string(),
|
||||
"EX123456".to_string(),
|
||||
Utc::now(),
|
||||
"12-31".to_string(),
|
||||
"info@example.com".to_string(),
|
||||
"123-456-7890".to_string(),
|
||||
"www.example.com".to_string(),
|
||||
"123 Example St, Example City".to_string(),
|
||||
BusinessType::new(BusinessType::GLOBAL.to_string())
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Warning: {}", e);
|
||||
BusinessType::new_unchecked(BusinessType::GLOBAL.to_string())
|
||||
}),
|
||||
"Technology".to_string(),
|
||||
"An example company".to_string(),
|
||||
CompanyStatus::Active,
|
||||
);
|
||||
|
||||
db.set(&company)?;
|
||||
println!("Inserted company: {}", company.name);
|
||||
|
||||
let companies = db.list::<Company>()?;
|
||||
println!("Found {} companies", companies.len());
|
||||
|
||||
// Clean up
|
||||
std::fs::remove_dir_all(db_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,13 +1,7 @@
|
||||
# Business Models
|
||||
|
||||
This directory contains the core business models used throughout the application for representing essential business objects like products, sales, and currency.
|
||||
|
||||
## Overview
|
||||
|
||||
The business models are implemented as Rust structs and enums with serialization/deserialization support via Serde. These models implement the `SledModel` and `Storable` traits for persistence in the application's database layer.
|
||||
|
||||
## Model Relationships
|
||||
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Customer │
|
||||
|
Loading…
Reference in New Issue
Block a user