From a535156aad49dec59d6bbe099ffafebcc2bca9ac Mon Sep 17 00:00:00 2001 From: filipriec Date: Wed, 5 Mar 2025 19:55:36 +0100 Subject: [PATCH] tests are now working for get count, two are failing --- .../get_table_data_by_position_test.rs | 483 +++++++++--------- 1 file changed, 245 insertions(+), 238 deletions(-) diff --git a/server/tests/tables_data/handlers/get_table_data_by_position_test.rs b/server/tests/tables_data/handlers/get_table_data_by_position_test.rs index 28ae03a..ad7ef68 100644 --- a/server/tests/tables_data/handlers/get_table_data_by_position_test.rs +++ b/server/tests/tables_data/handlers/get_table_data_by_position_test.rs @@ -1,17 +1,12 @@ // tests/tables_data/handlers/get_table_data_by_position_test.rs use rstest::{fixture, rstest}; +use tonic; use sqlx::PgPool; -use common::proto::multieko2::tables_data::{GetTableDataByPositionRequest, GetTableDataResponse}; +use common::proto::multieko2::tables_data::GetTableDataByPositionRequest; use server::tables_data::handlers::get_table_data_by_position; use crate::common::setup_test_db; -use tonic; -use std::sync::Arc; -use tokio::sync::Mutex; - -// Global mutex to prevent test interference -lazy_static::lazy_static! { - static ref TEST_MUTEX: Arc> = Arc::new(Mutex::new(())); -} +use chrono::Utc; +use serde_json::json; #[fixture] async fn pool() -> PgPool { @@ -25,82 +20,201 @@ async fn closed_pool(#[future] pool: PgPool) -> PgPool { pool } -async fn setup_profile_and_table(pool: &PgPool) { - // Create default profile - sqlx::query!( - "INSERT INTO profiles (name) VALUES ('default') ON CONFLICT DO NOTHING" +async fn setup_test_environment(pool: &PgPool) -> (String, String, i64) { + let now = Utc::now(); + let profile_name = format!("test_profile_{}", now.timestamp_nanos_opt().unwrap()); + let table_name = format!("test_table_{}", now.timestamp_nanos_opt().unwrap()); + + // Start transaction + let mut tx = pool.begin().await.unwrap(); + + // Create profile + let profile_id = sqlx::query_scalar!( + "INSERT INTO profiles (name) VALUES ($1) RETURNING id", + profile_name ) - .execute(pool) + .fetch_one(&mut *tx) .await .unwrap(); // Create table definition - let columns_json = serde_json::json!([ - r#""id" BIGSERIAL PRIMARY KEY"#, - r#""firma" TEXT NOT NULL"#, - r#""deleted" BOOLEAN NOT NULL DEFAULT FALSE"#, - r#""created_at" TIMESTAMPTZ DEFAULT NOW()"# - ]); - sqlx::query!( - r#"INSERT INTO table_definitions (profile_id, table_name, columns) - SELECT id, '2025_adresar', $1 - FROM profiles WHERE name = 'default' - ON CONFLICT DO NOTHING"#, - columns_json + r#"INSERT INTO table_definitions (profile_id, table_name, columns, indexes) + VALUES ($1, $2, $3, $4)"#, + profile_id, + table_name, + json!({}), + json!([]) ) - .execute(pool) - .await - .unwrap(); -} - -async fn create_test_record(pool: &PgPool, firma: &str, deleted: bool) -> i64 { - sqlx::query_scalar!( - r#"INSERT INTO "2025_adresar" (firma, deleted) VALUES ($1, $2) RETURNING id"#, - firma, - deleted - ) - .fetch_one(pool) - .await - .unwrap() -} - -async fn cleanup_test_records(pool: &PgPool, prefix: &str) { - sqlx::query!( - r#"DELETE FROM "2025_adresar" WHERE firma LIKE $1"#, - format!("{}%", prefix) - ) - .execute(pool) - .await - .unwrap(); -} - -async fn find_position_of_record(pool: &PgPool, id: i64) -> Option { - let records = sqlx::query_scalar!( - r#"SELECT id FROM "2025_adresar" WHERE deleted = FALSE ORDER BY id ASC"# - ) - .fetch_all(pool) + .execute(&mut *tx) .await .unwrap(); - records.iter() - .position(|&record_id| record_id == id) - .map(|pos| (pos + 1) as i64) + // Create actual table + sqlx::query(&format!( + r#"CREATE TABLE "{}" ( + id BIGSERIAL PRIMARY KEY, + deleted BOOLEAN NOT NULL DEFAULT false, + firma TEXT NOT NULL + )"#, + table_name + )) + .execute(&mut *tx) + .await + .unwrap(); + + tx.commit().await.unwrap(); + (profile_name, table_name, profile_id) +} + +async fn cleanup_test_environment(pool: &PgPool, profile_id: i64, table_name: &str) { + let mut tx = pool.begin().await.unwrap(); + + // Cleanup order matters! + sqlx::query(&format!(r#"DROP TABLE IF EXISTS "{}" CASCADE"#, table_name)) + .execute(&mut *tx) + .await + .unwrap(); + + sqlx::query!("DELETE FROM table_definitions WHERE profile_id = $1", profile_id) + .execute(&mut *tx) + .await + .unwrap(); + + sqlx::query!("DELETE FROM profiles WHERE id = $1", profile_id) + .execute(&mut *tx) + .await + .unwrap(); + + tx.commit().await.unwrap(); } #[rstest] #[tokio::test] -async fn test_position_validation( +async fn test_retrieves_correct_record_by_position( #[future] pool: PgPool, ) { let pool = pool.await; - let _guard = TEST_MUTEX.lock().await; - setup_profile_and_table(&pool).await; + let (profile_name, table_name, profile_id) = setup_test_environment(&pool).await; + + // Insert test data + let mut tx = pool.begin().await.unwrap(); + let id1: i64 = sqlx::query_scalar(&format!( + r#"INSERT INTO "{}" (firma) VALUES ('Test 1') RETURNING id"#, + table_name + )) + .fetch_one(&mut *tx) + .await + .unwrap(); + + let id2: i64 = sqlx::query_scalar(&format!( + r#"INSERT INTO "{}" (firma) VALUES ('Test 2') RETURNING id"#, + table_name + )) + .fetch_one(&mut *tx) + .await + .unwrap(); + tx.commit().await.unwrap(); + + // Test position 1 + let request = GetTableDataByPositionRequest { + profile_name: profile_name.clone(), + table_name: table_name.clone(), + position: 1, + }; + let response = get_table_data_by_position(&pool, request).await.unwrap(); + assert_eq!(response.data["id"], id1.to_string()); + + // Test position 2 + let request = GetTableDataByPositionRequest { + profile_name: profile_name.clone(), + table_name: table_name.clone(), + position: 2, + }; + let response = get_table_data_by_position(&pool, request).await.unwrap(); + assert_eq!(response.data["id"], id2.to_string()); + + cleanup_test_environment(&pool, profile_id, &table_name).await; +} + +#[rstest] +#[tokio::test] +async fn test_excludes_deleted_records( + #[future] pool: PgPool, +) { + let pool = pool.await; + let (profile_name, table_name, profile_id) = setup_test_environment(&pool).await; + + // Insert test data + let mut tx = pool.begin().await.unwrap(); + let id1: i64 = sqlx::query_scalar(&format!( + r#"INSERT INTO "{}" (firma) VALUES ('Test 1') RETURNING id"#, + table_name + )) + .fetch_one(&mut *tx) + .await + .unwrap(); + + // Insert and delete a record + let deleted_id: i64 = sqlx::query_scalar(&format!( + r#"INSERT INTO "{}" (firma) VALUES ('Deleted') RETURNING id"#, + table_name + )) + .fetch_one(&mut *tx) + .await + .unwrap(); + + let id2: i64 = sqlx::query_scalar(&format!( + r#"INSERT INTO "{}" (firma) VALUES ('Test 2') RETURNING id"#, + table_name + )) + .fetch_one(&mut *tx) + .await + .unwrap(); + + sqlx::query(&format!( + r#"UPDATE "{}" SET deleted = true WHERE id = $1"#, + table_name + )) + .bind(deleted_id) + .execute(&mut *tx) + .await + .unwrap(); + + tx.commit().await.unwrap(); + + // Test positions + let request = GetTableDataByPositionRequest { + profile_name: profile_name.clone(), + table_name: table_name.clone(), + position: 1, + }; + let response = get_table_data_by_position(&pool, request).await.unwrap(); + assert_eq!(response.data["id"], id1.to_string()); + + let request = GetTableDataByPositionRequest { + profile_name: profile_name.clone(), + table_name: table_name.clone(), + position: 2, + }; + let response = get_table_data_by_position(&pool, request).await.unwrap(); + assert_eq!(response.data["id"], id2.to_string()); + + cleanup_test_environment(&pool, profile_id, &table_name).await; +} + +#[rstest] +#[tokio::test] +async fn test_invalid_position( + #[future] pool: PgPool, +) { + let pool = pool.await; + let (profile_name, table_name, profile_id) = setup_test_environment(&pool).await; // Test position 0 let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), + profile_name: profile_name.clone(), + table_name: table_name.clone(), position: 0, }; let result = get_table_data_by_position(&pool, request).await; @@ -109,174 +223,86 @@ async fn test_position_validation( // Test negative position let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), + profile_name: profile_name.clone(), + table_name: table_name.clone(), position: -1, }; let result = get_table_data_by_position(&pool, request).await; assert!(result.is_err()); assert_eq!(result.unwrap_err().code(), tonic::Code::InvalidArgument); + + cleanup_test_environment(&pool, profile_id, &table_name).await; } #[rstest] #[tokio::test] -async fn test_profile_table_validation( +async fn test_position_out_of_bounds( #[future] pool: PgPool, ) { let pool = pool.await; - let _guard = TEST_MUTEX.lock().await; - setup_profile_and_table(&pool).await; + let (profile_name, table_name, profile_id) = setup_test_environment(&pool).await; - // Test non-existent profile - let request = GetTableDataByPositionRequest { - profile_name: "ghost_profile".to_string(), - table_name: "2025_adresar".to_string(), - position: 1, - }; - let result = get_table_data_by_position(&pool, request).await; - assert!(result.is_err()); - assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); - - // Test non-existent table - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "ghost_table".to_string(), - position: 1, - }; - let result = get_table_data_by_position(&pool, request).await; - assert!(result.is_err()); - assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); -} - -#[rstest] -#[tokio::test] -async fn test_basic_position_retrieval( - #[future] pool: PgPool, -) { - let pool = pool.await; - let _guard = TEST_MUTEX.lock().await; - setup_profile_and_table(&pool).await; - cleanup_test_records(&pool, "PosBasicTest").await; - - // Create test records - let id1 = create_test_record(&pool, "PosBasicTest_1", false).await; - let id2 = create_test_record(&pool, "PosBasicTest_2", false).await; - let id3 = create_test_record(&pool, "PosBasicTest_3", false).await; - - // Verify positions - let pos1 = find_position_of_record(&pool, id1).await.unwrap(); - let pos2 = find_position_of_record(&pool, id2).await.unwrap(); - let pos3 = find_position_of_record(&pool, id3).await.unwrap(); - - // Test retrieval - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: pos1 as i32, - }; - let response = get_table_data_by_position(&pool, request).await.unwrap(); - assert_eq!(response.data["id"], id1.to_string()); - - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: pos2 as i32, - }; - let response = get_table_data_by_position(&pool, request).await.unwrap(); - assert_eq!(response.data["id"], id2.to_string()); - - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: pos3 as i32, - }; - let response = get_table_data_by_position(&pool, request).await.unwrap(); - assert_eq!(response.data["id"], id3.to_string()); - - cleanup_test_records(&pool, "PosBasicTest").await; -} - -#[rstest] -#[tokio::test] -async fn test_deleted_records_excluded( - #[future] pool: PgPool, -) { - let pool = pool.await; - let _guard = TEST_MUTEX.lock().await; - setup_profile_and_table(&pool).await; - cleanup_test_records(&pool, "PosDeletedTest").await; - - // Create records - let id1 = create_test_record(&pool, "PosDeletedTest_1", false).await; - let _deleted_id = create_test_record(&pool, "PosDeletedTest_deleted", true).await; - let id2 = create_test_record(&pool, "PosDeletedTest_2", false).await; - - // Verify positions - let pos1 = find_position_of_record(&pool, id1).await.unwrap(); - let pos2 = find_position_of_record(&pool, id2).await.unwrap(); - - assert_eq!(pos2, pos1 + 1); - - // Test retrieval - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: pos1 as i32, - }; - let response = get_table_data_by_position(&pool, request).await.unwrap(); - assert_eq!(response.data["id"], id1.to_string()); - - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: pos2 as i32, - }; - let response = get_table_data_by_position(&pool, request).await.unwrap(); - assert_eq!(response.data["id"], id2.to_string()); - - cleanup_test_records(&pool, "PosDeletedTest").await; -} - -#[rstest] -#[tokio::test] -async fn test_position_after_deletion( - #[future] pool: PgPool, -) { - let pool = pool.await; - let _guard = TEST_MUTEX.lock().await; - setup_profile_and_table(&pool).await; - cleanup_test_records(&pool, "PosDeletionTest").await; - - // Create records - let id1 = create_test_record(&pool, "PosDeletionTest_1", false).await; - let id2 = create_test_record(&pool, "PosDeletionTest_2", false).await; - let id3 = create_test_record(&pool, "PosDeletionTest_3", false).await; - - // Delete middle record - sqlx::query!( - r#"UPDATE "2025_adresar" SET deleted = TRUE WHERE id = $1"#, - id2 - ) - .execute(&pool) + // Insert one record + let mut tx = pool.begin().await.unwrap(); + sqlx::query(&format!( + r#"INSERT INTO "{}" (firma) VALUES ('Test 1')"#, + table_name + )) + .execute(&mut *tx) .await .unwrap(); + tx.commit().await.unwrap(); - // Verify new positions - let pos1 = find_position_of_record(&pool, id1).await.unwrap(); - let pos3 = find_position_of_record(&pool, id3).await.unwrap(); - - assert_eq!(pos3, pos1 + 1); - - // Test retrieval + // Test position beyond count let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: pos3 as i32, + profile_name: profile_name.clone(), + table_name: table_name.clone(), + position: 2, }; - let response = get_table_data_by_position(&pool, request).await.unwrap(); - assert_eq!(response.data["id"], id3.to_string()); + let result = get_table_data_by_position(&pool, request).await; + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); - cleanup_test_records(&pool, "PosDeletionTest").await; + cleanup_test_environment(&pool, profile_id, &table_name).await; +} + +#[rstest] +#[tokio::test] +async fn test_table_not_in_profile( + #[future] pool: PgPool, +) { + let pool = pool.await; + let (profile_name, _, profile_id) = setup_test_environment(&pool).await; + + // Test with non-existent table + let request = GetTableDataByPositionRequest { + profile_name, + table_name: "non_existent_table".to_string(), + position: 1, + }; + let result = get_table_data_by_position(&pool, request).await; + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); + + cleanup_test_environment(&pool, profile_id, "dummy_table").await; +} + +#[rstest] +#[tokio::test] +async fn test_profile_not_found( + #[future] pool: PgPool, +) { + let pool = pool.await; + + let request = GetTableDataByPositionRequest { + profile_name: "nonexistent_profile".to_string(), + table_name: "adresar".to_string(), + position: 1, + }; + let result = get_table_data_by_position(&pool, request).await; + + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); } #[rstest] @@ -284,33 +310,14 @@ async fn test_position_after_deletion( async fn test_database_error( #[future] closed_pool: PgPool, ) { - let pool = closed_pool.await; + let closed_pool = closed_pool.await; let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), + profile_name: "test".to_string(), + table_name: "test".to_string(), position: 1, }; - let result = get_table_data_by_position(&pool, request).await; + let result = get_table_data_by_position(&closed_pool, request).await; + assert!(result.is_err()); assert_eq!(result.unwrap_err().code(), tonic::Code::Internal); } - -#[rstest] -#[tokio::test] -async fn test_empty_table( - #[future] pool: PgPool, -) { - let pool = pool.await; - let _guard = TEST_MUTEX.lock().await; - setup_profile_and_table(&pool).await; - cleanup_test_records(&pool, "").await; - - let request = GetTableDataByPositionRequest { - profile_name: "default".to_string(), - table_name: "2025_adresar".to_string(), - position: 1, - }; - let result = get_table_data_by_position(&pool, request).await; - assert!(result.is_err()); - assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); -}