test: add test for thread safety; remove mut for upsert and persist
This commit is contained in:
@@ -59,7 +59,7 @@ fn main() {
|
|||||||
// Define the path for the SQLite database file.
|
// Define the path for the SQLite database file.
|
||||||
let db_path = "./simple_product.sqlite3";
|
let db_path = "./simple_product.sqlite3";
|
||||||
// Create a new `Catalog` instance with the specified database path.
|
// Create a new `Catalog` instance with the specified database path.
|
||||||
let mut catalog = Catalog::new(db_path);
|
let catalog = Catalog::new(db_path);
|
||||||
// Initialize the catalog, which sets up the database.
|
// Initialize the catalog, which sets up the database.
|
||||||
catalog.init().unwrap();
|
catalog.init().unwrap();
|
||||||
// Create a new `Product` instance representing a laptop.
|
// Create a new `Product` instance representing a laptop.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ impl Catalog {
|
|||||||
/// removed from the in-memory catalog.
|
/// removed from the in-memory catalog.
|
||||||
/// - All other entities that were successfully persisted (new or updated) have
|
/// - All other entities that were successfully persisted (new or updated) have
|
||||||
/// their state changed to `EntityState::Loaded`.
|
/// their state changed to `EntityState::Loaded`.
|
||||||
pub fn persist(&mut self) -> result::Result<(), FailedTo> {
|
pub fn persist(&self) -> result::Result<(), FailedTo> {
|
||||||
let path = path::Path::new(&self.path);
|
let path = path::Path::new(&self.path);
|
||||||
self.on_items(|items| {
|
self.on_items(|items| {
|
||||||
sqlite::persist::catalog(path, items).map_err(|_| FailedTo::PersistCatalog)?;
|
sqlite::persist::catalog(path, items).map_err(|_| FailedTo::PersistCatalog)?;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ impl Catalog {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `object` - The object to insert or update, which must implement the `EAV` trait.
|
/// * `object` - The object to insert or update, which must implement the `EAV` trait.
|
||||||
pub fn upsert(&mut self, object: impl EAV) -> Result<(), FailedTo> {
|
pub fn upsert(&self, object: impl EAV) -> Result<(), FailedTo> {
|
||||||
let mut entity = object.try_into().map_err(|_| FailedTo::ConvertObject)?;
|
let mut entity = object.try_into().map_err(|_| FailedTo::ConvertObject)?;
|
||||||
self.on_items(|items| {
|
self.on_items(|items| {
|
||||||
if items.contains_key(&entity.id) {
|
if items.contains_key(&entity.id) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn get_should_retrieve_and_convert_entity_by_id() {
|
fn get_should_retrieve_and_convert_entity_by_id() {
|
||||||
// Should retrieve an entity by its ID and correctly convert it to the target type 'T'.
|
// Should retrieve an entity by its ID and correctly convert it to the target type 'T'.
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item = Item {
|
let item = Item {
|
||||||
id: "item-123".to_string(),
|
id: "item-123".to_string(),
|
||||||
subclass: Some("subitem".to_string()),
|
subclass: Some("subitem".to_string()),
|
||||||
@@ -21,7 +21,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn get_should_return_none_for_nonexistent_id() {
|
fn get_should_return_none_for_nonexistent_id() {
|
||||||
// Should return 'None' if the ID does not exist.
|
// Should return 'None' if the ID does not exist.
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item = Item {
|
let item = Item {
|
||||||
id: "item-123".to_string(),
|
id: "item-123".to_string(),
|
||||||
name: "Test Item".to_string(),
|
name: "Test Item".to_string(),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. 'init' -> 'insert' -> 'persist'
|
// 1. 'init' -> 'insert' -> 'persist'
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item_to_insert = Item {
|
let item_to_insert = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -96,7 +96,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. 'insert' -> 'persist'
|
// 1. 'insert' -> 'persist'
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item_to_delete = Item {
|
let item_to_delete = Item {
|
||||||
id: "item-to-delete".to_string(),
|
id: "item-to-delete".to_string(),
|
||||||
@@ -136,7 +136,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Initial setup
|
// 1. Initial setup
|
||||||
let mut catalog_setup = Catalog::new(db_path);
|
let catalog_setup = Catalog::new(db_path);
|
||||||
catalog_setup.init().unwrap();
|
catalog_setup.init().unwrap();
|
||||||
let initial_item = Item {
|
let initial_item = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn list_by_class_should_return_all_entities_of_class() {
|
fn list_by_class_should_return_all_entities_of_class() {
|
||||||
// Should return an iterator with all entities of a specific class.
|
// Should return an iterator with all entities of a specific class.
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item1 = Item {
|
let item1 = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
subclass: Some("subitem".to_string()),
|
subclass: Some("subitem".to_string()),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod tests {
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn list_by_class_and_subclass_should_return_matching_entities() {
|
fn list_by_class_and_subclass_should_return_matching_entities() {
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item1 = Item {
|
let item1 = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
subclass: Some("electronics".to_string()),
|
subclass: Some("electronics".to_string()),
|
||||||
@@ -35,7 +35,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn list_by_class_and_subclass_should_return_empty_if_no_match() {
|
fn list_by_class_and_subclass_should_return_empty_if_no_match() {
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item1 = Item {
|
let item1 = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
subclass: Some("electronics".to_string()),
|
subclass: Some("electronics".to_string()),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Setup DB with a few items of the same class
|
// 1. Setup DB with a few items of the same class
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item1 = Item {
|
let item1 = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -70,7 +70,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Persist an item to the database.
|
// 1. Persist an item to the database.
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item_in_db = Item {
|
let item_in_db = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ mod tests {
|
|||||||
if path.exists() {
|
if path.exists() {
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
let mut catalog_setup = Catalog::new(db_path);
|
let catalog_setup = Catalog::new(db_path);
|
||||||
catalog_setup.init().unwrap();
|
catalog_setup.init().unwrap();
|
||||||
let item_in_db = Item {
|
let item_in_db = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -1421,7 +1421,7 @@ mod tests {
|
|||||||
if path.exists() {
|
if path.exists() {
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
let mut catalog_setup = Catalog::new(db_path);
|
let catalog_setup = Catalog::new(db_path);
|
||||||
catalog_setup.init().unwrap();
|
catalog_setup.init().unwrap();
|
||||||
// Define a second struct with a different class
|
// Define a second struct with a different class
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Create a catalog, insert an item, and persist it to the DB.
|
// 1. Create a catalog, insert an item, and persist it to the DB.
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item_to_persist = Item {
|
let item_to_persist = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -66,7 +66,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Persist an item to the database.
|
// 1. Persist an item to the database.
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item_in_db = Item {
|
let item_in_db = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Create catalog, insert an item, and persist
|
// 1. Create catalog, insert an item, and persist
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let item1 = Item {
|
let item1 = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -77,7 +77,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Insert an entity and persist it.
|
// 1. Insert an entity and persist it.
|
||||||
let mut catalog1 = Catalog::new(db_path);
|
let catalog1 = Catalog::new(db_path);
|
||||||
catalog1.init().unwrap();
|
catalog1.init().unwrap();
|
||||||
let original_item = Item {
|
let original_item = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
@@ -132,7 +132,7 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
// 1. Setup: Pre-populate the database with some items.
|
// 1. Setup: Pre-populate the database with some items.
|
||||||
let mut catalog_setup = Catalog::new(db_path);
|
let catalog_setup = Catalog::new(db_path);
|
||||||
catalog_setup.init().unwrap();
|
catalog_setup.init().unwrap();
|
||||||
let item_to_update_original = Item {
|
let item_to_update_original = Item {
|
||||||
id: "update-me".to_string(),
|
id: "update-me".to_string(),
|
||||||
@@ -221,7 +221,7 @@ mod tests {
|
|||||||
// Using a directory as a path should cause a failure.
|
// Using a directory as a path should cause a failure.
|
||||||
let invalid_path = "target/test_dbs/a_directory_for_persist_fail";
|
let invalid_path = "target/test_dbs/a_directory_for_persist_fail";
|
||||||
std::fs::create_dir_all(invalid_path).unwrap();
|
std::fs::create_dir_all(invalid_path).unwrap();
|
||||||
let mut catalog = Catalog::new(invalid_path);
|
let catalog = Catalog::new(invalid_path);
|
||||||
let item = Item {
|
let item = Item {
|
||||||
id: "item-1".to_string(),
|
id: "item-1".to_string(),
|
||||||
name: "Test".to_string(),
|
name: "Test".to_string(),
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ mod tests {
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use std::sync::*;
|
use std::sync::*;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn thread_safety_test() {
|
fn thread_safety_test() {
|
||||||
let db_path = "target/test_dbs/thread_safety_test.db";
|
let db_path = "target/test_dbs/thread_safety_test.db";
|
||||||
@@ -12,10 +11,8 @@ mod tests {
|
|||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||||
|
|
||||||
let catalog = Arc::new(Catalog::new(db_path));
|
let catalog = Arc::new(Catalog::new(db_path));
|
||||||
catalog.init().unwrap();
|
catalog.init().unwrap();
|
||||||
|
|
||||||
let mut handles = vec![];
|
let mut handles = vec![];
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let catalog = Arc::clone(&catalog);
|
let catalog = Arc::clone(&catalog);
|
||||||
@@ -36,22 +33,16 @@ mod tests {
|
|||||||
});
|
});
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
for handle in handles {
|
for handle in handles {
|
||||||
handle.join().unwrap();
|
handle.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let total_items = catalog.with_items(|items| Ok(items.len())).unwrap();
|
let total_items = catalog.with_items(|items| Ok(items.len())).unwrap();
|
||||||
assert_eq!(total_items, 1000);
|
assert_eq!(total_items, 1000);
|
||||||
|
|
||||||
catalog.persist().unwrap();
|
catalog.persist().unwrap();
|
||||||
|
|
||||||
let mut new_catalog = Catalog::new(db_path);
|
let mut new_catalog = Catalog::new(db_path);
|
||||||
new_catalog.load_by_class::<Item>().unwrap();
|
new_catalog.load_by_class::<Item>().unwrap();
|
||||||
|
|
||||||
let total_items_after_load = new_catalog.with_items(|items| Ok(items.len())).unwrap();
|
let total_items_after_load = new_catalog.with_items(|items| Ok(items.len())).unwrap();
|
||||||
assert_eq!(total_items_after_load, 1000);
|
assert_eq!(total_items_after_load, 1000);
|
||||||
|
|
||||||
std::fs::remove_file(path).unwrap();
|
std::fs::remove_file(path).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn upsert_should_add_single_entity_as_new() {
|
fn upsert_should_add_single_entity_as_new() {
|
||||||
// 'upsert()': Should add a single entity to the 'items' map with 'EntityState::New'.
|
// 'upsert()': Should add a single entity to the 'items' map with 'EntityState::New'.
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item = Item {
|
let item = Item {
|
||||||
id: "item-123".to_string(),
|
id: "item-123".to_string(),
|
||||||
name: "Test Item".to_string(),
|
name: "Test Item".to_string(),
|
||||||
@@ -29,7 +29,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn upsert_should_overwrite_existing_entity() {
|
fn upsert_should_overwrite_existing_entity() {
|
||||||
// 'upsert()': Should overwrite an existing entity with the same ID.
|
// 'upsert()': Should overwrite an existing entity with the same ID.
|
||||||
let mut catalog = Catalog::new("dummy.db");
|
let catalog = Catalog::new("dummy.db");
|
||||||
let item1 = Item {
|
let item1 = Item {
|
||||||
id: "item-123".to_string(),
|
id: "item-123".to_string(),
|
||||||
name: "First Item".to_string(),
|
name: "First Item".to_string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user