diff --git a/server/Cargo.toml b/server/Cargo.toml index e6f4f97..efce36a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -26,3 +26,4 @@ path = "src/lib.rs" tokio = { version = "1.0", features = ["full", "test-util"] } dotenv = "0.15" rstest = "0.24.0" +lazy_static = "1.5.0" diff --git a/server/tests/adresar/get_adresar_count_test.rs b/server/tests/adresar/get_adresar_count_test.rs index 2e1e5d3..7b6047c 100644 --- a/server/tests/adresar/get_adresar_count_test.rs +++ b/server/tests/adresar/get_adresar_count_test.rs @@ -5,20 +5,19 @@ use common::proto::multieko2::common::Empty; use crate::common::setup_test_db; use sqlx::PgPool; use tonic; +use std::sync::Arc; +use tokio::sync::Mutex; + +// Use a global mutex to synchronize test execution +// This prevents tests from interfering with each other +lazy_static::lazy_static! { + static ref TEST_MUTEX: Arc> = Arc::new(Mutex::new(())); +} #[fixture] async fn pool() -> PgPool { - let pool = setup_test_db().await; - - // Ensure sequence is set correctly to avoid primary key conflicts - sqlx::query_scalar!( - "SELECT setval('adresar_id_seq', COALESCE((SELECT MAX(id) FROM adresar), 0) + 1, false)" - ) - .fetch_one(&pool) - .await - .unwrap(); - - pool + // Just connect to the test database without truncating anything + setup_test_db().await } #[fixture] @@ -32,26 +31,16 @@ async fn closed_pool(#[future] pool: PgPool) -> PgPool { #[tokio::test] async fn test_count_increments_after_adding_record(#[future] pool: PgPool) { let pool = pool.await; + + // Lock to prevent concurrent test execution + let _guard = TEST_MUTEX.lock().await; // Get initial count of active records - let initial_count = sqlx::query_scalar!("SELECT COUNT(*) FROM adresar WHERE deleted = FALSE") - .fetch_one(&pool) - .await - .unwrap() - .unwrap_or(0); - - // Verify initial count from handler let initial_response = get_adresar_count(&pool, Empty {}).await.unwrap(); - assert_eq!(initial_response.count, initial_count); - - // Generate unique identifier for test data - let test_id = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis(); + let initial_count = initial_response.count; // Insert new active record with unique name - let unique_name = format!("Test Company {}", test_id); + let unique_name = format!("Test Company {}", chrono::Utc::now().timestamp_millis()); sqlx::query!( "INSERT INTO adresar (firma, deleted) VALUES ($1, FALSE)", unique_name @@ -67,68 +56,121 @@ async fn test_count_increments_after_adding_record(#[future] pool: PgPool) { #[rstest] #[tokio::test] -async fn test_deleted_records_not_counted(#[future] pool: PgPool) { +async fn test_add_multiple_records_and_check_count(#[future] pool: PgPool) { let pool = pool.await; + + // Lock to prevent concurrent test execution + let _guard = TEST_MUTEX.lock().await; // Get initial count - let initial_count = sqlx::query_scalar!("SELECT COUNT(*) FROM adresar WHERE deleted = FALSE") - .fetch_one(&pool) + let initial_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + let initial_count = initial_response.count; + + // Add 5 active records + let timestamp = chrono::Utc::now().timestamp_millis(); + for i in 0..5 { + let unique_name = format!("Batch Company {} #{}", timestamp, i); + sqlx::query!( + "INSERT INTO adresar (firma, deleted) VALUES ($1, FALSE)", + unique_name + ) + .execute(&pool) .await - .unwrap() - .unwrap_or(0); + .unwrap(); + } - // Generate unique test identifier - let test_id = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_millis(); + // Verify count increased by 5 + let updated_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(updated_response.count, initial_count + 5); +} - // Insert test records (2 active, 1 deleted) - let max_id = sqlx::query_scalar!("SELECT COALESCE(MAX(id), 0) FROM adresar") - .fetch_one(&pool) +#[rstest] +#[tokio::test] +async fn test_deleted_records_not_counted(#[future] pool: PgPool) { + let pool = pool.await; + + // Lock to prevent concurrent test execution + let _guard = TEST_MUTEX.lock().await; + + // Get initial count + let initial_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + let initial_count = initial_response.count; + + // Generate unique identifier for test + let timestamp = chrono::Utc::now().timestamp_millis(); + + // Insert 5 test records (2 active, 3 deleted) + for i in 0..5 { + let is_deleted = i >= 2; // First 2 active, next 3 deleted + let unique_name = format!("Deletion Test {} #{}", timestamp, i); + + sqlx::query!( + "INSERT INTO adresar (firma, deleted) VALUES ($1, $2)", + unique_name, + is_deleted + ) + .execute(&pool) .await - .unwrap() - .unwrap_or(0); + .unwrap(); + } - sqlx::query!( - "INSERT INTO adresar (id, firma, deleted) VALUES ($1, $2, FALSE)", - max_id + 1, - format!("Active 1 {}", test_id) - ) - .execute(&pool) - .await - .unwrap(); + // Verify count only increased by 2 (the non-deleted records) + let updated_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(updated_response.count, initial_count + 2); +} - sqlx::query!( - "INSERT INTO adresar (id, firma, deleted) VALUES ($1, $2, TRUE)", - max_id + 2, - format!("Deleted {}", test_id) - ) - .execute(&pool) - .await - .unwrap(); +#[rstest] +#[tokio::test] +async fn test_mark_existing_record_as_deleted(#[future] pool: PgPool) { + let pool = pool.await; + + // Lock to prevent concurrent test execution + let _guard = TEST_MUTEX.lock().await; - sqlx::query!( - "INSERT INTO adresar (id, firma, deleted) VALUES ($1, $2, FALSE)", - max_id + 3, - format!("Active 2 {}", test_id) - ) - .execute(&pool) - .await - .unwrap(); + // Get initial count + let initial_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + let initial_count = initial_response.count; - // Verify count only includes active records - let response = get_adresar_count(&pool, Empty {}).await.unwrap(); - assert_eq!(response.count, initial_count + 2); - - // Reset sequence to maintain test stability - sqlx::query_scalar!( - "SELECT setval('adresar_id_seq', $1, true)", - max_id + 4 + // Insert one record + let unique_name = format!("Delete Me {}", chrono::Utc::now().timestamp_millis()); + let record_id = sqlx::query_scalar!( + "INSERT INTO adresar (firma, deleted) VALUES ($1, FALSE) RETURNING id", + unique_name ) .fetch_one(&pool) .await .unwrap(); + + // Verify count increased by 1 + let after_insert_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(after_insert_response.count, initial_count + 1); + + // Mark record as deleted + sqlx::query!( + "UPDATE adresar SET deleted = TRUE WHERE id = $1", + record_id + ) + .execute(&pool) + .await + .unwrap(); + + // Verify count decreased back to initial + let after_delete_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(after_delete_response.count, initial_count); +} + +#[rstest] +#[tokio::test] +async fn test_edge_case_empty_table(#[future] pool: PgPool) { + let pool = pool.await; + + // Get current count (whatever it is) + let current_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + let current_count = current_response.count; + + // We're not going to empty the table (could violate FK constraints) + // But we can at least verify that the count is non-negative + assert!(current_count >= 0); } #[rstest] @@ -139,3 +181,56 @@ async fn test_database_error(#[future] closed_pool: PgPool) { assert!(result.is_err()); assert_eq!(result.unwrap_err().code(), tonic::Code::Internal); } + +#[rstest] +#[tokio::test] +async fn test_toggle_deleted_status(#[future] pool: PgPool) { + let pool = pool.await; + + // Lock to prevent concurrent test execution + let _guard = TEST_MUTEX.lock().await; + + // Get initial count + let initial_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + let initial_count = initial_response.count; + + // Create a new record that will be toggled + let unique_name = format!("Toggle Me {}", chrono::Utc::now().timestamp_millis()); + let record_id = sqlx::query_scalar!( + "INSERT INTO adresar (firma, deleted) VALUES ($1, FALSE) RETURNING id", + unique_name + ) + .fetch_one(&pool) + .await + .unwrap(); + + // Verify count increased by 1 + let after_insert_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(after_insert_response.count, initial_count + 1); + + // Mark as deleted + sqlx::query!( + "UPDATE adresar SET deleted = TRUE WHERE id = $1", + record_id + ) + .execute(&pool) + .await + .unwrap(); + + // Verify count back to initial + let after_delete_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(after_delete_response.count, initial_count); + + // Mark as NOT deleted + sqlx::query!( + "UPDATE adresar SET deleted = FALSE WHERE id = $1", + record_id + ) + .execute(&pool) + .await + .unwrap(); + + // Verify count increased again + let after_undelete_response = get_adresar_count(&pool, Empty {}).await.unwrap(); + assert_eq!(after_undelete_response.count, initial_count + 1); +}