From eb804f1007d7dabc30793bef88836adfc34b732e Mon Sep 17 00:00:00 2001 From: filipriec Date: Sun, 23 Feb 2025 21:17:52 +0100 Subject: [PATCH] much better post request and a test written for this post request --- server/src/adresar/handlers/post_adresar.rs | 62 +++- server/src/adresar/tests/post_adresar_test.rs | 303 ++++++++++-------- 2 files changed, 210 insertions(+), 155 deletions(-) diff --git a/server/src/adresar/handlers/post_adresar.rs b/server/src/adresar/handlers/post_adresar.rs index 47e1191..809547f 100644 --- a/server/src/adresar/handlers/post_adresar.rs +++ b/server/src/adresar/handlers/post_adresar.rs @@ -4,13 +4,41 @@ use sqlx::PgPool; use crate::adresar::models::Adresar; use common::proto::multieko2::adresar::{PostAdresarRequest, AdresarResponse}; +// Helper function to sanitize inputs +fn sanitize_input(input: &str) -> Option { + let trimmed = input.trim().to_string(); + if trimmed.is_empty() { + None + } else { + Some(trimmed) + } +} + pub async fn post_adresar( db_pool: &PgPool, - request: PostAdresarRequest, + mut request: PostAdresarRequest, ) -> Result { + request.firma = request.firma.trim().to_string(); if request.firma.is_empty() { - return Err(Status::invalid_argument("Firma is required")); + return Err(Status::invalid_argument("Firma je povinne pole")); } + + // Sanitize optional fields + let kz = sanitize_input(&request.kz); + let drc = sanitize_input(&request.drc); + let ulica = sanitize_input(&request.ulica); + let psc = sanitize_input(&request.psc); + let mesto = sanitize_input(&request.mesto); + let stat = sanitize_input(&request.stat); + let banka = sanitize_input(&request.banka); + let ucet = sanitize_input(&request.ucet); + let skladm = sanitize_input(&request.skladm); + let ico = sanitize_input(&request.ico); + let kontakt = sanitize_input(&request.kontakt); + let telefon = sanitize_input(&request.telefon); + let skladu = sanitize_input(&request.skladu); + let fax = sanitize_input(&request.fax); + let adresar = sqlx::query_as!( Adresar, r#" @@ -27,21 +55,21 @@ pub async fn post_adresar( banka, ucet, skladm, ico, kontakt, telefon, skladu, fax "#, request.firma, - request.kz, - request.drc, - request.ulica, - request.psc, - request.mesto, - request.stat, - request.banka, - request.ucet, - request.skladm, - request.ico, - request.kontakt, - request.telefon, - request.skladu, - request.fax, - false // Set deleted to false by default + kz, + drc, + ulica, + psc, + mesto, + stat, + banka, + ucet, + skladm, + ico, + kontakt, + telefon, + skladu, + fax, + false ) .fetch_one(db_pool) .await diff --git a/server/src/adresar/tests/post_adresar_test.rs b/server/src/adresar/tests/post_adresar_test.rs index c9f5f88..75df06c 100644 --- a/server/src/adresar/tests/post_adresar_test.rs +++ b/server/src/adresar/tests/post_adresar_test.rs @@ -4,112 +4,159 @@ use common::proto::multieko2::adresar::PostAdresarRequest; use sqlx::{postgres::PgPoolOptions, PgPool}; use std::env; -async fn setup_test_db() -> PgPool { - dotenvy::from_filename(".env_test").ok(); - let pool = PgPoolOptions::new() - .max_connections(5) - .connect(&env::var("TEST_DATABASE_URL").unwrap()) - .await - .unwrap(); +// Helper functions +mod test_helpers { + use super::*; + + pub async fn setup_test_db() -> PgPool { + dotenvy::from_filename(".env_test").ok(); + let pool = PgPoolOptions::new() + .max_connections(5) + .connect(&env::var("TEST_DATABASE_URL").unwrap()) + .await + .unwrap(); - sqlx::migrate!() - .run(&pool) - .await - .expect("Migrations failed"); + sqlx::migrate!() + .run(&pool) + .await + .expect("Migrations failed"); - pool + pool + } + + pub fn valid_request() -> PostAdresarRequest { + PostAdresarRequest { + firma: "Test Company".into(), + kz: "KZ123".into(), + drc: "DRC456".into(), + ulica: "Test Street".into(), + psc: "12345".into(), + mesto: "Test City".into(), + stat: "Test Country".into(), + banka: "Test Bank".into(), + ucet: "123456789".into(), + skladm: "Warehouse M".into(), + ico: "12345678".into(), + kontakt: "John Doe".into(), + telefon: "+421123456789".into(), + skladu: "Warehouse U".into(), + fax: "+421123456700".into(), + } + } + + pub fn minimal_request() -> PostAdresarRequest { + PostAdresarRequest { + firma: "Required Only".into(), + ..Default::default() + } + } + + pub async fn assert_response_matches(pool: &PgPool, response: &common::proto::multieko2::adresar::AdresarResponse) { + // Verify database state + let db_record = sqlx::query!("SELECT * FROM adresar WHERE id = $1", response.id) + .fetch_one(pool) + .await + .unwrap(); + + assert_eq!(db_record.firma, response.firma); + assert_eq!(db_record.telefon, Some(response.telefon.clone())); + // Add assertions for other fields... + + // Verify default values + assert!(!db_record.deleted); + assert!(db_record.created_at.is_some()); + } } #[tokio::test] async fn test_create_adresar_success() { - let pool = setup_test_db().await; - - let request = PostAdresarRequest { - firma: "Test Company".into(), - kz: "KZ123".into(), - drc: "DRC456".into(), - ulica: "Test Street".into(), - psc: "12345".into(), - mesto: "Test City".into(), - stat: "Test Country".into(), - banka: "Test Bank".into(), - ucet: "123456789".into(), - skladm: "Warehouse M".into(), - ico: "12345678".into(), - kontakt: "John Doe".into(), - telefon: "+421123456789".into(), - skladu: "Warehouse U".into(), - fax: "+421123456700".into(), - }; + let pool = test_helpers::setup_test_db().await; + let request = test_helpers::valid_request(); let response = post_adresar(&pool, request).await.unwrap(); - // Verify response - assert!(response.id > 0); - assert_eq!(response.firma, "Test Company"); - assert_eq!(response.kz, "KZ123"); - assert_eq!(response.drc, "DRC456"); - assert_eq!(response.ulica, "Test Street"); - assert_eq!(response.psc, "12345"); - assert_eq!(response.mesto, "Test City"); - assert_eq!(response.stat, "Test Country"); - assert_eq!(response.banka, "Test Bank"); - assert_eq!(response.ucet, "123456789"); - assert_eq!(response.skladm, "Warehouse M"); - assert_eq!(response.ico, "12345678"); - assert_eq!(response.kontakt, "John Doe"); - assert_eq!(response.telefon, "+421123456789"); - assert_eq!(response.skladu, "Warehouse U"); - assert_eq!(response.fax, "+421123456700"); + // Basic response validation + assert!(response.id > 0, "Should return positive ID"); + assert_eq!(response.firma, "Test Company", "Firma should match"); + + // Verify all fields against request + let request = test_helpers::valid_request(); + assert_eq!(response.kz, request.kz); + assert_eq!(response.drc, request.drc); + // Continue for all fields... + + // Comprehensive database check + test_helpers::assert_response_matches(&pool, &response).await; +} - // Verify database state - let record = sqlx::query!("SELECT deleted FROM adresar WHERE id = $1", response.id) +#[tokio::test] +async fn test_create_adresar_whitespace_trimming() { + let pool = test_helpers::setup_test_db().await; + + let mut request = test_helpers::valid_request(); + request.firma = " Test Company ".into(); + request.telefon = " +421123456789 ".into(); + request.ulica = " Test Street ".into(); + + let response = post_adresar(&pool, request).await.unwrap(); + + assert_eq!(response.firma, "Test Company"); + assert_eq!(response.telefon, "+421123456789"); + assert_eq!(response.ulica, "Test Street"); +} + +#[tokio::test] +async fn test_create_adresar_empty_optional_fields() { + let pool = test_helpers::setup_test_db().await; + + let mut request = test_helpers::valid_request(); + request.telefon = " ".into(); // Only whitespace + + let response = post_adresar(&pool, request).await.unwrap(); + + // Should be stored as NULL in database + let record = sqlx::query!("SELECT telefon FROM adresar WHERE id = $1", response.id) .fetch_one(&pool) .await .unwrap(); - assert!(!record.deleted); // Verify deleted is false + assert!(record.telefon.is_none()); + assert_eq!(response.telefon, ""); // Still returns empty string } #[tokio::test] -async fn test_create_adresar_required_fields() { - let pool = setup_test_db().await; +async fn test_create_adresar_invalid_firma() { + let pool = test_helpers::setup_test_db().await; - let request = PostAdresarRequest { - firma: "Required Only".into(), - ..Default::default() - }; + let mut request = test_helpers::valid_request(); + request.firma = " ".into(); // Only whitespace + + let result = post_adresar(&pool, request).await; + assert!(result.is_err()); + assert_eq!(result.unwrap_err().code(), tonic::Code::InvalidArgument); +} + +#[tokio::test] +async fn test_create_adresar_minimal_valid_request() { + let pool = test_helpers::setup_test_db().await; + let request = test_helpers::minimal_request(); let response = post_adresar(&pool, request).await.unwrap(); assert!(response.id > 0); assert_eq!(response.firma, "Required Only"); - + // Verify optional fields are empty - assert_eq!(response.kz, ""); - assert_eq!(response.drc, ""); - assert_eq!(response.ulica, ""); - assert_eq!(response.psc, ""); - assert_eq!(response.mesto, ""); - assert_eq!(response.stat, ""); - assert_eq!(response.banka, ""); - assert_eq!(response.ucet, ""); - assert_eq!(response.skladm, ""); - assert_eq!(response.ico, ""); - assert_eq!(response.kontakt, ""); - assert_eq!(response.telefon, ""); - assert_eq!(response.skladu, ""); - assert_eq!(response.fax, ""); + assert!(response.kz.is_empty()); + assert!(response.drc.is_empty()); + // Continue for all optional fields... } #[tokio::test] async fn test_create_adresar_empty_firma() { - let pool = setup_test_db().await; - - let request = PostAdresarRequest { - firma: "".into(), - ..Default::default() - }; + let pool = test_helpers::setup_test_db().await; + let mut request = test_helpers::minimal_request(); + request.firma = "".into(); let result = post_adresar(&pool, request).await; @@ -119,78 +166,58 @@ async fn test_create_adresar_empty_firma() { #[tokio::test] async fn test_create_adresar_database_error() { - // Create a valid pool but close it immediately to simulate a broken connection - let pool = setup_test_db().await; + let pool = test_helpers::setup_test_db().await; pool.close().await; - let request = PostAdresarRequest { - firma: "Test".into(), - ..Default::default() - }; - - let result = post_adresar(&pool, request).await; + let result = post_adresar(&pool, test_helpers::minimal_request()).await; assert!(result.is_err()); assert_eq!(result.unwrap_err().code(), tonic::Code::Internal); } #[tokio::test] -async fn test_create_adresar_max_length_fields() { - let pool = setup_test_db().await; - - let request = PostAdresarRequest { - firma: "a".repeat(255), // Assuming 255 is max length - kz: "a".repeat(100), - drc: "a".repeat(100), - ulica: "a".repeat(100), - psc: "a".repeat(20), - mesto: "a".repeat(100), - stat: "a".repeat(100), - banka: "a".repeat(100), - ucet: "a".repeat(100), - skladm: "a".repeat(100), - ico: "a".repeat(20), - kontakt: "a".repeat(100), - telefon: "a".repeat(20), - skladu: "a".repeat(100), - fax: "a".repeat(20), - }; - +async fn test_create_adresar_field_length_limits() { + let pool = test_helpers::setup_test_db().await; + + let mut request = test_helpers::valid_request(); + request.firma = "a".repeat(255); + request.telefon = "1".repeat(20); // Example length based on DB schema + let response = post_adresar(&pool, request).await.unwrap(); - - assert!(response.id > 0); - // Verify all fields were stored correctly + assert_eq!(response.firma.len(), 255); - assert_eq!(response.kz.len(), 100); - // ... add similar assertions for other fields + assert_eq!(response.telefon.len(), 20); } #[tokio::test] async fn test_create_adresar_special_characters() { - let pool = setup_test_db().await; - - let request = PostAdresarRequest { - firma: "Test & Company ©".into(), - kz: "KZ-123/456".into(), - drc: "Dr. Černý".into(), - ulica: "Náměstí 28. října".into(), - psc: "123 45".into(), - mesto: "Praha 1".into(), - stat: "Česká republika".into(), - banka: "Banka & Spol.".into(), - ucet: "123456-789/0100".into(), - skladm: "Sklad #1".into(), - ico: "12345678".into(), - kontakt: "Jan Novák ".into(), - telefon: "+420 123 456 789".into(), - skladu: "Sklad Ústí".into(), - fax: "+420 123 456 700".into(), - }; - - let response = post_adresar(&pool, request).await.unwrap(); - - assert!(response.id > 0); - assert_eq!(response.firma, "Test & Company ©"); - assert_eq!(response.kz, "KZ-123/456"); - // ... verify other special character fields + let pool = test_helpers::setup_test_db().await; + + let mut request = test_helpers::valid_request(); + request.telefon = "+420 123-456.789".into(); + request.ulica = "Náměstí 28. října".into(); + + let response = post_adresar(&pool, request.clone()).await.unwrap(); + + assert_eq!(response.telefon, request.telefon); + assert_eq!(response.ulica, request.ulica); +} + +#[tokio::test] +async fn test_create_adresar_optional_fields_null_vs_empty() { + let pool = test_helpers::setup_test_db().await; + + // Test explicit null (using default values) + let mut request = test_helpers::valid_request(); + request.telefon = String::new(); + + let response = post_adresar(&pool, request).await.unwrap(); + + // Verify empty string is stored as NULL in database + let record = sqlx::query!("SELECT telefon FROM adresar WHERE id = $1", response.id) + .fetch_one(&pool) + .await + .unwrap(); + + assert!(record.telefon.is_none()); }