get is failing

This commit is contained in:
filipriec
2025-03-04 21:19:50 +01:00
parent 3a62f8ff81
commit 967915d734

View File

@@ -8,12 +8,6 @@ use tonic;
use chrono::Utc; use chrono::Utc;
use serde_json::json; use serde_json::json;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
lazy_static::lazy_static! {
static ref TEST_MUTEX: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}
#[fixture] #[fixture]
async fn pool() -> PgPool { async fn pool() -> PgPool {
@@ -28,10 +22,10 @@ async fn closed_pool(#[future] pool: PgPool) -> PgPool {
} }
#[fixture] #[fixture]
async fn existing_profile(#[future] pool: PgPool) -> (PgPool, String, i64) { async fn profile(#[future] pool: PgPool) -> (PgPool, String, i64) {
let _guard = TEST_MUTEX.lock().await;
let pool = pool.await; let pool = pool.await;
let profile_name = format!("TestProfile_{}", Utc::now().timestamp_nanos_opt().unwrap_or_default()); let profile_name = format!("TestProfile_{}", Utc::now().timestamp_nanos_opt().unwrap_or_default());
let profile = sqlx::query!( let profile = sqlx::query!(
"INSERT INTO profiles (name) VALUES ($1) RETURNING id", "INSERT INTO profiles (name) VALUES ($1) RETURNING id",
profile_name profile_name
@@ -39,15 +33,13 @@ async fn existing_profile(#[future] pool: PgPool) -> (PgPool, String, i64) {
.fetch_one(&pool) .fetch_one(&pool)
.await .await
.unwrap(); .unwrap();
(pool, profile_name, profile.id) (pool, profile_name, profile.id)
} }
#[fixture] #[fixture]
async fn existing_table( async fn table_definition(#[future] profile: (PgPool, String, i64)) -> (PgPool, String, String, i64) {
#[future] existing_profile: (PgPool, String, i64), let (pool, profile_name, profile_id) = profile.await;
) -> (PgPool, String, String) {
let _guard = TEST_MUTEX.lock().await;
let (pool, profile_name, profile_id) = existing_profile.await;
let table_name = format!("test_table_{}", Utc::now().timestamp_nanos_opt().unwrap_or_default()); let table_name = format!("test_table_{}", Utc::now().timestamp_nanos_opt().unwrap_or_default());
// Define columns for the table // Define columns for the table
@@ -58,13 +50,13 @@ async fn existing_table(
"\"is_active\" BOOLEAN" "\"is_active\" BOOLEAN"
]); ]);
sqlx::query!( let table_def = sqlx::query!(
"INSERT INTO table_definitions (profile_id, table_name, columns) VALUES ($1, $2, $3)", "INSERT INTO table_definitions (profile_id, table_name, columns) VALUES ($1, $2, $3) RETURNING id",
profile_id, profile_id,
table_name, table_name,
columns columns
) )
.execute(&pool) .fetch_one(&pool)
.await .await
.unwrap(); .unwrap();
@@ -89,16 +81,14 @@ async fn existing_table(
.await .await
.unwrap(); .unwrap();
(pool, profile_name, table_name) (pool, profile_name, table_name, table_def.id)
} }
#[fixture] #[fixture]
async fn existing_record( async fn regular_record(#[future] table_definition: (PgPool, String, String, i64)) -> (PgPool, String, String, i64) {
#[future] existing_table: (PgPool, String, String), let (pool, profile_name, table_name, _) = table_definition.await;
) -> (PgPool, String, String, i64) {
let _guard = TEST_MUTEX.lock().await; // Insert a record with all fields
let (pool, profile_name, table_name) = existing_table.await;
let query = format!( let query = format!(
r#"INSERT INTO "{}" (firma, name, age, email, is_active) r#"INSERT INTO "{}" (firma, name, age, email, is_active)
VALUES ($1, $2, $3, $4, $5) VALUES ($1, $2, $3, $4, $5)
@@ -121,36 +111,10 @@ async fn existing_record(
} }
#[fixture] #[fixture]
async fn existing_deleted_record( async fn null_fields_record(#[future] table_definition: (PgPool, String, String, i64)) -> (PgPool, String, String, i64) {
#[future] existing_table: (PgPool, String, String), let (pool, profile_name, table_name, _) = table_definition.await;
) -> (PgPool, String, String, i64) {
let _guard = TEST_MUTEX.lock().await;
let (pool, profile_name, table_name) = existing_table.await;
let query = format!(
r#"INSERT INTO "{}" (firma, deleted)
VALUES ($1, true)
RETURNING id"#,
table_name
);
let record = sqlx::query(&query) // Insert a record with only required fields
.bind("Deleted Company")
.fetch_one(&pool)
.await
.unwrap();
let id: i64 = record.get("id");
(pool, profile_name, table_name, id)
}
#[fixture]
async fn existing_record_with_nulls(
#[future] existing_table: (PgPool, String, String),
) -> (PgPool, String, String, i64) {
let _guard = TEST_MUTEX.lock().await;
let (pool, profile_name, table_name) = existing_table.await;
let query = format!( let query = format!(
r#"INSERT INTO "{}" (firma) r#"INSERT INTO "{}" (firma)
VALUES ($1) VALUES ($1)
@@ -168,30 +132,81 @@ async fn existing_record_with_nulls(
(pool, profile_name, table_name, id) (pool, profile_name, table_name, id)
} }
async fn cleanup_test_data(pool: &PgPool, table_name: &str) { #[fixture]
sqlx::query(&format!(r#"DROP TABLE IF EXISTS "{}" CASCADE"#, table_name)) async fn deleted_record(#[future] table_definition: (PgPool, String, String, i64)) -> (PgPool, String, String, i64) {
.execute(pool) let (pool, profile_name, table_name, _) = table_definition.await;
// Insert a deleted record
let query = format!(
r#"INSERT INTO "{}" (firma, deleted)
VALUES ($1, true)
RETURNING id"#,
table_name
);
let record = sqlx::query(&query)
.bind("Deleted Company")
.fetch_one(&pool)
.await .await
.unwrap(); .unwrap();
sqlx::query!("DELETE FROM table_definitions WHERE table_name = $1", table_name) let id: i64 = record.get("id");
.execute(pool) (pool, profile_name, table_name, id)
}
async fn assert_response_matches(pool: &PgPool, table_name: &str, id: i64, response: &HashMap<String, String>) {
let columns = format!(
"id, deleted, firma, name, age, email, is_active"
);
let query = format!(r#"SELECT {} FROM "{}" WHERE id = $1"#, columns, table_name);
let row = sqlx::query(&query)
.bind(id)
.fetch_one(pool)
.await .await
.unwrap(); .unwrap();
assert_eq!(row.get::<i64, _>("id").to_string(), response["id"]);
assert_eq!(row.get::<bool, _>("deleted").to_string(), response["deleted"]);
assert_eq!(row.get::<String, _>("firma"), response["firma"]);
// Check optional fields
let name: Option<String> = row.try_get("name").unwrap_or(None);
assert_eq!(name.unwrap_or_default(), response["name"]);
let age: Option<i32> = row.try_get("age").unwrap_or(None);
assert_eq!(age.map(|v| v.to_string()).unwrap_or_default(), response["age"]);
let email: Option<String> = row.try_get("email").unwrap_or(None);
assert_eq!(email.unwrap_or_default(), response["email"]);
let is_active: Option<bool> = row.try_get("is_active").unwrap_or(None);
assert_eq!(is_active.map(|v| v.to_string()).unwrap_or_default(), response["is_active"]);
}
async fn cleanup_test_data(pool: &PgPool, table_name: &str) {
let _ = sqlx::query(&format!(r#"DROP TABLE IF EXISTS "{}" CASCADE"#, table_name))
.execute(pool)
.await;
let _ = sqlx::query!("DELETE FROM table_definitions WHERE table_name = $1", table_name)
.execute(pool)
.await;
} }
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_table_data_success( async fn test_get_table_data_success(
#[future] existing_record: (PgPool, String, String, i64), #[future] regular_record: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name, id) = existing_record.await; let (pool, profile_name, table_name, id) = regular_record.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name: profile_name.clone(), profile_name: profile_name.clone(),
table_name: table_name.clone(), table_name: table_name.clone(),
id, id,
}; };
let response = get_table_data(&pool, request).await.unwrap(); let response = get_table_data(&pool, request).await.unwrap();
assert_eq!(response.data["id"], id.to_string()); assert_eq!(response.data["id"], id.to_string());
@@ -201,16 +216,19 @@ async fn test_get_table_data_success(
assert_eq!(response.data["email"], "john@example.com"); assert_eq!(response.data["email"], "john@example.com");
assert_eq!(response.data["is_active"], "true"); assert_eq!(response.data["is_active"], "true");
assert_eq!(response.data["deleted"], "false"); assert_eq!(response.data["deleted"], "false");
assert_response_matches(&pool, &table_name, id, &response.data).await;
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_optional_fields_null( async fn test_get_optional_fields_null(
#[future] existing_record_with_nulls: (PgPool, String, String, i64), #[future] null_fields_record: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name, id) = existing_record_with_nulls.await; let (pool, profile_name, table_name, id) = null_fields_record.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name: profile_name.clone(), profile_name: profile_name.clone(),
table_name: table_name.clone(), table_name: table_name.clone(),
@@ -224,16 +242,19 @@ async fn test_get_optional_fields_null(
assert_eq!(response.data["email"], ""); assert_eq!(response.data["email"], "");
assert_eq!(response.data["is_active"], ""); assert_eq!(response.data["is_active"], "");
assert_eq!(response.data["deleted"], "false"); assert_eq!(response.data["deleted"], "false");
assert_response_matches(&pool, &table_name, id, &response.data).await;
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_nonexistent_id( async fn test_get_nonexistent_id(
#[future] existing_table: (PgPool, String, String), #[future] table_definition: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name) = existing_table.await; let (pool, profile_name, table_name, _) = table_definition.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name: profile_name.clone(), profile_name: profile_name.clone(),
table_name: table_name.clone(), table_name: table_name.clone(),
@@ -241,18 +262,20 @@ async fn test_get_nonexistent_id(
}; };
let result = get_table_data(&pool, request).await; let result = get_table_data(&pool, request).await;
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_deleted_record( async fn test_get_deleted_record(
#[future] existing_deleted_record: (PgPool, String, String, i64), #[future] deleted_record: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name, id) = existing_deleted_record.await; let (pool, profile_name, table_name, id) = deleted_record.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name: profile_name.clone(), profile_name: profile_name.clone(),
table_name: table_name.clone(), table_name: table_name.clone(),
@@ -260,9 +283,10 @@ async fn test_get_deleted_record(
}; };
let result = get_table_data(&pool, request).await; let result = get_table_data(&pool, request).await;
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }
@@ -272,6 +296,7 @@ async fn test_get_database_error(
#[future] closed_pool: PgPool, #[future] closed_pool: PgPool,
) { ) {
let closed_pool = closed_pool.await; let closed_pool = closed_pool.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name: "test".into(), profile_name: "test".into(),
table_name: "test".into(), table_name: "test".into(),
@@ -279,6 +304,7 @@ async fn test_get_database_error(
}; };
let result = get_table_data(&closed_pool, request).await; let result = get_table_data(&closed_pool, request).await;
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().code(), tonic::Code::Internal); assert_eq!(result.unwrap_err().code(), tonic::Code::Internal);
} }
@@ -286,10 +312,10 @@ async fn test_get_database_error(
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_special_characters( async fn test_get_special_characters(
#[future] existing_table: (PgPool, String, String), #[future] table_definition: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name) = existing_table.await; let (pool, profile_name, table_name, _) = table_definition.await;
let query = format!( let query = format!(
r#"INSERT INTO "{}" (firma, name, email) r#"INSERT INTO "{}" (firma, name, email)
VALUES ($1, $2, $3) VALUES ($1, $2, $3)
@@ -317,17 +343,19 @@ async fn test_get_special_characters(
assert_eq!(response.data["name"], "Náměstí ČR"); assert_eq!(response.data["name"], "Náměstí ČR");
assert_eq!(response.data["email"], "čšěř@example.com"); assert_eq!(response.data["email"], "čšěř@example.com");
assert_response_matches(&pool, &table_name, id, &response.data).await;
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_max_length_fields( async fn test_get_max_length_fields(
#[future] existing_table: (PgPool, String, String), #[future] table_definition: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name) = existing_table.await; let (pool, profile_name, table_name, _) = table_definition.await;
let long_name = "a".repeat(255); let long_name = "a".repeat(255);
let query = format!( let query = format!(
r#"INSERT INTO "{}" (firma, name) r#"INSERT INTO "{}" (firma, name)
@@ -355,7 +383,9 @@ async fn test_get_max_length_fields(
assert_eq!(response.data["name"], long_name); assert_eq!(response.data["name"], long_name);
assert_eq!(response.data["name"].len(), 255); assert_eq!(response.data["name"].len(), 255);
assert_response_matches(&pool, &table_name, id, &response.data).await;
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }
@@ -365,6 +395,7 @@ async fn test_get_invalid_profile(
#[future] pool: PgPool, #[future] pool: PgPool,
) { ) {
let pool = pool.await; let pool = pool.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name: "non_existent_profile".into(), profile_name: "non_existent_profile".into(),
table_name: "test_table".into(), table_name: "test_table".into(),
@@ -372,6 +403,7 @@ async fn test_get_invalid_profile(
}; };
let result = get_table_data(&pool, request).await; let result = get_table_data(&pool, request).await;
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
} }
@@ -379,9 +411,10 @@ async fn test_get_invalid_profile(
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_invalid_table( async fn test_get_invalid_table(
#[future] existing_profile: (PgPool, String, i64), #[future] profile: (PgPool, String, i64),
) { ) {
let (pool, profile_name, _) = existing_profile.await; let (pool, profile_name, _) = profile.await;
let request = GetTableDataRequest { let request = GetTableDataRequest {
profile_name, profile_name,
table_name: "non_existent_table".into(), table_name: "non_existent_table".into(),
@@ -389,6 +422,7 @@ async fn test_get_invalid_table(
}; };
let result = get_table_data(&pool, request).await; let result = get_table_data(&pool, request).await;
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound); assert_eq!(result.unwrap_err().code(), tonic::Code::NotFound);
} }
@@ -396,19 +430,19 @@ async fn test_get_invalid_table(
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
async fn test_get_invalid_column( async fn test_get_invalid_column(
#[future] existing_record: (PgPool, String, String, i64), #[future] regular_record: (PgPool, String, String, i64),
) { ) {
let (pool, profile_name, table_name, id) = existing_record.await; let (pool, profile_name, table_name, id) = regular_record.await;
// Try to request a column that doesn't exist in the table definition let request = GetTableDataRequest {
let mut request = GetTableDataRequest {
profile_name: profile_name.clone(), profile_name: profile_name.clone(),
table_name: table_name.clone(), table_name: table_name.clone(),
id, id,
}; };
let result = get_table_data(&pool, request).await; let result = get_table_data(&pool, request).await;
assert!(result.is_ok()); // Should still succeed as we're not filtering columns assert!(result.is_ok()); // Should still succeed as we're not filtering columns
cleanup_test_data(&pool, &table_name).await; cleanup_test_data(&pool, &table_name).await;
} }