//! Integration tests for project commands. use goingson_core::{NewProject, ProjectStatus, ProjectType}; use crate::test_utils::setup_test_state; #[tokio::test] async fn test_create_project_success() { let (state, user_id) = setup_test_state().await; let new_project = NewProject { name: "My Project".to_string(), description: "A test project".to_string(), project_type: ProjectType::SideProject, status: ProjectStatus::Active, }; let result = state.projects.create(user_id, new_project).await; assert!(result.is_ok()); let project = result.unwrap(); assert_eq!(project.name, "My Project"); assert_eq!(project.description, "A test project"); assert_eq!(project.project_type, ProjectType::SideProject); assert_eq!(project.status, ProjectStatus::Active); } #[tokio::test] async fn test_list_projects() { let (state, user_id) = setup_test_state().await; // Create two projects let project1 = NewProject { name: "Project 1".to_string(), description: "First".to_string(), project_type: ProjectType::Job, status: ProjectStatus::Active, }; state.projects.create(user_id, project1).await.unwrap(); let project2 = NewProject { name: "Project 2".to_string(), description: "Second".to_string(), project_type: ProjectType::Company, status: ProjectStatus::OnHold, }; state.projects.create(user_id, project2).await.unwrap(); // List all projects let projects = state.projects.list_all(user_id).await.unwrap(); assert_eq!(projects.len(), 2); } #[tokio::test] async fn test_get_project_by_id() { let (state, user_id) = setup_test_state().await; let new_project = NewProject { name: "Findable Project".to_string(), description: "Can be found".to_string(), project_type: ProjectType::Essay, status: ProjectStatus::Active, }; let created = state.projects.create(user_id, new_project).await.unwrap(); let fetched = state.projects.get_by_id(created.id, user_id).await.unwrap(); assert!(fetched.is_some()); assert_eq!(fetched.unwrap().name, "Findable Project"); } #[tokio::test] async fn test_get_nonexistent_project() { let (state, user_id) = setup_test_state().await; let nonexistent_id = goingson_core::ProjectId::new(); let result = state.projects.get_by_id(nonexistent_id, user_id).await.unwrap(); assert!(result.is_none()); } #[tokio::test] async fn test_update_project() { let (state, user_id) = setup_test_state().await; let new_project = NewProject { name: "Original Name".to_string(), description: "Original".to_string(), project_type: ProjectType::SideProject, status: ProjectStatus::Active, }; let created = state.projects.create(user_id, new_project).await.unwrap(); // Update the project let update = goingson_core::UpdateProject { name: "Updated Name".to_string(), description: "Updated description".to_string(), project_type: ProjectType::Job, status: ProjectStatus::OnHold, }; let updated = state .projects .update(created.id, user_id, update) .await .unwrap(); assert!(updated.is_some()); let project = updated.unwrap(); assert_eq!(project.name, "Updated Name"); assert_eq!(project.description, "Updated description"); assert_eq!(project.project_type, ProjectType::Job); assert_eq!(project.status, ProjectStatus::OnHold); } #[tokio::test] async fn test_delete_project() { let (state, user_id) = setup_test_state().await; let new_project = NewProject { name: "To Delete".to_string(), description: "Will be deleted".to_string(), project_type: ProjectType::Other, status: ProjectStatus::Active, }; let created = state.projects.create(user_id, new_project).await.unwrap(); // Delete the project let deleted = state.projects.delete(created.id, user_id).await.unwrap(); assert!(deleted); // Verify it's gone let fetched = state.projects.get_by_id(created.id, user_id).await.unwrap(); assert!(fetched.is_none()); } #[tokio::test] async fn test_find_project_by_name() { let (state, user_id) = setup_test_state().await; let new_project = NewProject { name: "Unique Project Name".to_string(), description: "Findable by name".to_string(), project_type: ProjectType::Article, status: ProjectStatus::Active, }; state.projects.create(user_id, new_project).await.unwrap(); // Find by exact name let found = state .projects .find_by_name(user_id, "Unique Project Name") .await .unwrap(); assert!(found.is_some()); assert_eq!(found.unwrap().name, "Unique Project Name"); // Find non-existent name let not_found = state .projects .find_by_name(user_id, "Does Not Exist") .await .unwrap(); assert!(not_found.is_none()); } #[tokio::test] async fn test_project_isolation_between_users() { let (state, user1_id) = setup_test_state().await; // Create a second user let user2_id = goingson_core::UserId::new(); let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); sqlx::query( "INSERT INTO users (id, email, password_hash, display_name, created_at) VALUES (?, ?, ?, ?, ?)" ) .bind(user2_id.to_string()) .bind("user2@example.com") .bind("password-hash") .bind("User 2") .bind(&now) .execute(&state.pool) .await .unwrap(); // User 1 creates a project let project = NewProject { name: "User 1 Project".to_string(), description: "Belongs to user 1".to_string(), project_type: ProjectType::SideProject, status: ProjectStatus::Active, }; let created = state.projects.create(user1_id, project).await.unwrap(); // User 1 can see it let user1_projects = state.projects.list_all(user1_id).await.unwrap(); assert_eq!(user1_projects.len(), 1); // User 2 cannot see it let user2_projects = state.projects.list_all(user2_id).await.unwrap(); assert_eq!(user2_projects.len(), 0); // User 2 cannot access by ID let fetched = state.projects.get_by_id(created.id, user2_id).await.unwrap(); assert!(fetched.is_none()); }