feat: add signed integer filter condition with equal comparison
This commit is contained in:
@@ -6,6 +6,7 @@ pub fn run(filter: &Filter) -> Result<Vec<Box<dyn ToSql>>, FailedTo> {
|
||||
for condition in filter.conditions() {
|
||||
match condition.2 {
|
||||
Condition::Bool(value) => params.push(Box::new(value)),
|
||||
Condition::SignedInt(value) => params.push(Box::new(value)),
|
||||
}
|
||||
}
|
||||
Ok(params)
|
||||
@@ -14,7 +15,6 @@ pub fn run(filter: &Filter) -> Result<Vec<Box<dyn ToSql>>, FailedTo> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Ensures that an empty vector is returned when the filter has no conditions.
|
||||
#[test]
|
||||
fn returns_empty_vec_for_empty_filter() {
|
||||
@@ -22,7 +22,6 @@ mod tests {
|
||||
let params = run(&filter).unwrap();
|
||||
assert!(params.is_empty());
|
||||
}
|
||||
|
||||
// Verifies that a single boolean condition is correctly converted to a ToSql parameter.
|
||||
#[test]
|
||||
fn returns_params_for_one_bool_condition() {
|
||||
@@ -34,7 +33,6 @@ mod tests {
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(1))
|
||||
);
|
||||
}
|
||||
|
||||
// Checks that multiple boolean conditions are correctly converted and ordered.
|
||||
#[test]
|
||||
fn returns_params_for_multiple_bool_conditions() {
|
||||
@@ -52,4 +50,51 @@ mod tests {
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(0))
|
||||
);
|
||||
}
|
||||
// Verifies that a single signed integer condition is correctly converted to a ToSql parameter.
|
||||
#[test]
|
||||
fn returns_params_for_one_signed_int_condition() {
|
||||
let filter = Filter::new().with_signed_int("level", Comparison::Equal, -5);
|
||||
let params = run(&filter).unwrap();
|
||||
assert_eq!(params.len(), 1);
|
||||
assert_eq!(
|
||||
params[0].to_sql().unwrap(),
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(-5))
|
||||
);
|
||||
}
|
||||
// Checks that multiple signed integer conditions are correctly converted and ordered.
|
||||
#[test]
|
||||
fn returns_params_for_multiple_signed_int_conditions() {
|
||||
let filter = Filter::new()
|
||||
.with_signed_int("level", Comparison::Equal, -5)
|
||||
.with_signed_int("score", Comparison::Equal, 100);
|
||||
let params = run(&filter).unwrap();
|
||||
assert_eq!(params.len(), 2);
|
||||
assert_eq!(
|
||||
params[0].to_sql().unwrap(),
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(-5))
|
||||
);
|
||||
assert_eq!(
|
||||
params[1].to_sql().unwrap(),
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(100))
|
||||
);
|
||||
}
|
||||
// Tests conversion for a mix of boolean and signed integer conditions.
|
||||
#[test]
|
||||
fn returns_params_for_mixed_conditions() {
|
||||
let filter = Filter::new().with_bool("is_active", true).with_signed_int(
|
||||
"level",
|
||||
Comparison::Equal,
|
||||
-10,
|
||||
);
|
||||
let params = run(&filter).unwrap();
|
||||
assert_eq!(params.len(), 2);
|
||||
assert_eq!(
|
||||
params[0].to_sql().unwrap(),
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(1))
|
||||
);
|
||||
assert_eq!(
|
||||
params[1].to_sql().unwrap(),
|
||||
rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(-10))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,18 @@ const BASE_SELECT: &str = r#"SELECT * FROM entity WHERE 1=1"#;
|
||||
pub fn run(filter: &Filter) -> Result<String, FailedTo> {
|
||||
let mut statement = String::from(BASE_SELECT);
|
||||
for (i, (name, _comparison, condition)) in filter.conditions().enumerate() {
|
||||
let fragment = match *condition {
|
||||
Condition::Bool(_) => format!(
|
||||
let fragment = match (_comparison, condition) {
|
||||
(_, Condition::Bool(_)) => format!(
|
||||
" AND id IN (SELECT entity_id FROM attribute WHERE id = '{}' AND value_bool = ?{})",
|
||||
name,
|
||||
i + 1
|
||||
),
|
||||
(Comparison::Equal, Condition::SignedInt(_)) => format!(
|
||||
" AND id IN (SELECT entity_id FROM attribute WHERE id = '{}' AND value_int = ?{})",
|
||||
name,
|
||||
i + 1
|
||||
),
|
||||
_ => todo!(),
|
||||
};
|
||||
statement.push_str(&fragment);
|
||||
}
|
||||
@@ -21,14 +27,12 @@ pub fn run(filter: &Filter) -> Result<String, FailedTo> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Filter;
|
||||
|
||||
#[test]
|
||||
fn builds_statement_with_no_conditions() {
|
||||
let filter = Filter::new();
|
||||
let statement = run(&filter).unwrap();
|
||||
assert_eq!(statement, BASE_SELECT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_statement_with_one_bool_condition() {
|
||||
let filter = Filter::new().with_bool("is_active", true);
|
||||
@@ -38,7 +42,6 @@ mod tests {
|
||||
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_active' AND value_bool = ?1)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_statement_with_multiple_bool_conditions() {
|
||||
let filter = Filter::new()
|
||||
@@ -50,4 +53,37 @@ mod tests {
|
||||
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_active' AND value_bool = ?1) AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_deleted' AND value_bool = ?2)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn builds_statement_with_one_signed_int_equal_condition() {
|
||||
let filter = Filter::new().with_signed_int("level", Comparison::Equal, 10);
|
||||
let statement = run(&filter).unwrap();
|
||||
assert_eq!(
|
||||
statement,
|
||||
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'level' AND value_int = ?1)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn builds_statement_with_multiple_signed_int_equal_conditions() {
|
||||
let filter = Filter::new()
|
||||
.with_signed_int("level", Comparison::Equal, 10)
|
||||
.with_signed_int("score", Comparison::Equal, 100);
|
||||
let statement = run(&filter).unwrap();
|
||||
assert_eq!(
|
||||
statement,
|
||||
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'level' AND value_int = ?1) AND id IN (SELECT entity_id FROM attribute WHERE id = 'score' AND value_int = ?2)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn builds_statement_with_mixed_bool_and_signed_int_conditions() {
|
||||
let filter = Filter::new().with_bool("is_active", true).with_signed_int(
|
||||
"level",
|
||||
Comparison::Equal,
|
||||
5,
|
||||
);
|
||||
let statement = run(&filter).unwrap();
|
||||
assert_eq!(
|
||||
statement,
|
||||
"SELECT * FROM entity WHERE 1=1 AND id IN (SELECT entity_id FROM attribute WHERE id = 'is_active' AND value_bool = ?1) AND id IN (SELECT entity_id FROM attribute WHERE id = 'level' AND value_int = ?2)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ mod tests {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub price: u64,
|
||||
pub sell_trend: i64,
|
||||
pub in_stock: bool,
|
||||
}
|
||||
impl EAV for Item {
|
||||
@@ -245,6 +246,7 @@ mod tests {
|
||||
.with_id(&value.id)
|
||||
.with_attribute("name", value.name)
|
||||
.with_attribute("price", value.price)
|
||||
.with_attribute("sell_trend", value.sell_trend)
|
||||
.with_attribute("in_stock", value.in_stock)
|
||||
}
|
||||
}
|
||||
@@ -254,6 +256,9 @@ mod tests {
|
||||
id: entity.id.clone(),
|
||||
name: entity.unwrap("name").expect("name is always present"),
|
||||
price: entity.unwrap("price").expect("price is always present"),
|
||||
sell_trend: entity
|
||||
.unwrap("sell_trend")
|
||||
.expect("sell_trend is always present"),
|
||||
in_stock: entity
|
||||
.unwrap("in_stock")
|
||||
.expect("in_stock is always present"),
|
||||
@@ -327,6 +332,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item_id = item.id.clone();
|
||||
@@ -337,6 +343,7 @@ mod tests {
|
||||
assert_eq!(entity.class, "item");
|
||||
assert_eq!(entity.value_of("name"), Some(&Value::from("Test Item")));
|
||||
assert_eq!(entity.value_of("price"), Some(&Value::from(100u64)));
|
||||
assert_eq!(entity.value_of("sell_trend"), Some(&Value::from(0i64)));
|
||||
assert_eq!(entity.value_of("in_stock"), Some(&Value::from(true)));
|
||||
}
|
||||
#[test]
|
||||
@@ -347,6 +354,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "First Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item_id = item1.id.clone();
|
||||
@@ -355,6 +363,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "Second Item".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 10,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item2);
|
||||
@@ -362,6 +371,7 @@ mod tests {
|
||||
let entity = catalog.items.get(&item_id).unwrap();
|
||||
assert_eq!(entity.value_of("name"), Some(&Value::from("Second Item")));
|
||||
assert_eq!(entity.value_of("price"), Some(&Value::from(200u64)));
|
||||
assert_eq!(entity.value_of("sell_trend"), Some(&Value::from(10i64)));
|
||||
assert_eq!(entity.value_of("in_stock"), Some(&Value::from(false)));
|
||||
assert_eq!(entity.state, EntityState::Updated);
|
||||
}
|
||||
@@ -374,12 +384,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item 1".to_string(),
|
||||
price: 10,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
},
|
||||
Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item 2".to_string(),
|
||||
price: 20,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
},
|
||||
];
|
||||
@@ -388,9 +400,13 @@ mod tests {
|
||||
let entity1 = catalog.items.get("item-1").unwrap();
|
||||
assert_eq!(entity1.state, EntityState::New);
|
||||
assert_eq!(entity1.value_of("name"), Some(&Value::from("Item 1")));
|
||||
assert_eq!(entity1.value_of("price"), Some(&Value::from(10u64)));
|
||||
assert_eq!(entity1.value_of("sell_trend"), Some(&Value::from(0i64)));
|
||||
let entity2 = catalog.items.get("item-2").unwrap();
|
||||
assert_eq!(entity2.state, EntityState::New);
|
||||
assert_eq!(entity2.value_of("name"), Some(&Value::from("Item 2")));
|
||||
assert_eq!(entity2.value_of("price"), Some(&Value::from(20u64)));
|
||||
assert_eq!(entity2.value_of("sell_trend"), Some(&Value::from(0i64)));
|
||||
}
|
||||
// ## 'get()'
|
||||
#[test]
|
||||
@@ -401,6 +417,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item.clone());
|
||||
@@ -415,6 +432,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item.clone());
|
||||
@@ -430,12 +448,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Unique Item".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item1.clone());
|
||||
@@ -453,12 +473,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 250,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item1.clone());
|
||||
@@ -477,6 +499,11 @@ mod tests {
|
||||
.get_by_class_and_attribute("in_stock", true)
|
||||
.unwrap();
|
||||
assert_eq!(retrieved_by_stock, Some(item1.clone()));
|
||||
// Test with i64 for sell_trend attribute
|
||||
let retrieved_by_sell_trend: Option<Item> = catalog
|
||||
.get_by_class_and_attribute("sell_trend", 0i64)
|
||||
.unwrap();
|
||||
assert_eq!(retrieved_by_sell_trend, Some(item1.clone()));
|
||||
}
|
||||
#[test]
|
||||
fn get_by_class_and_attribute_should_return_none_if_no_match() {
|
||||
@@ -486,6 +513,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item.clone());
|
||||
@@ -509,12 +537,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item1.clone());
|
||||
@@ -546,18 +576,21 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let item3 = Item {
|
||||
id: "item-3".to_string(),
|
||||
name: "Item Three".to_string(),
|
||||
price: 300,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item1.clone());
|
||||
@@ -580,6 +613,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item);
|
||||
@@ -612,6 +646,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item_id = item.id.clone();
|
||||
@@ -628,6 +663,7 @@ mod tests {
|
||||
id: "item-123".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item);
|
||||
@@ -653,6 +689,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 123,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item1.clone());
|
||||
@@ -682,6 +719,7 @@ mod tests {
|
||||
id: "item-to-delete".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 123,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item1.clone());
|
||||
@@ -714,6 +752,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Original Name".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(original_item.clone());
|
||||
@@ -726,6 +765,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Updated Name".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog2.upsert(updated_item.clone());
|
||||
@@ -761,18 +801,21 @@ mod tests {
|
||||
id: "update-me".to_string(),
|
||||
name: "Original".to_string(),
|
||||
price: 10,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item_to_delete = Item {
|
||||
id: "delete-me".to_string(),
|
||||
name: "Delete Me".to_string(),
|
||||
price: 20,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item_to_keep = Item {
|
||||
id: "keep-me".to_string(),
|
||||
name: "Keep Me".to_string(),
|
||||
price: 30,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog_setup.upsert(item_to_update_original.clone());
|
||||
@@ -787,6 +830,7 @@ mod tests {
|
||||
id: "add-me".to_string(),
|
||||
name: "Add Me".to_string(),
|
||||
price: 40,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog_ops.upsert(item_to_add.clone()); // State: New
|
||||
@@ -795,6 +839,7 @@ mod tests {
|
||||
id: "update-me".to_string(),
|
||||
name: "Updated".to_string(),
|
||||
price: 11,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog_ops.upsert(item_to_update_new.clone()); // State: Updated
|
||||
@@ -834,6 +879,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Test".to_string(),
|
||||
price: 10,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item);
|
||||
@@ -859,17 +905,23 @@ mod tests {
|
||||
let item_to_update = Item {
|
||||
id: "update-me".to_string(),
|
||||
name: "Original".to_string(),
|
||||
..Default::default()
|
||||
price: 0,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let item_to_delete = Item {
|
||||
id: "delete-me".to_string(),
|
||||
name: "Delete Me".to_string(),
|
||||
..Default::default()
|
||||
price: 0,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let item_untouched = Item {
|
||||
id: "keep-me".to_string(),
|
||||
name: "Keep Me".to_string(),
|
||||
..Default::default()
|
||||
price: 0,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item_to_update.clone());
|
||||
let _ = catalog.upsert(item_to_delete.clone());
|
||||
@@ -894,14 +946,18 @@ mod tests {
|
||||
let item_new = Item {
|
||||
id: "add-me".to_string(),
|
||||
name: "Add Me".to_string(),
|
||||
..Default::default()
|
||||
price: 0,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item_new.clone()); // State: New
|
||||
// An updated version of an existing item.
|
||||
let item_updated = Item {
|
||||
id: "update-me".to_string(),
|
||||
name: "Updated".to_string(),
|
||||
..Default::default()
|
||||
price: 0,
|
||||
sell_trend: 10,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item_updated.clone()); // State: Updated
|
||||
// An item to be deleted.
|
||||
@@ -941,6 +997,10 @@ mod tests {
|
||||
updated_item_entity.value_of("name"),
|
||||
Some(&Value::from("Updated"))
|
||||
);
|
||||
assert_eq!(
|
||||
updated_item_entity.value_of("sell_trend"),
|
||||
Some(&Value::from(10i64))
|
||||
);
|
||||
let untouched_item_entity = catalog.items.get("keep-me").unwrap();
|
||||
assert_eq!(untouched_item_entity.state, EntityState::Loaded);
|
||||
assert_eq!(
|
||||
@@ -967,6 +1027,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 123,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item_to_persist.clone());
|
||||
@@ -988,6 +1049,7 @@ mod tests {
|
||||
Some(&Value::from("Test Item"))
|
||||
);
|
||||
assert_eq!(loaded_entity.value_of("price"), Some(&Value::from(123u64)));
|
||||
assert_eq!(loaded_entity.value_of("sell_trend"), Some(&Value::from(0i64)));
|
||||
assert_eq!(loaded_entity.value_of("in_stock"), Some(&Value::from(true)));
|
||||
assert_eq!(loaded_entity.state, EntityState::Loaded); // Should be Synced after loading.
|
||||
// 6. Also verify by using the public 'get' method.
|
||||
@@ -1012,6 +1074,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item from DB".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item_in_db.clone());
|
||||
@@ -1022,6 +1085,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "In-memory version".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog2.upsert(item_in_memory);
|
||||
@@ -1045,6 +1109,10 @@ mod tests {
|
||||
entity_after_load.value_of("price"),
|
||||
Some(&Value::from(100u64))
|
||||
);
|
||||
assert_eq!(
|
||||
entity_after_load.value_of("sell_trend"),
|
||||
Some(&Value::from(0i64))
|
||||
);
|
||||
// 5. Verify using the public 'get' method.
|
||||
let retrieved_item: Item = catalog2.get("item-1").unwrap().unwrap();
|
||||
assert_eq!(retrieved_item, item_in_db);
|
||||
@@ -1102,12 +1170,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog1.upsert(item1.clone());
|
||||
@@ -1150,6 +1220,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "DB Version".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item_in_db.clone());
|
||||
@@ -1160,6 +1231,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Memory Version".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog2.upsert(item_in_memory);
|
||||
@@ -1203,6 +1275,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "In-memory only".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.upsert(item_in_memory.clone());
|
||||
@@ -1249,18 +1322,21 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
},
|
||||
Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
},
|
||||
Item {
|
||||
id: "item-3".to_string(),
|
||||
name: "Item Three".to_string(),
|
||||
price: 300,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
},
|
||||
];
|
||||
@@ -1290,12 +1366,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
},
|
||||
Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
},
|
||||
];
|
||||
@@ -1325,12 +1403,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
},
|
||||
Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
},
|
||||
];
|
||||
@@ -1357,6 +1437,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "DB Version".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
catalog_setup.upsert(item_in_db.clone()).unwrap();
|
||||
@@ -1366,6 +1447,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Memory Version".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
catalog.upsert(item_in_memory).unwrap();
|
||||
@@ -1387,6 +1469,55 @@ mod tests {
|
||||
assert_eq!(result.unwrap_err(), FailedTo::LoadFromDB);
|
||||
std::fs::remove_dir_all(invalid_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_by_filter_should_load_matching_sell_trend() {
|
||||
let db_path = "target/test_dbs/lbf_matching_sell_trend.db";
|
||||
let path = std::path::Path::new(db_path);
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
if path.exists() {
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
let mut catalog_setup = Catalog::new(db_path);
|
||||
catalog_setup.init().unwrap();
|
||||
let items = vec![
|
||||
Item {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 10,
|
||||
in_stock: true,
|
||||
},
|
||||
Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: -5,
|
||||
in_stock: false,
|
||||
},
|
||||
Item {
|
||||
id: "item-3".to_string(),
|
||||
name: "Item Three".to_string(),
|
||||
price: 300,
|
||||
sell_trend: 10,
|
||||
in_stock: true,
|
||||
},
|
||||
];
|
||||
catalog_setup.insert_many(items).unwrap();
|
||||
catalog_setup.persist().unwrap();
|
||||
|
||||
let mut catalog = Catalog::new(db_path);
|
||||
let filter = Filter::new().with_signed_int("sell_trend", Comparison::Equal, 10);
|
||||
assert!(catalog.load_by_filter(&filter).is_ok());
|
||||
|
||||
assert_eq!(catalog.items.len(), 2);
|
||||
assert!(catalog.items.contains_key("item-1"));
|
||||
assert!(catalog.items.contains_key("item-3"));
|
||||
assert!(!catalog.items.contains_key("item-2"));
|
||||
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
// ## Integration Tests
|
||||
#[test]
|
||||
fn integration_test_init_insert_persist_load_get() {
|
||||
@@ -1404,6 +1535,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Integration Test Item".to_string(),
|
||||
price: 999,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item_to_insert.clone());
|
||||
@@ -1434,12 +1566,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
},
|
||||
Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
},
|
||||
];
|
||||
@@ -1478,6 +1612,7 @@ mod tests {
|
||||
id: "item-to-delete".to_string(),
|
||||
name: "Test Item".to_string(),
|
||||
price: 123,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog1.upsert(item_to_delete.clone());
|
||||
@@ -1506,12 +1641,14 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "First Item".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Second Item".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let _ = catalog.upsert(item1.clone());
|
||||
@@ -1532,18 +1669,21 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Item One".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "item-2".to_string(),
|
||||
name: "Item Two".to_string(),
|
||||
price: 200,
|
||||
sell_trend: 0,
|
||||
in_stock: false,
|
||||
};
|
||||
let item3 = Item {
|
||||
id: "item-3".to_string(),
|
||||
name: "Item Three".to_string(),
|
||||
price: 150,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog.insert_many(vec![item1.clone(), item2.clone(), item3.clone()]);
|
||||
@@ -1579,6 +1719,7 @@ mod tests {
|
||||
id: "item-1".to_string(),
|
||||
name: "Original".to_string(),
|
||||
price: 100,
|
||||
sell_trend: 0,
|
||||
in_stock: true,
|
||||
};
|
||||
let _ = catalog_setup.upsert(initial_item);
|
||||
@@ -1611,8 +1752,8 @@ mod tests {
|
||||
catalog_verify.load_by_id("item-1").unwrap();
|
||||
let final_item: Item = catalog_verify.get("item-1").unwrap().unwrap();
|
||||
// The final state depends on which thread persisted last. One update will have been lost.
|
||||
let thread1_won = final_item.name == "Updated by Thread 1" && final_item.price == 100;
|
||||
let thread2_won = final_item.name == "Original" && final_item.price == 200;
|
||||
let thread1_won = final_item.name == "Updated by Thread 1" && final_item.price == 100 && final_item.sell_trend == 0;
|
||||
let thread2_won = final_item.name == "Original" && final_item.price == 200 && final_item.sell_trend == 0;
|
||||
assert!(
|
||||
thread1_won || thread2_won,
|
||||
"Final state must be the result of one of the threads winning the race."
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
|
||||
pub enum E {
|
||||
Equal,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
Lesser,
|
||||
LesserOrEqual,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
|
||||
pub enum E {
|
||||
Bool(bool),
|
||||
SignedInt(i64),
|
||||
}
|
||||
|
||||
@@ -19,6 +19,19 @@ impl Filter {
|
||||
));
|
||||
self
|
||||
}
|
||||
pub fn with_signed_int(
|
||||
mut self,
|
||||
attribute_name: &str,
|
||||
comparison: Comparison,
|
||||
value: i64,
|
||||
) -> Self {
|
||||
self.conditions.push((
|
||||
attribute_name.to_string(),
|
||||
comparison,
|
||||
Condition::SignedInt(value),
|
||||
));
|
||||
self
|
||||
}
|
||||
pub(crate) fn conditions(&self) -> impl Iterator<Item = &(String, Comparison, Condition)> {
|
||||
self.conditions.iter()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user