From fd61fc3910acfe4dfcc4610ba2968781c1ee90f6 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 2 Apr 2025 09:25:38 -0600 Subject: [PATCH] duplciate is unnested --- .../rest/routes/chats/duplicate_chat.rs | 98 ++++++++------- api/tests/example_test.rs | 119 ++++++++++++++++++ 2 files changed, 169 insertions(+), 48 deletions(-) create mode 100644 api/tests/example_test.rs diff --git a/api/src/routes/rest/routes/chats/duplicate_chat.rs b/api/src/routes/rest/routes/chats/duplicate_chat.rs index 988566b98..e6a46b897 100644 --- a/api/src/routes/rest/routes/chats/duplicate_chat.rs +++ b/api/src/routes/rest/routes/chats/duplicate_chat.rs @@ -4,7 +4,7 @@ use axum::Extension; use handlers::chats::duplicate_chat_handler; use handlers::chats::types::ChatWithMessages; use middleware::AuthenticatedUser; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use uuid::Uuid; use crate::routes::rest::ApiResponse; @@ -18,36 +18,35 @@ pub struct DuplicateChatRequest { pub message_id: Option, } -/// Response containing the duplicated chat with messages -#[derive(Serialize, Deserialize)] -pub struct DuplicateChatResponse { - /// The duplicated chat with all its messages - pub chat: ChatWithMessages, -} - /// Handler for POST /chats/duplicate endpoint -/// +/// /// Duplicates an existing chat, including all messages and file references. /// If message_id is provided, only messages from that point onward are duplicated. /// Each file reference is duplicated with is_duplicate=true to track duplicated content. pub async fn duplicate_chat_route( Extension(user): Extension, Json(request): Json, -) -> Result, (StatusCode, String)> { +) -> Result, (StatusCode, String)> { // Call the handler function with the request parameters match duplicate_chat_handler(&request.id, request.message_id.as_ref(), &user).await { - Ok(chat) => Ok(ApiResponse::JsonData(DuplicateChatResponse { chat })), + Ok(chat) => Ok(ApiResponse::JsonData(chat)), Err(e) => { tracing::error!("Error duplicating chat: {}", e); - + // Return different error codes based on the type of error let error_msg = e.to_string(); if error_msg.contains("not found") { Err((StatusCode::NOT_FOUND, "Chat not found".to_string())) } else if error_msg.contains("permission") { - Err((StatusCode::FORBIDDEN, "You don't have permission to view this chat".to_string())) + Err(( + StatusCode::FORBIDDEN, + "You don't have permission to view this chat".to_string(), + )) } else { - Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to duplicate chat".to_string())) + Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to duplicate chat".to_string(), + )) } } } @@ -61,21 +60,21 @@ mod tests { use axum::routing::post; use axum::Router; use chrono::Utc; - use middleware::{AuthenticatedUser, types::OrganizationMembership}; use database::{ enums::{AssetPermissionRole, AssetType, IdentityType, UserOrganizationRole}, models::{AssetPermission, Chat, Message, MessageToFile, User}, pool::get_pg_pool, schema::{asset_permissions, chats, messages, messages_to_files, users}, }; - use diesel::prelude::*; use diesel::insert_into; + use diesel::prelude::*; use diesel_async::RunQueryDsl; + use middleware::{types::OrganizationMembership, AuthenticatedUser}; use serde_json::{json, Value}; use std::collections::HashMap; use tower::ServiceExt; use uuid::Uuid; - + async fn setup_test_user() -> (AuthenticatedUser, User) { let user_id = Uuid::new_v4(); let user = User { @@ -88,21 +87,20 @@ mod tests { attributes: json!({}), avatar_url: None, }; - + let mut conn = get_pg_pool().get().await.unwrap(); insert_into(users::table) .values(&user) .execute(&mut conn) - .await.unwrap(); - + .await + .unwrap(); + let org_id = Uuid::new_v4(); - let organizations = vec![ - OrganizationMembership { - id: org_id, - role: UserOrganizationRole::Owner, - } - ]; - + let organizations = vec![OrganizationMembership { + id: org_id, + role: UserOrganizationRole::Owner, + }]; + let auth_user = AuthenticatedUser { id: user_id, email: "test@example.com".to_string(), @@ -115,14 +113,14 @@ mod tests { attributes: json!({}), avatar_url: None, }; - + (auth_user, user) } - + async fn setup_test_chat(user: &User) -> (Uuid, Vec) { let chat_id = Uuid::new_v4(); let now = Utc::now(); - + // Create chat record let chat = Chat { id: chat_id, @@ -139,13 +137,14 @@ mod tests { most_recent_file_id: None, most_recent_file_type: None, }; - + let mut conn = get_pg_pool().get().await.unwrap(); insert_into(chats::table) .values(&chat) .execute(&mut conn) - .await.unwrap(); - + .await + .unwrap(); + // Create permission for user let permission = AssetPermission { identity_id: user.id, @@ -159,15 +158,16 @@ mod tests { created_by: user.id, updated_by: user.id, }; - + insert_into(asset_permissions::table) .values(&permission) .execute(&mut conn) - .await.unwrap(); - + .await + .unwrap(); + // Create test messages let message_ids = vec![Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()]; - + for (i, message_id) in message_ids.iter().enumerate() { let message = Message { id: *message_id, @@ -184,20 +184,21 @@ mod tests { created_by: user.id, feedback: None, }; - + insert_into(messages::table) .values(&message) .execute(&mut conn) - .await.unwrap(); + .await + .unwrap(); } - + (chat_id, message_ids) } - + async fn setup_test_file_reference(message_id: &Uuid, user_id: &Uuid) -> Uuid { let file_id = Uuid::new_v4(); let now = Utc::now(); - + let file_ref = MessageToFile { id: Uuid::new_v4(), message_id: *message_id, @@ -207,23 +208,24 @@ mod tests { deleted_at: None, is_duplicate: false, }; - + let mut conn = get_pg_pool().get().await.unwrap(); insert_into(messages_to_files::table) .values(&file_ref) .execute(&mut conn) - .await.unwrap(); - + .await + .unwrap(); + file_id } - + fn create_test_app(auth_user: AuthenticatedUser) -> Router { Router::new() .route("/chats/duplicate", post(duplicate_chat_route)) .layer(Extension(auth_user)) } - + // Instead of unit tests that depend on a real database connection, // we should run the integration tests from the tests directory. // These unit tests might become outdated if the model structures change. -} \ No newline at end of file +} diff --git a/api/tests/example_test.rs b/api/tests/example_test.rs new file mode 100644 index 000000000..621039c5f --- /dev/null +++ b/api/tests/example_test.rs @@ -0,0 +1,119 @@ +use anyhow::Result; +use uuid::Uuid; + +// Import the TestInstance type +use crate::TestInstance; + +/// Example test that requires database access - environment automatically initialized +#[tokio::test] +async fn test_simple_database_connection() -> Result<()> { + // The environment is already initialized by the test framework + + // Just create a test instance to get a unique test ID and access to pools + let test = TestInstance::new().await?; + + // Now we can use the test database + let pool = test.get_diesel_pool(); + + // Verify connection works by getting a connection from the pool + let conn = pool.get().await?; + + // We've successfully connected! + Ok(()) +} + +/// Example test for working with test data +#[tokio::test] +async fn test_with_isolation() -> Result<()> { + // Create a test instance with a unique ID + let test = TestInstance::new().await?; + + // Use the test_id to tag test data + let test_id = &test.test_id; + + // Create a connection + let mut conn = test.get_diesel_pool().get().await?; + + // Example raw SQL for test data creation (using the test_id) + diesel::sql_query("INSERT INTO example_table (id, name, test_id) VALUES ($1, $2, $3)") + .bind::(Uuid::new_v4()) + .bind::("Test item") + .bind::(test_id) + .execute(&mut conn) + .await?; + + // Run your test logic... + + // Clean up after the test + diesel::sql_query("DELETE FROM example_table WHERE test_id = $1") + .bind::(test_id) + .execute(&mut conn) + .await?; + + Ok(()) +} + +/// Example test for third-party API integration +#[tokio::test] +async fn test_third_party_api() -> Result<()> { + // Skip test if third-party testing is disabled + if std::env::var("ENABLE_THIRD_PARTY_TESTS").is_err() { + println!("Skipping third-party API test"); + return Ok(()); + } + + // Get API credentials from the test environment + let api_key = std::env::var("THIRD_PARTY_API_KEY") + .expect("THIRD_PARTY_API_KEY must be set for this test"); + let api_url = std::env::var("THIRD_PARTY_API_URL") + .expect("THIRD_PARTY_API_URL must be set for this test"); + + // Create test instance for database access if needed + let test = TestInstance::new().await?; + + // Create API client with test credentials + // let client = ThirdPartyClient::new(&api_key, &api_url); + + // Run API tests... + + Ok(()) +} + +/// Example test that specifically uses Redis +#[tokio::test] +async fn test_redis_connection() -> Result<()> { + // Create test instance + let test = TestInstance::new().await?; + + // Get Redis pool + let redis_pool = test.get_redis_pool(); + + // Get Redis connection + let mut conn = redis_pool.get().await?; + + // Example Redis operations + // redis::cmd("SET").arg("test_key").arg("test_value").execute(&mut *conn); + // let value: String = redis::cmd("GET").arg("test_key").query_async(&mut *conn).await?; + // assert_eq!(value, "test_value"); + + Ok(()) +} + +/// Example of a test that runs with parallel services +#[tokio::test] +async fn test_multiple_services() -> Result<()> { + // Create test instance + let test = TestInstance::new().await?; + + // Access both SQL and Redis databases + let pg_pool = test.get_diesel_pool(); + let redis_pool = test.get_redis_pool(); + + // Example of parallel operations + let pg_conn = pg_pool.get().await?; + let redis_conn = redis_pool.get().await?; + + // Use both connections... + + Ok(()) +} \ No newline at end of file