use bootrust::dao::Dao;
use bootrust::database::{mysql::MySqlDatabase, DatabaseConfig, RelationalDatabase, Value};
use chrono::{DateTime, Utc};
use serial_test::serial;
use std::marker::PhantomData;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct Product {
id: i64,
name: String,
description: String,
price: f64,
stock: i64,
#[serde(with = "chrono::serde::ts_seconds")]
created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct CartItem {
id: i64,
user_id: i64,
product_id: i64,
quantity: i64,
#[serde(with = "chrono::serde::ts_seconds")]
added_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct Payment {
id: i64,
order_id: i64,
amount: f64,
payment_method: String,
transaction_id: String,
#[serde(with = "chrono::serde::ts_seconds")]
paid_at: DateTime<Utc>,
}
struct ECommerceDo<T: Sized, D: RelationalDatabase> {
database: D,
_table: PhantomData<T>,
}
impl<D: RelationalDatabase> Dao<Product> for ECommerceDo<Product, D> {
type Database = D;
fn new(database: Self::Database) -> Self {
ECommerceDo {
database,
_table: PhantomData,
}
}
fn database(&self) -> &Self::Database {
&self.database
}
fn table_name() -> String {
"products".to_string()
}
fn primary_key_column() -> String {
"id".to_string()
}
}
impl<D: RelationalDatabase> Dao<CartItem> for ECommerceDo<CartItem, D> {
type Database = D;
fn new(database: Self::Database) -> Self {
ECommerceDo {
database,
_table: PhantomData,
}
}
fn database(&self) -> &Self::Database {
&self.database
}
fn table_name() -> String {
"cart_items".to_string()
}
fn primary_key_column() -> String {
"id".to_string()
}
}
impl<D: RelationalDatabase> Dao<Payment> for ECommerceDo<Payment, D> {
type Database = D;
fn new(database: Self::Database) -> Self {
ECommerceDo {
database,
_table: PhantomData,
}
}
fn database(&self) -> &Self::Database {
&self.database
}
fn table_name() -> String {
"payments".to_string()
}
fn primary_key_column() -> String {
"id".to_string()
}
}
fn setup_test_db() -> MySqlDatabase {
let config = DatabaseConfig {
host: "localhost".to_string(),
port: 3306,
username: "root".to_string(),
password: "root".to_string(),
database_name: "test".to_string(),
max_size: 10,
};
let db = MySqlDatabase::connect(config).unwrap();
db.execute("DROP TABLE IF EXISTS products", vec![]).unwrap();
db.execute(
"CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
price DOUBLE NOT NULL,
stock INTEGER NOT NULL,
created_at BIGINT NOT NULL
)",
vec![],
)
.unwrap();
db.execute("DROP TABLE IF EXISTS cart_items", vec![])
.unwrap();
db.execute(
"CREATE TABLE cart_items (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
added_at BIGINT NOT NULL
)",
vec![],
)
.unwrap();
db.execute("DROP TABLE IF EXISTS payments", vec![]).unwrap();
db.execute(
"CREATE TABLE payments (
id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
amount DOUBLE NOT NULL,
payment_method TEXT NOT NULL,
transaction_id TEXT NOT NULL,
paid_at BIGINT NOT NULL
)",
vec![],
)
.unwrap();
db
}
fn create_test_product() -> Product {
Product {
id: 1,
name: "Test Product".to_string(),
description: "This is a test product.".to_string(),
price: 99.99,
stock: 100,
created_at: Utc::now(),
}
}
fn create_test_cart_item() -> CartItem {
CartItem {
id: 1,
user_id: 1,
product_id: 1,
quantity: 2,
added_at: Utc::now(),
}
}
fn create_test_payment() -> Payment {
Payment {
id: 1,
order_id: 1,
amount: 199.98,
payment_method: "Credit Card".to_string(),
transaction_id: "tx12345".to_string(),
paid_at: Utc::now(),
}
}
#[test]
#[serial]
fn test_add_product_to_cart() {
let db = setup_test_db();
let product_dao = ECommerceDo::new(db.clone());
let cart_dao = ECommerceDo::new(db.clone());
let product = create_test_product();
product_dao.create(&product).unwrap();
let mut cart_item = create_test_cart_item();
cart_item.product_id = product.id;
let result = cart_dao.create(&cart_item);
assert!(result.is_ok());
let added_item = cart_dao.find_by_id(Value::Bigint(cart_item.id)).unwrap();
assert!(added_item.is_some());
assert_eq!(added_item.unwrap().product_id, product.id);
}
#[test]
#[serial]
fn test_remove_product_from_cart() {
let db = setup_test_db();
let cart_dao = ECommerceDo::new(db.clone());
let cart_item = create_test_cart_item();
cart_dao.create(&cart_item).unwrap();
let result = cart_dao.delete(Value::Bigint(cart_item.id));
assert!(result.is_ok());
let removed_item = cart_dao.find_by_id(Value::Bigint(cart_item.id)).unwrap();
assert!(removed_item.is_none());
}
#[test]
#[serial]
fn test_update_cart_item_quantity() {
let db = setup_test_db();
let cart_dao = ECommerceDo::new(db.clone());
let mut cart_item = create_test_cart_item();
cart_dao.create(&cart_item).unwrap();
cart_item.quantity = 3;
let result = cart_dao.update(&cart_item);
assert!(result.is_ok());
let updated_item = cart_dao.find_by_id(Value::Bigint(cart_item.id)).unwrap();
assert_eq!(updated_item.unwrap().quantity, 3);
}
#[test]
#[serial]
fn test_payment_process() {
let db = setup_test_db();
let payment_dao = ECommerceDo::new(db.clone());
let order_id = 1;
let mut payment = create_test_payment();
payment.order_id = order_id;
let result = payment_dao.create(&payment);
assert!(result.is_ok());
let saved_payment = payment_dao.find_by_id(Value::Bigint(payment.id)).unwrap();
assert!(saved_payment.is_some());
assert_eq!(saved_payment.unwrap().order_id, order_id);
}
#[test]
#[serial]
fn test_stock_update() {
let db = setup_test_db();
let product_dao = ECommerceDo::new(db.clone());
let mut product = create_test_product();
product_dao.create(&product).unwrap();
product.stock = 50;
let result = product_dao.update(&product);
assert!(result.is_ok());
let updated_product = product_dao.find_by_id(Value::Bigint(product.id)).unwrap();
assert_eq!(updated_product.unwrap().stock, 50);
}
#[test]
#[serial]
fn test_transaction() {
let db = setup_test_db();
let product_dao = ECommerceDo::new(db.clone());
let cart_dao = ECommerceDo::new(db.clone());
let payment_dao = ECommerceDo::new(db.clone());
let result = product_dao.begin_transaction();
assert!(result.is_ok());
let product = create_test_product();
let result = product_dao.create(&product);
assert!(result.is_ok());
let mut cart_item = create_test_cart_item();
cart_item.product_id = product.id;
let result = cart_dao.create(&cart_item);
assert!(result.is_ok());
let payment = create_test_payment();
let result = payment_dao.create(&payment);
assert!(result.is_ok());
let result = product_dao.commit();
assert!(result.is_ok());
let found_product = product_dao.find_by_id(Value::Bigint(product.id)).unwrap();
assert!(found_product.is_some());
let found_cart_item = cart_dao.find_by_id(Value::Bigint(cart_item.id)).unwrap();
assert!(found_cart_item.is_some());
let found_payment = payment_dao.find_by_id(Value::Bigint(payment.id)).unwrap();
assert!(found_payment.is_some());
}
#[test]
#[serial]
fn test_transaction_rollback() {
let db = setup_test_db();
let product_dao = ECommerceDo::new(db.clone());
let cart_dao = ECommerceDo::new(db.clone());
let result = product_dao.begin_transaction();
assert!(result.is_ok());
let product = create_test_product();
let result = product_dao.create(&product);
assert!(result.is_ok());
let mut cart_item = create_test_cart_item();
cart_item.product_id = 999; let _result = cart_dao.create(&cart_item);
let result = product_dao.rollback();
assert!(result.is_ok());
let found_product = product_dao.find_by_id(Value::Bigint(product.id)).unwrap();
assert!(found_product.is_none());
let found_cart_item = cart_dao.find_by_id(Value::Bigint(cart_item.id)).unwrap();
assert!(found_cart_item.is_none());
}