Compare commits
No commits in common. "a3554d10c42b172ac60150c44c4332174513bc18" and "d0e54c29c0f6a04f7f7d0c4ae0b8d7d7552279f4" have entirely different histories.
a3554d10c4
...
d0e54c29c0
|
|
@ -92,7 +92,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn update_equipment(&self, equipment: Equipment) -> Result<bool, sqlx::Error> {
|
pub async fn update_equipment(&self, equipment: Equipment) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `equipment` SET inv_number=?, maintenance_date=?, worker_id=?, name=?, is_written_off=? WHERE id=?;")
|
match sqlx::query("UPDATE Brewery.equipment SET inv_number=?, maintenance_date=?, worker_id=?, name=?, is_written_off=? WHERE id=?;")
|
||||||
.bind(equipment.inv_number)
|
.bind(equipment.inv_number)
|
||||||
.bind(equipment.maintenance_date)
|
.bind(equipment.maintenance_date)
|
||||||
.bind(equipment.worker.id)
|
.bind(equipment.worker.id)
|
||||||
|
|
@ -149,7 +149,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
pub async fn add_worker(&self, worker: WorkerRow) -> Result<bool, sqlx::Error> {
|
pub async fn add_worker(&self, worker: WorkerRow) -> Result<bool, sqlx::Error> {
|
||||||
// sqlx::query!("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);",worker.position.id, worker.hire_date ,worker.is_fired,worker.full_name)
|
// sqlx::query!("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);",worker.position.id, worker.hire_date ,worker.is_fired,worker.full_name)
|
||||||
match sqlx::query("INSERT INTO `worker` (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);")
|
match sqlx::query("INSERT INTO Brewery.worker (id, position_id, hire_date, is_fired, full_name) VALUES(0, ?, ?, ?, ?);")
|
||||||
.bind(worker.position_id)
|
.bind(worker.position_id)
|
||||||
.bind(worker.hire_date)
|
.bind(worker.hire_date)
|
||||||
.bind(worker.is_fired)
|
.bind(worker.is_fired)
|
||||||
|
|
@ -165,7 +165,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn add_position(&self, position: Position) -> Result<bool, sqlx::Error> {
|
pub async fn add_position(&self, position: Position) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("INSERT INTO `position` (id, name, wage) VALUES(0, ?, ?);")
|
match sqlx::query("INSERT INTO Brewery.`position` (id, name, wage) VALUES(0, ?, ?);")
|
||||||
.bind(position.name)
|
.bind(position.name)
|
||||||
.bind(position.wage)
|
.bind(position.wage)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
|
|
@ -190,7 +190,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
pub async fn add_material(&self, mat: MaterialRow) -> Result<bool, sqlx::Error> {
|
pub async fn add_material(&self, mat: MaterialRow) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query(
|
match sqlx::query(
|
||||||
"INSERT INTO `material` (id, name, quantity, category_id) VALUES(0, ?, ?, ?);",
|
"INSERT INTO Brewery.material (id, name, quantity, category_id) VALUES(0, ?, ?, ?);",
|
||||||
)
|
)
|
||||||
.bind(mat.name)
|
.bind(mat.name)
|
||||||
.bind(mat.quantity)
|
.bind(mat.quantity)
|
||||||
|
|
@ -203,7 +203,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn delete_position(&self, position: Position) -> Result<bool, sqlx::Error> {
|
pub async fn delete_position(&self, position: Position) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("DELETE FROM `position` WHERE id = ?;")
|
match sqlx::query("DELETE FROM Brewery.`position` WHERE id = ?;")
|
||||||
.bind(position.id)
|
.bind(position.id)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
.await
|
.await
|
||||||
|
|
@ -216,7 +216,7 @@ impl DBOperator {
|
||||||
&self,
|
&self,
|
||||||
mat_cat: MaterialCategory,
|
mat_cat: MaterialCategory,
|
||||||
) -> Result<bool, sqlx::Error> {
|
) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("DELETE FROM `material_category` WHERE id = ?;")
|
match sqlx::query("DELETE FROM Brewery.`material_category` WHERE id = ?;")
|
||||||
.bind(mat_cat.id)
|
.bind(mat_cat.id)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
.await
|
.await
|
||||||
|
|
@ -226,7 +226,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn delete_material(&self, mat: MaterialRow) -> Result<bool, sqlx::Error> {
|
pub async fn delete_material(&self, mat: MaterialRow) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("DELETE FROM `material` WHERE id = ?;")
|
match sqlx::query("DELETE FROM Brewery.`material` WHERE id = ?;")
|
||||||
.bind(mat.id)
|
.bind(mat.id)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
.await
|
.await
|
||||||
|
|
@ -237,7 +237,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_worker(&self, worker: WorkerRow) -> Result<bool, sqlx::Error> {
|
pub async fn update_worker(&self, worker: WorkerRow) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `worker` SET position_id=?, hire_date=?, is_fired=?, full_name=? WHERE id=?;")
|
match sqlx::query("UPDATE Brewery.worker SET position_id=?, hire_date=?, is_fired=?, full_name=? WHERE id=?;")
|
||||||
.bind(worker.position_id)
|
.bind(worker.position_id)
|
||||||
.bind(worker.hire_date)
|
.bind(worker.hire_date)
|
||||||
.bind(worker.is_fired)
|
.bind(worker.is_fired)
|
||||||
|
|
@ -257,7 +257,7 @@ impl DBOperator {
|
||||||
&self,
|
&self,
|
||||||
mat_cat: MaterialCategory,
|
mat_cat: MaterialCategory,
|
||||||
) -> Result<bool, sqlx::Error> {
|
) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `material_category` SET name=? WHERE id=?;")
|
match sqlx::query("UPDATE Brewery.`material_category` SET name=? WHERE id=?;")
|
||||||
.bind(mat_cat.name)
|
.bind(mat_cat.name)
|
||||||
.bind(mat_cat.id)
|
.bind(mat_cat.id)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
|
|
@ -269,7 +269,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
pub async fn update_material(&self, mat: MaterialRow) -> Result<bool, sqlx::Error> {
|
pub async fn update_material(&self, mat: MaterialRow) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query(
|
match sqlx::query(
|
||||||
"UPDATE `material` SET name=?, quantity=?, category_id=? WHERE id=?;",
|
"UPDATE Brewery.material SET name=?, quantity=?, category_id=? WHERE id=?;",
|
||||||
)
|
)
|
||||||
.bind(mat.name)
|
.bind(mat.name)
|
||||||
.bind(mat.quantity)
|
.bind(mat.quantity)
|
||||||
|
|
@ -303,7 +303,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_position(&self, pos: Position) -> Result<bool, sqlx::Error> {
|
pub async fn update_position(&self, pos: Position) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `position`SET name=?, wage=? WHERE id=?")
|
match sqlx::query("UPDATE Brewery.position SET name=?, wage=? WHERE id=?")
|
||||||
.bind(pos.name)
|
.bind(pos.name)
|
||||||
.bind(pos.wage)
|
.bind(pos.wage)
|
||||||
.bind(pos.id)
|
.bind(pos.id)
|
||||||
|
|
@ -319,7 +319,7 @@ impl DBOperator {
|
||||||
username: String,
|
username: String,
|
||||||
password_hash: String,
|
password_hash: String,
|
||||||
) -> Result<bool, sqlx::Error> {
|
) -> Result<bool, sqlx::Error> {
|
||||||
let ans = sqlx::query("SELECT * FROM `profile` WHERE login=? AND password=?")
|
let ans = sqlx::query("SELECT * FROM Brewery.profile WHERE login=? AND password=?")
|
||||||
.bind(username)
|
.bind(username)
|
||||||
.bind(password_hash)
|
.bind(password_hash)
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
|
|
@ -344,11 +344,11 @@ impl DBOperator {
|
||||||
profile.login,
|
profile.login,
|
||||||
position.id
|
position.id
|
||||||
FROM
|
FROM
|
||||||
`profile`
|
Brewery.profile
|
||||||
JOIN
|
JOIN
|
||||||
`worker`
|
Brewery.worker
|
||||||
JOIN
|
JOIN
|
||||||
`position`
|
Brewery.position
|
||||||
ON profile.worker_id = worker.id
|
ON profile.worker_id = worker.id
|
||||||
AND profile.login=?
|
AND profile.login=?
|
||||||
AND worker.position_id =position.id"#,
|
AND worker.position_id =position.id"#,
|
||||||
|
|
@ -397,7 +397,7 @@ impl DBOperator {
|
||||||
salary_size: BigDecimal,
|
salary_size: BigDecimal,
|
||||||
comment: Option<String>,
|
comment: Option<String>,
|
||||||
) -> Result<bool, sqlx::Error> {
|
) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("INSERT INTO `salary` (id, worker_id, salary_size, salary_date, comment) VALUES(0, ?, ?, ?, ?);")
|
match sqlx::query("INSERT INTO Brewery.salary (id, worker_id, salary_size, salary_date, comment) VALUES(0, ?, ?, ?, ?);")
|
||||||
.bind(worker_id)
|
.bind(worker_id)
|
||||||
.bind(salary_size)
|
.bind(salary_size)
|
||||||
.bind(salary_date)
|
.bind(salary_date)
|
||||||
|
|
@ -409,7 +409,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn update_salary(&self, salary: Salary) -> Result<bool, sqlx::Error> {
|
pub async fn update_salary(&self, salary: Salary) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `salary` SET worker_id=?, salary_size=?, salary_date=?, comment=? WHERE id=?;")
|
match sqlx::query("UPDATE Brewery.salary SET worker_id=?, salary_size=?, salary_date=?, comment=? WHERE id=?;")
|
||||||
.bind(salary.worker.id)
|
.bind(salary.worker.id)
|
||||||
.bind(salary.salary_size)
|
.bind(salary.salary_size)
|
||||||
.bind(salary.salary_date)
|
.bind(salary.salary_date)
|
||||||
|
|
@ -422,7 +422,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn remove_salary(&self, id: i32) -> Result<bool, sqlx::Error> {
|
pub async fn remove_salary(&self, id: i32) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("DELETE FROM `salary` WHERE id=?;")
|
match sqlx::query("DELETE FROM Brewery.salary WHERE id=?;")
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
.await
|
.await
|
||||||
|
|
@ -432,7 +432,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_recipes(&self) -> Result<Vec<Recipe>, sqlx::Error> {
|
pub async fn get_recipes(&self) -> Result<Vec<Recipe>, sqlx::Error> {
|
||||||
let pre_ret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM `recipe`")
|
let pre_ret = sqlx::query_as::<_, RecipeRow>("SELECT * FROM recipe")
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
.await
|
.await
|
||||||
.unwrap_or(Vec::new());
|
.unwrap_or(Vec::new());
|
||||||
|
|
@ -480,7 +480,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn update_recipe(&self, recipe: Recipe) -> Result<bool, sqlx::Error> {
|
pub async fn update_recipe(&self, recipe: Recipe) -> Result<bool, sqlx::Error> {
|
||||||
match sqlx::query("UPDATE `recipe` SET name=? WHERE id=?;")
|
match sqlx::query("UPDATE Brewery.recipe SET name=? WHERE id=?;")
|
||||||
.bind(recipe.name)
|
.bind(recipe.name)
|
||||||
.bind(recipe.id)
|
.bind(recipe.id)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
|
|
@ -491,7 +491,7 @@ impl DBOperator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_recipe_elements(&self) -> Result<Vec<RecipeElement>, sqlx::Error> {
|
pub async fn get_recipe_elements(&self) -> Result<Vec<RecipeElement>, sqlx::Error> {
|
||||||
let pre_ret = sqlx::query_as::<_, RecipeElementRow>("SELECT id, recipe_id, material_id, equipment_id, quantity FROM `recipe_element`;").fetch_all(&self.pool).await;
|
let pre_ret = sqlx::query_as::<_, RecipeElementRow>("SELECT id, recipe_id, material_id, equipment_id, quantity FROM Brewery.recipe_element;").fetch_all(&self.pool).await;
|
||||||
|
|
||||||
let mats = self.get_materials().await.unwrap();
|
let mats = self.get_materials().await.unwrap();
|
||||||
let eqs = self.get_equipment().await.unwrap();
|
let eqs = self.get_equipment().await.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,353 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::database::DBOperator;
|
||||||
|
use crate::models::*;
|
||||||
|
use bigdecimal::BigDecimal;
|
||||||
|
use chrono::Local;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// UNIT TESTS (no database required)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/// Test 1: Position default values and construction
|
||||||
|
#[test]
|
||||||
|
fn test_position_default_and_construction() {
|
||||||
|
let default_pos = Position::default();
|
||||||
|
assert_eq!(default_pos.id, 1);
|
||||||
|
assert_eq!(default_pos.name, "");
|
||||||
|
assert_eq!(default_pos.wage, BigDecimal::from(0));
|
||||||
|
|
||||||
|
let custom_pos = Position {
|
||||||
|
id: 42,
|
||||||
|
name: "Пивовар".to_string(),
|
||||||
|
wage: BigDecimal::from(65000),
|
||||||
|
};
|
||||||
|
assert_eq!(custom_pos.id, 42);
|
||||||
|
assert_eq!(custom_pos.name, "Пивовар");
|
||||||
|
assert_eq!(custom_pos.wage, BigDecimal::from(65000));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 2: Worker default values and construction with nested Position
|
||||||
|
#[test]
|
||||||
|
fn test_worker_default_and_construction() {
|
||||||
|
let default_worker = Worker::default();
|
||||||
|
assert_eq!(default_worker.id, 0);
|
||||||
|
assert_eq!(default_worker.full_name, "");
|
||||||
|
assert!(!default_worker.is_fired);
|
||||||
|
|
||||||
|
let pos = Position {
|
||||||
|
id: 3,
|
||||||
|
name: "Пивовар".to_string(),
|
||||||
|
wage: BigDecimal::from(65000),
|
||||||
|
};
|
||||||
|
let worker = Worker {
|
||||||
|
id: 1,
|
||||||
|
full_name: "Иванов Иван".to_string(),
|
||||||
|
hire_date: Local::now(),
|
||||||
|
position: pos.clone(),
|
||||||
|
is_fired: false,
|
||||||
|
};
|
||||||
|
assert_eq!(worker.full_name, "Иванов Иван");
|
||||||
|
assert_eq!(worker.position.name, "Пивовар");
|
||||||
|
assert_eq!(worker.position.wage, BigDecimal::from(65000));
|
||||||
|
assert!(!worker.is_fired);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 3: MaterialCategory default and Material construction with category
|
||||||
|
#[test]
|
||||||
|
fn test_material_and_category_construction() {
|
||||||
|
let default_cat = MaterialCategory::default();
|
||||||
|
assert_eq!(default_cat.id, 0);
|
||||||
|
assert_eq!(default_cat.name, "");
|
||||||
|
|
||||||
|
let cat = MaterialCategory {
|
||||||
|
id: 2,
|
||||||
|
name: "Солод".to_string(),
|
||||||
|
};
|
||||||
|
let mat = Material {
|
||||||
|
id: 1,
|
||||||
|
name: "Курский тёмный".to_string(),
|
||||||
|
quantity: 20,
|
||||||
|
category: cat.clone(),
|
||||||
|
};
|
||||||
|
assert_eq!(mat.name, "Курский тёмный");
|
||||||
|
assert_eq!(mat.quantity, 20);
|
||||||
|
assert_eq!(mat.category.name, "Солод");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 4: ModalStatus default and equality (using matches! to avoid Debug)
|
||||||
|
#[test]
|
||||||
|
fn test_modal_status_default_and_equality() {
|
||||||
|
let default_status = ModalStatus::default();
|
||||||
|
assert!(matches!(default_status, ModalStatus::Add));
|
||||||
|
assert!(!matches!(default_status, ModalStatus::Edit));
|
||||||
|
assert!(!matches!(default_status, ModalStatus::Remove));
|
||||||
|
|
||||||
|
let edit_status = ModalStatus::Edit;
|
||||||
|
assert!(matches!(edit_status, ModalStatus::Edit));
|
||||||
|
assert!(!matches!(edit_status, ModalStatus::Add));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 5: Equipment default and construction with nested Worker
|
||||||
|
#[test]
|
||||||
|
fn test_equipment_default_and_construction() {
|
||||||
|
let default_eq = Equipment::default();
|
||||||
|
assert_eq!(default_eq.id, 0);
|
||||||
|
assert_eq!(default_eq.name, "");
|
||||||
|
assert_eq!(default_eq.inv_number, "");
|
||||||
|
assert!(!default_eq.is_written_off);
|
||||||
|
|
||||||
|
let pos = Position {
|
||||||
|
id: 1,
|
||||||
|
name: "Уборщик".to_string(),
|
||||||
|
wage: BigDecimal::from(20000),
|
||||||
|
};
|
||||||
|
let worker = Worker {
|
||||||
|
id: 1,
|
||||||
|
full_name: "Иван Петров".to_string(),
|
||||||
|
hire_date: Local::now(),
|
||||||
|
position: pos,
|
||||||
|
is_fired: false,
|
||||||
|
};
|
||||||
|
let equipment = Equipment {
|
||||||
|
id: 1,
|
||||||
|
name: "Метла деревяная".to_string(),
|
||||||
|
inv_number: "М-1".to_string(),
|
||||||
|
maintenance_date: Local::now(),
|
||||||
|
worker: worker.clone(),
|
||||||
|
is_written_off: false,
|
||||||
|
};
|
||||||
|
assert_eq!(equipment.name, "Метла деревяная");
|
||||||
|
assert_eq!(equipment.inv_number, "М-1");
|
||||||
|
assert_eq!(equipment.worker.full_name, "Иван Петров");
|
||||||
|
assert!(!equipment.is_written_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// INTEGRATION TESTS (require database connection)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/// Test 6: Integration — get_position returns positions from DB
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_integration_get_position() {
|
||||||
|
let db = DBOperator::new().await;
|
||||||
|
let positions = db.get_position().await.expect("get_position failed");
|
||||||
|
// The seed data has 4 positions: Уборщик, Кладовщик, Пивовар, Менеджер
|
||||||
|
assert!(
|
||||||
|
positions.len() >= 4,
|
||||||
|
"Expected at least 4 positions, got {}",
|
||||||
|
positions.len()
|
||||||
|
);
|
||||||
|
let names: Vec<&str> = positions.iter().map(|p| p.name.as_str()).collect();
|
||||||
|
assert!(names.contains(&"Уборщик"), "Should contain Уборщик");
|
||||||
|
assert!(names.contains(&"Пивовар"), "Should contain Пивовар");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 7: Integration — add_worker and get_workers round-trip
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_integration_add_and_get_worker() {
|
||||||
|
let db = DBOperator::new().await;
|
||||||
|
|
||||||
|
// Add a new worker
|
||||||
|
let new_worker = WorkerRow {
|
||||||
|
id: 0, // auto-increment
|
||||||
|
full_name: "Тестовый Работник Для Теста".to_string(),
|
||||||
|
hire_date: Local::now(),
|
||||||
|
position_id: 1, // Уборщик
|
||||||
|
is_fired: false,
|
||||||
|
};
|
||||||
|
let add_result = db.add_worker(new_worker).await.expect("add_worker failed");
|
||||||
|
assert!(add_result, "add_worker should return true");
|
||||||
|
|
||||||
|
// Verify the worker appears in get_workers
|
||||||
|
let workers = db.get_workers().await.expect("get_workers failed");
|
||||||
|
let found = workers
|
||||||
|
.iter()
|
||||||
|
.any(|w| w.full_name == "Тестовый Работник Для Теста");
|
||||||
|
assert!(found, "Newly added worker should appear in get_workers");
|
||||||
|
|
||||||
|
// Cleanup: find and mark the test worker as fired
|
||||||
|
let test_worker = workers
|
||||||
|
.iter()
|
||||||
|
.find(|w| w.full_name == "Тестовый Работник Для Теста")
|
||||||
|
.expect("Test worker not found for cleanup");
|
||||||
|
let fired_row = WorkerRow {
|
||||||
|
id: test_worker.id,
|
||||||
|
full_name: test_worker.full_name.clone(),
|
||||||
|
hire_date: test_worker.hire_date,
|
||||||
|
position_id: test_worker.position.id,
|
||||||
|
is_fired: true,
|
||||||
|
};
|
||||||
|
db.update_worker(fired_row)
|
||||||
|
.await
|
||||||
|
.expect("update_worker (fire) failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 8: Integration — material_category full CRUD round-trip
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_integration_material_category_crud() {
|
||||||
|
let db = DBOperator::new().await;
|
||||||
|
|
||||||
|
// Add a new category
|
||||||
|
let new_cat = MaterialCategory {
|
||||||
|
id: 0,
|
||||||
|
name: "Тестовая Категория Интеграция".to_string(),
|
||||||
|
};
|
||||||
|
let add_result = db
|
||||||
|
.add_material_category(new_cat)
|
||||||
|
.await
|
||||||
|
.expect("add_material_category failed");
|
||||||
|
assert!(add_result, "add_material_category should return true");
|
||||||
|
|
||||||
|
// Verify it appears in get_mcat
|
||||||
|
let cats = db.get_mcat().await.expect("get_mcat failed");
|
||||||
|
let found_cat = cats
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.name == "Тестовая Категория Интеграция")
|
||||||
|
.expect("New category not found in get_mcat");
|
||||||
|
|
||||||
|
// Update the category
|
||||||
|
let mut updated_cat = found_cat.clone();
|
||||||
|
updated_cat.name = "Обновлённая Тестовая Категория".to_string();
|
||||||
|
let update_result = db
|
||||||
|
.update_material_category(updated_cat.clone())
|
||||||
|
.await
|
||||||
|
.expect("update_material_category failed");
|
||||||
|
assert!(update_result, "update_material_category should return true");
|
||||||
|
|
||||||
|
// Verify update
|
||||||
|
let cats_after = db.get_mcat().await.expect("get_mcat after update failed");
|
||||||
|
let updated = cats_after
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.id == found_cat.id)
|
||||||
|
.expect("Category not found after update");
|
||||||
|
assert_eq!(updated.name, "Обновлённая Тестовая Категория");
|
||||||
|
|
||||||
|
// Delete the category
|
||||||
|
let delete_result = db
|
||||||
|
.delete_material_category(updated_cat)
|
||||||
|
.await
|
||||||
|
.expect("delete_material_category failed");
|
||||||
|
assert!(delete_result, "delete_material_category should return true");
|
||||||
|
|
||||||
|
// Verify deletion
|
||||||
|
let cats_final = db.get_mcat().await.expect("get_mcat after delete failed");
|
||||||
|
let not_found = cats_final.iter().find(|c| c.id == found_cat.id).is_none();
|
||||||
|
assert!(not_found, "Deleted category should not appear in get_mcat");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 9: Integration — salary full CRUD round-trip
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_integration_salary_crud() {
|
||||||
|
let db = DBOperator::new().await;
|
||||||
|
|
||||||
|
// Get existing workers to use a valid worker_id
|
||||||
|
let workers = db.get_workers().await.expect("get_workers failed");
|
||||||
|
let worker = workers.first().expect("No workers in DB for salary test");
|
||||||
|
|
||||||
|
let salary_date = Local::now();
|
||||||
|
let salary_size = BigDecimal::from(99999);
|
||||||
|
let comment = Some("Интеграционный тест зарплаты".to_string());
|
||||||
|
|
||||||
|
// Add salary
|
||||||
|
let add_result = db
|
||||||
|
.add_salary(worker.id, salary_date, salary_size.clone(), comment.clone())
|
||||||
|
.await
|
||||||
|
.expect("add_salary failed");
|
||||||
|
assert!(add_result, "add_salary should return true");
|
||||||
|
|
||||||
|
// Verify it appears in get_salaries
|
||||||
|
let salaries = db.get_salaries().await.expect("get_salaries failed");
|
||||||
|
let found_sal = salaries
|
||||||
|
.iter()
|
||||||
|
.find(|s| {
|
||||||
|
s.worker.id == worker.id
|
||||||
|
&& s.salary_size == salary_size
|
||||||
|
&& s.comment == "Интеграционный тест зарплаты"
|
||||||
|
})
|
||||||
|
.expect("Newly added salary not found in get_salaries");
|
||||||
|
|
||||||
|
// Remove the salary
|
||||||
|
let remove_result = db
|
||||||
|
.remove_salary(found_sal.id)
|
||||||
|
.await
|
||||||
|
.expect("remove_salary failed");
|
||||||
|
assert!(remove_result, "remove_salary should return true");
|
||||||
|
|
||||||
|
// Verify removal
|
||||||
|
let salaries_after = db
|
||||||
|
.get_salaries()
|
||||||
|
.await
|
||||||
|
.expect("get_salaries after remove failed");
|
||||||
|
let not_found = salaries_after
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.id == found_sal.id)
|
||||||
|
.is_none();
|
||||||
|
assert!(
|
||||||
|
not_found,
|
||||||
|
"Removed salary should not appear in get_salaries"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test 10: Integration — recipe full CRUD round-trip
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_integration_recipe_crud() {
|
||||||
|
let db = DBOperator::new().await;
|
||||||
|
|
||||||
|
// Add a new recipe
|
||||||
|
let new_recipe = Recipe {
|
||||||
|
id: 0,
|
||||||
|
name: "Тестовый Рецепт Интеграция".to_string(),
|
||||||
|
elements: HashSet::new(),
|
||||||
|
};
|
||||||
|
let recipe_id = db.add_recipe(new_recipe).await.expect("add_recipe failed");
|
||||||
|
assert!(recipe_id > 0, "add_recipe should return a valid ID");
|
||||||
|
|
||||||
|
// Verify it appears in get_recipes
|
||||||
|
let recipes = db.get_recipes().await.expect("get_recipes failed");
|
||||||
|
let found_recipe = recipes
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.name == "Тестовый Рецепт Интеграция")
|
||||||
|
.expect("Newly added recipe not found in get_recipes");
|
||||||
|
assert_eq!(found_recipe.id as u64, recipe_id);
|
||||||
|
|
||||||
|
// Update the recipe
|
||||||
|
let mut updated_recipe = found_recipe.clone();
|
||||||
|
updated_recipe.name = "Обновлённый Тестовый Рецепт".to_string();
|
||||||
|
let update_result = db
|
||||||
|
.update_recipe(updated_recipe.clone())
|
||||||
|
.await
|
||||||
|
.expect("update_recipe failed");
|
||||||
|
assert!(update_result, "update_recipe should return true");
|
||||||
|
|
||||||
|
// Verify update
|
||||||
|
let recipes_after = db
|
||||||
|
.get_recipes()
|
||||||
|
.await
|
||||||
|
.expect("get_recipes after update failed");
|
||||||
|
let updated = recipes_after
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.id == found_recipe.id)
|
||||||
|
.expect("Recipe not found after update");
|
||||||
|
assert_eq!(updated.name, "Обновлённый Тестовый Рецепт");
|
||||||
|
|
||||||
|
// Remove the recipe
|
||||||
|
let remove_result = db
|
||||||
|
.remove_recipe(found_recipe.id)
|
||||||
|
.await
|
||||||
|
.expect("remove_recipe failed");
|
||||||
|
assert!(remove_result, "remove_recipe should return true");
|
||||||
|
|
||||||
|
// Verify removal
|
||||||
|
let recipes_final = db
|
||||||
|
.get_recipes()
|
||||||
|
.await
|
||||||
|
.expect("get_recipes after remove failed");
|
||||||
|
let not_found = recipes_final
|
||||||
|
.iter()
|
||||||
|
.find(|r| r.id == found_recipe.id)
|
||||||
|
.is_none();
|
||||||
|
assert!(not_found, "Removed recipe should not appear in get_recipes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ mod reports;
|
||||||
mod tab;
|
mod tab;
|
||||||
mod worker_database;
|
mod worker_database;
|
||||||
mod worker_tab;
|
mod worker_tab;
|
||||||
|
mod lib;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue