//! Integration tests for email repository operations. //! //! Tests local SQLite email operations only (no IMAP/SMTP). use chrono::{Duration, Utc}; use goingson_core::NewEmail; use crate::test_utils::{create_test_project, setup_test_state}; #[tokio::test] async fn create_and_list_email() { let (state, user_id) = setup_test_state().await; let new_email = NewEmail { project_id: None, from_address: "sender@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Test Subject".to_string(), body: "Test body content".to_string(), is_read: false, received_at: Some(Utc::now()), }; let email = state.emails.create(user_id, new_email).await.unwrap(); assert_eq!(email.subject, "Test Subject"); assert_eq!(email.from, "sender@example.com"); // List should include the email let emails = state.emails.list_all(user_id, false).await.unwrap(); assert_eq!(emails.len(), 1); assert_eq!(emails[0].subject, "Test Subject"); } #[tokio::test] async fn mark_email_read_unread() { let (state, user_id) = setup_test_state().await; let new_email = NewEmail { project_id: None, from_address: "sender@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Read/Unread Test".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; let email = state.emails.create(user_id, new_email).await.unwrap(); assert!(!email.is_read); // Mark as read let read_result = state.emails.mark_read(email.id, user_id).await.unwrap(); assert!(read_result); // Verify it's read let fetched = state.emails.get_by_id(email.id, user_id).await.unwrap().unwrap(); assert!(fetched.is_read); // Mark as unread let unread_result = state.emails.mark_unread(email.id, user_id).await.unwrap(); assert!(unread_result); // Verify it's unread let fetched = state.emails.get_by_id(email.id, user_id).await.unwrap().unwrap(); assert!(!fetched.is_read); } #[tokio::test] async fn snooze_and_unsnooze_email() { let (state, user_id) = setup_test_state().await; let new_email = NewEmail { project_id: None, from_address: "sender@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Snooze Test".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; let email = state.emails.create(user_id, new_email).await.unwrap(); // Snooze until future date let snooze_until = Utc::now() + Duration::days(3); let snoozed = state .emails .snooze(email.id, user_id, snooze_until) .await .unwrap() .unwrap(); assert!(snoozed.snoozed_until.is_some()); // Verify it appears in snoozed list let snoozed_list = state.emails.list_snoozed(user_id).await.unwrap(); assert_eq!(snoozed_list.len(), 1); assert_eq!(snoozed_list[0].id, email.id); // Unsnooze let unsnoozed = state .emails .unsnooze(email.id, user_id) .await .unwrap() .unwrap(); assert!(unsnoozed.snoozed_until.is_none()); // Verify snoozed list is empty let snoozed_list = state.emails.list_snoozed(user_id).await.unwrap(); assert!(snoozed_list.is_empty()); } #[tokio::test] async fn mark_waiting_and_clear() { let (state, user_id) = setup_test_state().await; let new_email = NewEmail { project_id: None, from_address: "sender@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Waiting Test".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; let email = state.emails.create(user_id, new_email).await.unwrap(); // Mark as waiting let expected_date = Utc::now() + Duration::days(5); let waiting = state .emails .mark_waiting(email.id, user_id, Some(expected_date)) .await .unwrap() .unwrap(); assert!(waiting.is_waiting()); // Verify it appears in waiting list let waiting_list = state.emails.list_waiting(user_id).await.unwrap(); assert_eq!(waiting_list.len(), 1); // Clear waiting let cleared = state .emails .clear_waiting(email.id, user_id) .await .unwrap() .unwrap(); assert!(!cleared.is_waiting()); // Verify waiting list is empty let waiting_list = state.emails.list_waiting(user_id).await.unwrap(); assert!(waiting_list.is_empty()); } #[tokio::test] async fn archive_email_local() { let (state, user_id) = setup_test_state().await; let new_email = NewEmail { project_id: None, from_address: "sender@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Archive Test".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; let email = state.emails.create(user_id, new_email).await.unwrap(); // Archive let archived = state.emails.archive(email.id, user_id).await.unwrap(); assert!(archived); // Not in normal list (exclude archived) let emails = state.emails.list_all(user_id, false).await.unwrap(); assert!(emails.is_empty()); // In list when including archived let all_emails = state.emails.list_all(user_id, true).await.unwrap(); assert_eq!(all_emails.len(), 1); } #[tokio::test] async fn link_email_to_project() { let (state, user_id) = setup_test_state().await; let project_id = create_test_project(&state, user_id).await; let new_email = NewEmail { project_id: None, from_address: "sender@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Link Test".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; let email = state.emails.create(user_id, new_email).await.unwrap(); assert!(email.project_id.is_none()); // Link to project let linked = state .emails .link_to_project(email.id, user_id, Some(project_id)) .await .unwrap(); assert!(linked); // Verify in project emails let project_emails = state .emails .list_by_project(user_id, project_id) .await .unwrap(); assert_eq!(project_emails.len(), 1); assert_eq!(project_emails[0].id, email.id); } #[tokio::test] async fn list_unlinked_emails() { let (state, user_id) = setup_test_state().await; let project_id = create_test_project(&state, user_id).await; // Create a linked email let linked_email = NewEmail { project_id: Some(project_id), from_address: "linked@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Linked Email".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; state.emails.create(user_id, linked_email).await.unwrap(); // Create an unlinked email let unlinked_email = NewEmail { project_id: None, from_address: "unlinked@example.com".to_string(), to_address: "me@example.com".to_string(), subject: "Unlinked Email".to_string(), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; state.emails.create(user_id, unlinked_email).await.unwrap(); let unlinked = state.emails.list_unlinked(user_id).await.unwrap(); assert_eq!(unlinked.len(), 1); assert_eq!(unlinked[0].subject, "Unlinked Email"); } #[tokio::test] async fn count_unread() { let (state, user_id) = setup_test_state().await; // Initially zero let count = state.emails.count_unread(user_id).await.unwrap(); assert_eq!(count, 0); // Create two unread emails for i in 0..2 { let new_email = NewEmail { project_id: None, from_address: format!("sender{}@example.com", i), to_address: "me@example.com".to_string(), subject: format!("Email {}", i), body: "Body".to_string(), is_read: false, received_at: Some(Utc::now()), }; state.emails.create(user_id, new_email).await.unwrap(); } let count = state.emails.count_unread(user_id).await.unwrap(); assert_eq!(count, 2); // Mark one as read let emails = state.emails.list_all(user_id, false).await.unwrap(); state.emails.mark_read(emails[0].id, user_id).await.unwrap(); let count = state.emails.count_unread(user_id).await.unwrap(); assert_eq!(count, 1); }