...
This commit is contained in:
parent
46785c3410
commit
1364db3f94
5
herodb/readme.md
Normal file
5
herodb/readme.md
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
```bash
|
||||
cargo test zaz::tests -- --test-threads=1
|
||||
```
|
@ -424,345 +424,3 @@ impl DB {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test module with mocked models
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use chrono::Utc;
|
||||
use tempfile::tempdir;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Test model
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct TestUser {
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
impl TestUser {
|
||||
fn new(id: u32, name: String, email: String) -> Self {
|
||||
Self { id, name, email }
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for TestUser {}
|
||||
|
||||
impl SledModel for TestUser {
|
||||
fn get_id(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
|
||||
fn db_prefix() -> &'static str {
|
||||
"test_user"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_db_builder() {
|
||||
// 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::<TestUser>()
|
||||
.build()
|
||||
.expect("Failed to build DB");
|
||||
|
||||
// Create a test user
|
||||
let user = TestUser::new(1, "Test User".to_string(), "test@example.com".to_string());
|
||||
|
||||
// Set the user
|
||||
db.set(&user).expect("Failed to set user");
|
||||
|
||||
// Get the user
|
||||
let retrieved: TestUser = db.get(&user.id.to_string()).expect("Failed to get user");
|
||||
|
||||
// Check that it matches
|
||||
assert_eq!(user, retrieved);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dynamic_registration() {
|
||||
// Create a temporary directory for the test
|
||||
let dir = tempdir().expect("Failed to create temp dir");
|
||||
|
||||
// Create an empty DB
|
||||
let mut db = DB::new(dir.path()).expect("Failed to create DB");
|
||||
|
||||
// Register the TestUser model
|
||||
db.register::<TestUser>().expect("Failed to register TestUser");
|
||||
|
||||
// Create a test user
|
||||
let user = TestUser::new(2, "Dynamic User".to_string(), "dynamic@example.com".to_string());
|
||||
|
||||
// Set the user
|
||||
db.set(&user).expect("Failed to set user");
|
||||
|
||||
// Get the user
|
||||
let retrieved: TestUser = db.get(&user.id.to_string()).expect("Failed to get user");
|
||||
|
||||
// Check that it matches
|
||||
assert_eq!(user, retrieved);
|
||||
}
|
||||
}
|
||||
|
||||
// The as_type function is no longer needed with our type-map based implementation
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use chrono::Utc;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_read_your_writes() {
|
||||
// Create a temporary directory for the test
|
||||
let dir = tempdir().expect("Failed to create temp dir");
|
||||
let db = DB::new(dir.path()).expect("Failed to create DB");
|
||||
|
||||
// Create a user
|
||||
let user = User::new(
|
||||
10,
|
||||
"Original User".to_string(),
|
||||
"original@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Original Corp".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
// Insert the user directly (no transaction)
|
||||
db.set(&user).expect("Failed to insert user");
|
||||
|
||||
// Begin a transaction
|
||||
db.begin_transaction().expect("Failed to begin transaction");
|
||||
|
||||
// Verify we can read the original user
|
||||
let original = db.get::<User>(&user.id.to_string()).expect("Failed to get original user");
|
||||
assert_eq!(original.name, "Original User");
|
||||
|
||||
// Create a modified user with the same ID
|
||||
let modified_user = User::new(
|
||||
10,
|
||||
"Modified User".to_string(),
|
||||
"modified@example.com".to_string(),
|
||||
"new_password".to_string(),
|
||||
"Modified Corp".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
|
||||
// Update the user in the transaction
|
||||
db.set(&modified_user).expect("Failed to update user in transaction");
|
||||
|
||||
// Verify we can read our own writes within the transaction
|
||||
let in_transaction = db.get::<User>(&user.id.to_string()).expect("Failed to get user from transaction");
|
||||
assert_eq!(in_transaction.name, "Modified User");
|
||||
assert_eq!(in_transaction.email, "modified@example.com");
|
||||
|
||||
// Create a new user that only exists in the transaction
|
||||
let new_user = User::new(
|
||||
20,
|
||||
"Transaction Only User".to_string(),
|
||||
"tx@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"TX Corp".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
|
||||
// Add the new user in the transaction
|
||||
db.set(&new_user).expect("Failed to add new user in transaction");
|
||||
|
||||
// Verify we can read the new user within the transaction
|
||||
let new_in_tx = db.get::<User>(&new_user.id.to_string()).expect("Failed to get new user from transaction");
|
||||
assert_eq!(new_in_tx.name, "Transaction Only User");
|
||||
|
||||
// Delete a user in the transaction and verify it appears deleted within the transaction
|
||||
db.delete::<User>(&user.id.to_string()).expect("Failed to delete user in transaction");
|
||||
match db.get::<User>(&user.id.to_string()) {
|
||||
Err(SledDBError::NotFound(_)) => (), // Expected result
|
||||
Ok(_) => panic!("User should appear deleted within transaction"),
|
||||
Err(e) => panic!("Unexpected error: {}", e),
|
||||
}
|
||||
|
||||
// Rollback the transaction
|
||||
db.rollback_transaction().expect("Failed to rollback transaction");
|
||||
|
||||
// Verify the original user is still available and unchanged after rollback
|
||||
let after_rollback = db.get::<User>(&user.id.to_string()).expect("Failed to get user after rollback");
|
||||
assert_eq!(after_rollback.name, "Original User");
|
||||
|
||||
// Verify the transaction-only user doesn't exist after rollback
|
||||
assert!(db.get::<User>(&new_user.id.to_string()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transactions() {
|
||||
// Create a temporary directory for the test
|
||||
let dir = tempdir().expect("Failed to create temp dir");
|
||||
let db = DB::new(dir.path()).expect("Failed to create DB");
|
||||
|
||||
// Create a sample user and company for testing
|
||||
let user = User::new(
|
||||
1,
|
||||
"Transaction Test User".to_string(),
|
||||
"tx@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Test Corp".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
|
||||
let incorporation_date = Utc::now();
|
||||
let company = Company::new(
|
||||
1,
|
||||
"Transaction Test Corp".to_string(),
|
||||
"TX123".to_string(),
|
||||
incorporation_date,
|
||||
"12-31".to_string(),
|
||||
"tx@corp.com".to_string(),
|
||||
"123-456-7890".to_string(),
|
||||
"www.testcorp.com".to_string(),
|
||||
"123 Test St".to_string(),
|
||||
BusinessType::Global,
|
||||
"Tech".to_string(),
|
||||
"A test company for transactions".to_string(),
|
||||
CompanyStatus::Active,
|
||||
);
|
||||
|
||||
// Test successful transaction (multiple operations committed at once)
|
||||
{
|
||||
// Start a transaction
|
||||
db.begin_transaction().expect("Failed to begin transaction");
|
||||
assert!(db.has_active_transaction());
|
||||
|
||||
// Perform multiple operations within the transaction
|
||||
db.set(&user).expect("Failed to add user to transaction");
|
||||
db.set(&company).expect("Failed to add company to transaction");
|
||||
|
||||
// Commit the transaction
|
||||
db.commit_transaction().expect("Failed to commit transaction");
|
||||
assert!(!db.has_active_transaction());
|
||||
|
||||
// Verify both operations were applied
|
||||
let retrieved_user: User = db.get(&user.id.to_string()).expect("Failed to get user after commit");
|
||||
let retrieved_company: Company = db.get(&company.id.to_string()).expect("Failed to get company after commit");
|
||||
|
||||
assert_eq!(user.name, retrieved_user.name);
|
||||
assert_eq!(company.name, retrieved_company.name);
|
||||
}
|
||||
|
||||
// Test transaction rollback
|
||||
{
|
||||
// Create another user that should not be persisted
|
||||
let temp_user = User::new(
|
||||
2,
|
||||
"Temporary User".to_string(),
|
||||
"temp@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Temp Corp".to_string(),
|
||||
"Temp".to_string(),
|
||||
);
|
||||
|
||||
// Start a transaction
|
||||
db.begin_transaction().expect("Failed to begin transaction");
|
||||
|
||||
// Add the temporary user
|
||||
db.set(&temp_user).expect("Failed to add temporary user to transaction");
|
||||
|
||||
// Perform a delete operation in the transaction
|
||||
db.delete::<Company>(&company.id.to_string()).expect("Failed to delete company in transaction");
|
||||
|
||||
// Rollback the transaction - should discard all operations
|
||||
db.rollback_transaction().expect("Failed to rollback transaction");
|
||||
assert!(!db.has_active_transaction());
|
||||
|
||||
// Verify the temporary user was not added
|
||||
match db.get::<User>(&temp_user.id.to_string()) {
|
||||
Err(SledDBError::NotFound(_)) => (), // Expected outcome
|
||||
Ok(_) => panic!("Temporary user should not exist after rollback"),
|
||||
Err(e) => panic!("Unexpected error: {}", e),
|
||||
}
|
||||
|
||||
// Verify the company was not deleted
|
||||
let company_still_exists = db.get::<Company>(&company.id.to_string()).is_ok();
|
||||
assert!(company_still_exists, "Company should still exist after transaction rollback");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_db_operations() {
|
||||
// Create a temporary directory for the test
|
||||
let dir = tempdir().expect("Failed to create temp dir");
|
||||
let db = DB::new(dir.path()).expect("Failed to create DB");
|
||||
|
||||
// Test simple transaction functionality
|
||||
assert!(!db.has_active_transaction());
|
||||
db.begin_transaction().expect("Failed to begin transaction");
|
||||
assert!(db.has_active_transaction());
|
||||
db.rollback_transaction().expect("Failed to rollback transaction");
|
||||
assert!(!db.has_active_transaction());
|
||||
|
||||
// Create a sample user
|
||||
let user = User::new(
|
||||
1,
|
||||
"Test User".to_string(),
|
||||
"test@example.com".to_string(),
|
||||
"password".to_string(),
|
||||
"Test Corp".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
|
||||
// Insert the user
|
||||
db.set(&user).expect("Failed to insert user");
|
||||
|
||||
// Get the user
|
||||
let retrieved_user: User = db.get(&user.id.to_string()).expect("Failed to get user");
|
||||
assert_eq!(user.name, retrieved_user.name);
|
||||
|
||||
// Create a sample company
|
||||
let incorporation_date = Utc::now();
|
||||
let company = Company::new(
|
||||
1,
|
||||
"Test Corp".to_string(),
|
||||
"REG123".to_string(),
|
||||
incorporation_date,
|
||||
"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::Global,
|
||||
"Tech".to_string(),
|
||||
"A test company".to_string(),
|
||||
CompanyStatus::Active,
|
||||
);
|
||||
|
||||
// Insert the company
|
||||
db.set(&company).expect("Failed to insert company");
|
||||
|
||||
// Get the company
|
||||
let retrieved_company: Company = db.get(&company.id.to_string())
|
||||
.expect("Failed to get company");
|
||||
assert_eq!(company.name, retrieved_company.name);
|
||||
|
||||
// List all companies
|
||||
let companies: Vec<Company> = db.list().expect("Failed to list companies");
|
||||
assert_eq!(companies.len(), 1);
|
||||
assert_eq!(companies[0].name, company.name);
|
||||
|
||||
// List all users
|
||||
let users: Vec<User> = db.list().expect("Failed to list users");
|
||||
assert_eq!(users.len(), 1);
|
||||
assert_eq!(users[0].name, user.name);
|
||||
|
||||
// Delete the company
|
||||
db.delete::<Company>(&company.id.to_string())
|
||||
.expect("Failed to delete company");
|
||||
|
||||
// Try to get the deleted company (should fail)
|
||||
match db.get::<Company>(&company.id.to_string()) {
|
||||
Err(SledDBError::NotFound(_)) => (),
|
||||
_ => panic!("Expected NotFound error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,3 +14,6 @@ pub mod examples;
|
||||
// Expose the cmd module
|
||||
pub mod cmd;
|
||||
|
||||
// Include tests module when running tests
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
@ -1,7 +1,8 @@
|
||||
//! Integration tests for the zaz database module
|
||||
|
||||
use sled;
|
||||
use bincode;
|
||||
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;
|
||||
@ -19,113 +20,6 @@ fn test_basic_database_operations() {
|
||||
}
|
||||
|
||||
fn run_comprehensive_test() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// User model
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct User {
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: String,
|
||||
company: String,
|
||||
role: String,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn new(
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: String,
|
||||
company: String,
|
||||
role: String,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
company,
|
||||
role,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Company model
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
enum BusinessType {
|
||||
Local,
|
||||
National,
|
||||
Global,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
enum CompanyStatus {
|
||||
Active,
|
||||
Inactive,
|
||||
Pending,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct Company {
|
||||
id: u32,
|
||||
name: String,
|
||||
registration_number: String,
|
||||
registration_date: DateTime<Utc>,
|
||||
fiscal_year_end: String,
|
||||
email: String,
|
||||
phone: String,
|
||||
website: String,
|
||||
address: String,
|
||||
business_type: BusinessType,
|
||||
industry: String,
|
||||
description: String,
|
||||
status: CompanyStatus,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Company {
|
||||
fn new(
|
||||
id: u32,
|
||||
name: String,
|
||||
registration_number: String,
|
||||
registration_date: DateTime<Utc>,
|
||||
fiscal_year_end: String,
|
||||
email: String,
|
||||
phone: String,
|
||||
website: String,
|
||||
address: String,
|
||||
business_type: BusinessType,
|
||||
industry: String,
|
||||
description: String,
|
||||
status: CompanyStatus,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
registration_number,
|
||||
registration_date,
|
||||
fiscal_year_end,
|
||||
email,
|
||||
phone,
|
||||
website,
|
||||
address,
|
||||
business_type,
|
||||
industry,
|
||||
description,
|
||||
status,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary directory for testing
|
||||
let temp_dir = tempdir()?;
|
||||
println!("Using temporary directory: {:?}", temp_dir.path());
|
||||
@ -147,44 +41,8 @@ fn run_comprehensive_test() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
fn test_user_operations(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// User model (duplicate for scope)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct User {
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: String,
|
||||
company: String,
|
||||
role: String,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn new(
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: String,
|
||||
company: String,
|
||||
role: String,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
company,
|
||||
role,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the user database
|
||||
let db = sled::open(base_path.join("users"))?;
|
||||
let db = SledDB::<User>::open(base_path.join("users"))?;
|
||||
println!("Opened user database at: {:?}", base_path.join("users"));
|
||||
|
||||
// Create a test user
|
||||
@ -198,21 +56,14 @@ fn test_user_operations(base_path: &Path) -> Result<(), Box<dyn std::error::Erro
|
||||
);
|
||||
|
||||
// Insert the user
|
||||
let user_id = user.id.to_string();
|
||||
let user_bytes = bincode::serialize(&user)?;
|
||||
db.insert(user_id.as_bytes(), user_bytes)?;
|
||||
db.flush()?;
|
||||
db.insert(&user)?;
|
||||
println!("Inserted user: {}", user.name);
|
||||
|
||||
// Retrieve the user
|
||||
if let Some(data) = db.get(user_id.as_bytes())? {
|
||||
let retrieved_user: User = bincode::deserialize(&data)?;
|
||||
println!("Retrieved user: {}", retrieved_user.name);
|
||||
assert_eq!(user.name, retrieved_user.name);
|
||||
assert_eq!(user.email, retrieved_user.email);
|
||||
} else {
|
||||
return Err("Failed to retrieve user".into());
|
||||
}
|
||||
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(
|
||||
@ -224,108 +75,30 @@ fn test_user_operations(base_path: &Path) -> Result<(), Box<dyn std::error::Erro
|
||||
"SuperAdmin".to_string(),
|
||||
);
|
||||
|
||||
let updated_bytes = bincode::serialize(&updated_user)?;
|
||||
db.insert(user_id.as_bytes(), updated_bytes)?;
|
||||
db.flush()?;
|
||||
db.insert(&updated_user)?;
|
||||
println!("Updated user: {}", updated_user.name);
|
||||
|
||||
// Retrieve the updated user
|
||||
if let Some(data) = db.get(user_id.as_bytes())? {
|
||||
let retrieved_user: User = bincode::deserialize(&data)?;
|
||||
println!("Retrieved updated user: {}", retrieved_user.name);
|
||||
assert_eq!(updated_user.name, retrieved_user.name);
|
||||
assert_eq!(updated_user.email, retrieved_user.email);
|
||||
} else {
|
||||
return Err("Failed to retrieve updated user".into());
|
||||
}
|
||||
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.remove(user_id.as_bytes())?;
|
||||
db.flush()?;
|
||||
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.as_bytes())?;
|
||||
assert!(result.is_none(), "User should be deleted");
|
||||
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>> {
|
||||
// Company model (duplicate for scope)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
enum BusinessType {
|
||||
Local,
|
||||
National,
|
||||
Global,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
enum CompanyStatus {
|
||||
Active,
|
||||
Inactive,
|
||||
Pending,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct Company {
|
||||
id: u32,
|
||||
name: String,
|
||||
registration_number: String,
|
||||
registration_date: DateTime<Utc>,
|
||||
fiscal_year_end: String,
|
||||
email: String,
|
||||
phone: String,
|
||||
website: String,
|
||||
address: String,
|
||||
business_type: BusinessType,
|
||||
industry: String,
|
||||
description: String,
|
||||
status: CompanyStatus,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Company {
|
||||
fn new(
|
||||
id: u32,
|
||||
name: String,
|
||||
registration_number: String,
|
||||
registration_date: DateTime<Utc>,
|
||||
fiscal_year_end: String,
|
||||
email: String,
|
||||
phone: String,
|
||||
website: String,
|
||||
address: String,
|
||||
business_type: BusinessType,
|
||||
industry: String,
|
||||
description: String,
|
||||
status: CompanyStatus,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
registration_number,
|
||||
registration_date,
|
||||
fiscal_year_end,
|
||||
email,
|
||||
phone,
|
||||
website,
|
||||
address,
|
||||
business_type,
|
||||
industry,
|
||||
description,
|
||||
status,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the company database
|
||||
let db = sled::open(base_path.join("companies"))?;
|
||||
let db = SledDB::<Company>::open(base_path.join("companies"))?;
|
||||
println!("Opened company database at: {:?}", base_path.join("companies"));
|
||||
|
||||
// Create a test company
|
||||
@ -346,43 +119,25 @@ fn test_company_operations(base_path: &Path) -> Result<(), Box<dyn std::error::E
|
||||
);
|
||||
|
||||
// Insert the company
|
||||
let company_id = company.id.to_string();
|
||||
let company_bytes = bincode::serialize(&company)?;
|
||||
db.insert(company_id.as_bytes(), company_bytes)?;
|
||||
db.flush()?;
|
||||
db.insert(&company)?;
|
||||
println!("Inserted company: {}", company.name);
|
||||
|
||||
// Retrieve the company
|
||||
if let Some(data) = db.get(company_id.as_bytes())? {
|
||||
let retrieved_company: Company = bincode::deserialize(&data)?;
|
||||
println!("Retrieved company: {}", retrieved_company.name);
|
||||
assert_eq!(company.name, retrieved_company.name);
|
||||
} else {
|
||||
return Err("Failed to retrieve company".into());
|
||||
}
|
||||
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 mut companies = Vec::new();
|
||||
for item in db.iter() {
|
||||
let (_key, value) = item?;
|
||||
let company: Company = bincode::deserialize(&value)?;
|
||||
companies.push(company);
|
||||
}
|
||||
let companies = db.list()?;
|
||||
println!("Found {} companies", companies.len());
|
||||
assert_eq!(companies.len(), 1);
|
||||
|
||||
// Delete the company
|
||||
db.remove(company_id.as_bytes())?;
|
||||
db.flush()?;
|
||||
db.delete(&company.id.to_string())?;
|
||||
println!("Deleted company: {}", company.name);
|
||||
|
||||
// List companies again (should be empty)
|
||||
let mut companies = Vec::new();
|
||||
for item in db.iter() {
|
||||
let (_key, value) = item?;
|
||||
let company: Company = bincode::deserialize(&value)?;
|
||||
companies.push(company);
|
||||
}
|
||||
let companies = db.list()?;
|
||||
assert_eq!(companies.len(), 0);
|
||||
println!("Verified company was deleted");
|
||||
|
||||
@ -390,45 +145,11 @@ fn test_company_operations(base_path: &Path) -> Result<(), Box<dyn std::error::E
|
||||
}
|
||||
|
||||
fn test_transaction_simulation(base_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
// User model (duplicate for scope)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct User {
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: String,
|
||||
company: String,
|
||||
role: String,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
// Create a DB instance with User model registered
|
||||
let mut db = DB::new(base_path.join("transaction"))?;
|
||||
db.register::<User>()?;
|
||||
|
||||
impl User {
|
||||
fn new(
|
||||
id: u32,
|
||||
name: String,
|
||||
email: String,
|
||||
password: String,
|
||||
company: String,
|
||||
role: String,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
company,
|
||||
role,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the user database
|
||||
let db = sled::open(base_path.join("tx_users"))?;
|
||||
println!("Opened transaction test database at: {:?}", base_path.join("tx_users"));
|
||||
println!("Created DB with User model registered at: {:?}", base_path.join("transaction"));
|
||||
|
||||
// Add a user outside of transaction
|
||||
let user = User::new(
|
||||
@ -440,28 +161,15 @@ fn test_transaction_simulation(base_path: &Path) -> Result<(), Box<dyn std::erro
|
||||
"User".to_string(),
|
||||
);
|
||||
|
||||
let user_id = user.id.to_string();
|
||||
let user_bytes = bincode::serialize(&user)?;
|
||||
db.insert(user_id.as_bytes(), user_bytes)?;
|
||||
db.flush()?;
|
||||
// Add the user without a transaction
|
||||
db.set(&user)?;
|
||||
println!("Added initial user: {}", user.name);
|
||||
|
||||
// Since sled doesn't have explicit transaction support like the DB mock in the original code,
|
||||
// we'll simulate transaction behavior by:
|
||||
// 1. Making changes in memory
|
||||
// 2. Only writing to the database when we "commit"
|
||||
println!("Simulating transaction operations...");
|
||||
// Begin a transaction
|
||||
db.begin_transaction()?;
|
||||
println!("Transaction started");
|
||||
|
||||
// Create in-memory copy of our data (transaction workspace)
|
||||
let mut tx_workspace = std::collections::HashMap::new();
|
||||
|
||||
// Retrieve initial state from db
|
||||
if let Some(data) = db.get(user_id.as_bytes())? {
|
||||
let retrieved_user: User = bincode::deserialize(&data)?;
|
||||
tx_workspace.insert(user_id.clone(), retrieved_user);
|
||||
}
|
||||
|
||||
// Update user in transaction workspace
|
||||
// Update user in transaction
|
||||
let updated_user = User::new(
|
||||
200,
|
||||
"Updated in TX".to_string(),
|
||||
@ -470,10 +178,10 @@ fn test_transaction_simulation(base_path: &Path) -> Result<(), Box<dyn std::erro
|
||||
"New Corp".to_string(),
|
||||
"Admin".to_string(),
|
||||
);
|
||||
tx_workspace.insert(user_id.clone(), updated_user.clone());
|
||||
println!("Updated user in transaction workspace");
|
||||
db.set(&updated_user)?;
|
||||
println!("Updated user in transaction");
|
||||
|
||||
// Add new user in transaction workspace
|
||||
// Add new user in transaction
|
||||
let new_user = User::new(
|
||||
201,
|
||||
"New in TX".to_string(),
|
||||
@ -482,85 +190,59 @@ fn test_transaction_simulation(base_path: &Path) -> Result<(), Box<dyn std::erro
|
||||
"New Corp".to_string(),
|
||||
"User".to_string(),
|
||||
);
|
||||
let new_user_id = new_user.id.to_string();
|
||||
tx_workspace.insert(new_user_id.clone(), new_user.clone());
|
||||
println!("Added new user in transaction workspace");
|
||||
db.set(&new_user)?;
|
||||
println!("Added new user in transaction");
|
||||
|
||||
// Verify the transaction workspace state
|
||||
let tx_user = tx_workspace.get(&user_id).unwrap();
|
||||
// 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 within workspace");
|
||||
println!("Verified transaction changes are visible");
|
||||
|
||||
// Simulate a rollback by discarding our workspace without writing to db
|
||||
println!("Rolled back transaction (discarded workspace without writing to db)");
|
||||
// Commit the transaction
|
||||
db.commit_transaction()?;
|
||||
println!("Transaction committed");
|
||||
|
||||
// Verify original user is unchanged in the database
|
||||
if let Some(data) = db.get(user_id.as_bytes())? {
|
||||
let original: User = bincode::deserialize(&data)?;
|
||||
assert_eq!(original.name, "Transaction Test");
|
||||
println!("Verified original user is unchanged after rollback");
|
||||
} else {
|
||||
return Err("Failed to retrieve user after rollback".into());
|
||||
}
|
||||
// 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");
|
||||
|
||||
// Verify new user was not added to the database
|
||||
let result = db.get(new_user_id.as_bytes())?;
|
||||
assert!(result.is_none());
|
||||
println!("Verified new user was not added after rollback");
|
||||
// Test transaction rollback
|
||||
|
||||
// Test commit transaction
|
||||
println!("Simulating a new transaction...");
|
||||
// Begin another transaction
|
||||
db.begin_transaction()?;
|
||||
println!("New transaction started");
|
||||
|
||||
// Create new transaction workspace
|
||||
let mut tx_workspace = std::collections::HashMap::new();
|
||||
|
||||
// Retrieve current state from db
|
||||
if let Some(data) = db.get(user_id.as_bytes())? {
|
||||
let retrieved_user: User = bincode::deserialize(&data)?;
|
||||
tx_workspace.insert(user_id.clone(), retrieved_user);
|
||||
}
|
||||
|
||||
// Update user in new transaction workspace
|
||||
let committed_user = User::new(
|
||||
// Make changes that will be rolled back
|
||||
let rollback_user = User::new(
|
||||
200,
|
||||
"Committed Update".to_string(),
|
||||
"commit@example.com".to_string(),
|
||||
"commit_pass".to_string(),
|
||||
"Commit Corp".to_string(),
|
||||
"Manager".to_string(),
|
||||
"Will Be Rolled Back".to_string(),
|
||||
"rollback@example.com".to_string(),
|
||||
"temppass".to_string(),
|
||||
"Temp Corp".to_string(),
|
||||
"TempAdmin".to_string(),
|
||||
);
|
||||
tx_workspace.insert(user_id.clone(), committed_user.clone());
|
||||
println!("Updated user in new transaction");
|
||||
db.set(&rollback_user)?;
|
||||
println!("Updated user in transaction that will be rolled back");
|
||||
|
||||
// Commit the transaction by writing the workspace changes to the database
|
||||
println!("Committing transaction by writing changes to database");
|
||||
for (key, user) in tx_workspace {
|
||||
let user_bytes = bincode::serialize(&user)?;
|
||||
db.insert(key.as_bytes(), user_bytes)?;
|
||||
}
|
||||
db.flush()?;
|
||||
// Rollback the transaction
|
||||
db.rollback_transaction()?;
|
||||
println!("Transaction rolled back");
|
||||
|
||||
// Verify changes persisted to the database
|
||||
if let Some(data) = db.get(user_id.as_bytes())? {
|
||||
let final_user: User = bincode::deserialize(&data)?;
|
||||
assert_eq!(final_user.name, "Committed Update");
|
||||
println!("Verified changes persisted after commit");
|
||||
} else {
|
||||
return Err("Failed to retrieve user after commit".into());
|
||||
}
|
||||
// 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 the basic CRUD functionality with a single model
|
||||
#[test]
|
||||
fn test_simple_db() {
|
||||
// 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 test user
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
// A simpler test that uses a basic DB setup
|
||||
|
||||
// Test model
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct User {
|
||||
id: u32,
|
||||
name: String,
|
||||
@ -573,56 +255,39 @@ fn test_simple_db() {
|
||||
}
|
||||
}
|
||||
|
||||
// Open a sled database in the temporary directory
|
||||
let db = sled::open(temp_dir.path().join("simple_users")).expect("Failed to open database");
|
||||
println!("Opened database at: {:?}", temp_dir.path().join("simple_users"));
|
||||
impl Storable for User {}
|
||||
|
||||
// CREATE: Create a user
|
||||
let user = User::new(1, "Simple User".to_string(), "simple@example.com".to_string());
|
||||
let user_key = user.id.to_string();
|
||||
let user_value = bincode::serialize(&user).expect("Failed to serialize user");
|
||||
db.insert(user_key.as_bytes(), user_value).expect("Failed to insert user");
|
||||
db.flush().expect("Failed to flush database");
|
||||
println!("Created user: {} ({})", user.name, user.email);
|
||||
|
||||
// READ: Retrieve the user
|
||||
let result = db.get(user_key.as_bytes()).expect("Failed to query database");
|
||||
assert!(result.is_some(), "User should exist");
|
||||
if let Some(data) = result {
|
||||
let retrieved_user: User = bincode::deserialize(&data).expect("Failed to deserialize user");
|
||||
println!("Retrieved user: {} ({})", retrieved_user.name, retrieved_user.email);
|
||||
assert_eq!(user, retrieved_user, "Retrieved user should match original");
|
||||
impl SledModel for User {
|
||||
fn get_id(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
|
||||
fn db_prefix() -> &'static str {
|
||||
"test_simple_user"
|
||||
}
|
||||
}
|
||||
|
||||
// UPDATE: Update the user
|
||||
let updated_user = User::new(1, "Updated User".to_string(), "updated@example.com".to_string());
|
||||
let updated_value = bincode::serialize(&updated_user).expect("Failed to serialize updated user");
|
||||
db.insert(user_key.as_bytes(), updated_value).expect("Failed to update user");
|
||||
db.flush().expect("Failed to flush database");
|
||||
println!("Updated user: {} ({})", updated_user.name, updated_user.email);
|
||||
// Create a temporary directory for the test
|
||||
let dir = tempdir().expect("Failed to create temp dir");
|
||||
|
||||
// Verify update
|
||||
let result = db.get(user_key.as_bytes()).expect("Failed to query database");
|
||||
assert!(result.is_some(), "Updated user should exist");
|
||||
if let Some(data) = result {
|
||||
let retrieved_user: User = bincode::deserialize(&data).expect("Failed to deserialize user");
|
||||
println!("Retrieved updated user: {} ({})", retrieved_user.name, retrieved_user.email);
|
||||
assert_eq!(updated_user, retrieved_user, "Retrieved user should match updated version");
|
||||
}
|
||||
// Create a DB with the builder
|
||||
let db = DBBuilder::new(dir.path())
|
||||
.register_model::<User>()
|
||||
.build()
|
||||
.expect("Failed to build DB");
|
||||
|
||||
// DELETE: Delete the user
|
||||
db.remove(user_key.as_bytes()).expect("Failed to delete user");
|
||||
db.flush().expect("Failed to flush database");
|
||||
println!("Deleted user");
|
||||
// Create a test user
|
||||
let user = User::new(1, "Simple Test User".to_string(), "simple@example.com".to_string());
|
||||
|
||||
// Verify deletion
|
||||
let result = db.get(user_key.as_bytes()).expect("Failed to query database");
|
||||
assert!(result.is_none(), "User should be deleted");
|
||||
println!("Verified user deletion");
|
||||
// Set the user
|
||||
db.set(&user).expect("Failed to set user");
|
||||
|
||||
// Clean up
|
||||
drop(db);
|
||||
temp_dir.close().expect("Failed to cleanup temporary directory");
|
||||
// Get the user
|
||||
let retrieved: User = db.get(&user.id.to_string()).expect("Failed to get user");
|
||||
|
||||
println!("Simple DB test completed successfully!");
|
||||
// Check that it matches
|
||||
assert_eq!(user.name, retrieved.name);
|
||||
assert_eq!(user.email, retrieved.email);
|
||||
|
||||
println!("Simple DB test passed!");
|
||||
}
|
||||
|
6
herodb/src/zaz/tests/mod.rs
Normal file
6
herodb/src/zaz/tests/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! Tests for the Zaz module
|
||||
|
||||
// Re-export the test modules
|
||||
pub mod model_db_test;
|
||||
pub mod db_integration_test;
|
||||
pub mod transaction_test;
|
@ -2,7 +2,9 @@
|
||||
|
||||
use crate::core::{DB, DBBuilder, SledDBResult, Storable, SledModel};
|
||||
use crate::zaz::factory::create_zaz_db;
|
||||
use crate::zaz::models::*;
|
||||
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;
|
||||
@ -103,14 +105,13 @@ fn test_db_builder() {
|
||||
let product = Product::new(
|
||||
1,
|
||||
"Unregistered Product".to_string(),
|
||||
"PROD-123".to_string(),
|
||||
"A test product".to_string(),
|
||||
Currency::new(100.0, "USD".to_string()),
|
||||
ProductType::Product,
|
||||
"Test".to_string(),
|
||||
ProductType::Standard,
|
||||
ProductStatus::Available,
|
||||
Currency::USD,
|
||||
100.0,
|
||||
vec![],
|
||||
10, // max_amount
|
||||
30, // validity_days
|
||||
);
|
||||
|
||||
// This should fail because Product was not registered
|
||||
|
Loading…
Reference in New Issue
Block a user