From cb84d40a484b4c80a7b7efb30e93407161e86d71 Mon Sep 17 00:00:00 2001 From: Mika Bomm Date: Thu, 19 Jun 2025 13:18:33 +0200 Subject: [PATCH] refactor: enhance user tests with dynamic user data generation and improved assertions --- crates/backend/tests/common/test_helpers.rs | 49 ++++++++ crates/backend/tests/endpoints/user.rs | 127 ++++++++++++++++---- 2 files changed, 151 insertions(+), 25 deletions(-) diff --git a/crates/backend/tests/common/test_helpers.rs b/crates/backend/tests/common/test_helpers.rs index 4b44d43..33fa894 100644 --- a/crates/backend/tests/common/test_helpers.rs +++ b/crates/backend/tests/common/test_helpers.rs @@ -1,5 +1,6 @@ use backend::Database; use lazy_static::lazy_static; +use std::sync::atomic::{AtomicU64, Ordering}; use testcontainers::ContainerAsync; use testcontainers_modules::{postgres::Postgres, redis::Redis}; @@ -30,6 +31,54 @@ pub async fn get_database() -> &'static Database { &state.database } +static TEST_COUNTER: AtomicU64 = AtomicU64::new(1); + +pub fn get_unique_test_id() -> String { + let counter = TEST_COUNTER.fetch_add(1, Ordering::SeqCst); + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis(); + format!("test_{}_{}", timestamp, counter) +} + +pub struct UserFactory; + +impl UserFactory { + pub fn create_request(username: Option, name: Option) -> serde_json::Value { + let test_id = get_unique_test_id(); + serde_json::json!({ + "username": username.unwrap_or_else(|| format!("user_{}", test_id)), + "name": name.unwrap_or_else(|| format!("Test User {}", test_id)), + "password": "password123" + }) + } + + pub fn create_unique_request() -> serde_json::Value { + Self::create_request(None, None) + } +} + +pub struct TestContext { + pub test_id: String, +} + +impl TestContext { + pub fn new() -> Self { + Self { + test_id: get_unique_test_id(), + } + } + + pub fn create_user_data(&self, username_prefix: Option<&str>, name: Option<&str>) -> serde_json::Value { + let username = username_prefix + .map(|prefix| format!("{}_{}", prefix, self.test_id)) + .unwrap_or_else(|| format!("user_{}", self.test_id)); + + UserFactory::create_request(Some(username), name.map(String::from)) + } +} + #[macro_export] macro_rules! create_test_app { () => {{ diff --git a/crates/backend/tests/endpoints/user.rs b/crates/backend/tests/endpoints/user.rs index bed9260..1d6bfaf 100644 --- a/crates/backend/tests/endpoints/user.rs +++ b/crates/backend/tests/endpoints/user.rs @@ -1,12 +1,10 @@ use actix_web::{http::header, test}; use serde::{Deserialize, Serialize}; -use crate::create_test_app; +use crate::{create_test_app, common::test_helpers::UserFactory}; #[cfg(test)] mod tests { - use serde_json::json; - use super::*; #[derive(Deserialize, Serialize, Debug, Clone)] @@ -19,46 +17,125 @@ mod tests { #[actix_web::test] async fn test_create_user() { let app = create_test_app!(); + let user_data = UserFactory::create_unique_request(); let resp = test::TestRequest::post() .uri("/api/v1/user") .insert_header(header::ContentType::json()) - .set_payload( - json!({ - "username": "testuser", - "name": "Test User", - "password": "password" - }) - .to_string(), - ) + .set_payload(user_data.to_string()) .send_request(&app) .await; let status = resp.status(); let user: RespCreateUser = test::read_body_json(resp).await; - assert!(user.name == "Test User"); - assert!(user.username == "testuser"); - + // Verify that the user was created with the expected structure + assert!(!user.name.is_empty()); + assert!(!user.username.is_empty()); + assert!(user.username.starts_with("user_test_")); + assert!(user.name.starts_with("Test User")); assert!(status.is_success()); - let resp_del = test::TestRequest::delete() + // Cleanup - delete the created user + let _delete_resp = test::TestRequest::delete() .uri(&format!("/api/v1/user/{}", user.id)) .send_request(&app) .await; - let status_del = resp_del.status(); + // Don't assert on cleanup status in case of race conditions + } - let delete_message: String = test::read_body_json(resp_del).await; + #[actix_web::test] + async fn test_delete_user() { + let app = create_test_app!(); + let user_data = UserFactory::create_unique_request(); + + // Create user to delete + let create_resp = test::TestRequest::post() + .uri("/api/v1/user") + .insert_header(header::ContentType::json()) + .set_payload(user_data.to_string()) + .send_request(&app) + .await; + + let create_status = create_resp.status(); + assert!(create_status.is_success(), "Failed to create user: {}", create_status); + let user: RespCreateUser = test::read_body_json(create_resp).await; + + // Delete the user + let delete_resp = test::TestRequest::delete() + .uri(&format!("/api/v1/user/{}", user.id)) + .send_request(&app) + .await; + let delete_status = delete_resp.status(); + + let delete_message: String = test::read_body_json(delete_resp).await; assert_eq!(delete_message, format!("User {} deleted", user.id)); + assert!(delete_status.is_success(), "Failed to delete user with status: {:?}", delete_status); + } - assert!( - status_del.is_success(), - "Failed to delete user with status: {:?}", - status_del - ); + #[actix_web::test] + async fn test_get_users() { + let app = create_test_app!(); - // Debugging output - dbg!(user); - dbg!(delete_message); + let resp = test::TestRequest::get() + .uri("/api/v1/user") + .send_request(&app) + .await; + + let status = resp.status(); + let users: Vec = test::read_body_json(resp).await; + + assert!(status.is_success()); + assert!(users.is_empty() || !users.is_empty()); // Just verify it returns a valid array + } + + #[actix_web::test] + async fn test_create_user_duplicate_username() { + let app = create_test_app!(); + let user_data = UserFactory::create_unique_request(); + + // Create first user + let resp1 = test::TestRequest::post() + .uri("/api/v1/user") + .insert_header(header::ContentType::json()) + .set_payload(user_data.to_string()) + .send_request(&app) + .await; + + let status1 = resp1.status(); + let user1: RespCreateUser = test::read_body_json(resp1).await; + assert!(status1.is_success()); + + // Try to create user with same username + let resp2 = test::TestRequest::post() + .uri("/api/v1/user") + .insert_header(header::ContentType::json()) + .set_payload(user_data.to_string()) + .send_request(&app) + .await; + + let status2 = resp2.status(); + assert!(status2.is_client_error() || status2.is_server_error()); + + // Cleanup + let _delete_resp = test::TestRequest::delete() + .uri(&format!("/api/v1/user/{}", user1.id)) + .send_request(&app) + .await; + // Don't assert on cleanup status in case of race conditions + } + + #[actix_web::test] + async fn test_delete_nonexistent_user() { + let app = create_test_app!(); + let fake_id = "00000000-0000-0000-0000-000000000000"; + + let resp = test::TestRequest::delete() + .uri(&format!("/api/v1/user/{}", fake_id)) + .send_request(&app) + .await; + + let status = resp.status(); + assert!(status.is_client_error() || status.is_server_error()); } }