//! Integration tests for SqliteAttachmentRepository. mod common; use goingson_core::{ AttachmentRepository, EmailRepository, NewAttachment, NewEmailWithTracking, ProjectId, }; use goingson_db_sqlite::{SqliteAttachmentRepository, SqliteEmailRepository}; use uuid::Uuid; #[tokio::test] async fn test_create_and_get_attachment() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteAttachmentRepository::new(pool); let attachment = repo .create(user_id, NewAttachment { task_id: Some(task_id), project_id: None, filename: "test.pdf".into(), file_size: 1024, mime_type: "application/pdf".into(), blob_hash: "abc123".into(), source_email_id: None, }) .await .expect("Failed to create attachment"); assert_eq!(attachment.filename, "test.pdf"); assert_eq!(attachment.file_size, 1024); assert_eq!(attachment.blob_hash, "abc123"); assert_eq!(attachment.task_id, Some(task_id)); let fetched = repo.get_by_id(attachment.id, user_id).await.unwrap(); assert!(fetched.is_some()); assert_eq!(fetched.unwrap().id, attachment.id); } #[tokio::test] async fn test_list_for_task() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteAttachmentRepository::new(pool); // Create two attachments for the same task for i in 0..2 { repo.create(user_id, NewAttachment { task_id: Some(task_id), project_id: None, filename: format!("file{}.txt", i), file_size: 100 * (i + 1), mime_type: "text/plain".into(), blob_hash: format!("hash{}", i), source_email_id: None, }) .await .unwrap(); } let list = repo.list_for_task(task_id, user_id).await.unwrap(); assert_eq!(list.len(), 2); } #[tokio::test] async fn test_list_for_project() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let repo = SqliteAttachmentRepository::new(pool.clone()); // Create a project let project_id = ProjectId::from(Uuid::new_v4()); let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); sqlx::query("INSERT INTO projects (id, user_id, name, status, created_at) VALUES (?, ?, ?, ?, ?)") .bind(project_id.to_string()) .bind(user_id.to_string()) .bind("Test Project") .bind("Active") .bind(&now) .execute(&pool) .await .unwrap(); repo.create(user_id, NewAttachment { task_id: None, project_id: Some(project_id), filename: "spec.pdf".into(), file_size: 5000, mime_type: "application/pdf".into(), blob_hash: "projhash".into(), source_email_id: None, }) .await .unwrap(); let list = repo.list_for_project(project_id, user_id).await.unwrap(); assert_eq!(list.len(), 1); assert_eq!(list[0].filename, "spec.pdf"); } #[tokio::test] async fn test_delete_attachment() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteAttachmentRepository::new(pool); let attachment = repo .create(user_id, NewAttachment { task_id: Some(task_id), project_id: None, filename: "delete-me.txt".into(), file_size: 10, mime_type: "text/plain".into(), blob_hash: "delhash".into(), source_email_id: None, }) .await .unwrap(); let deleted = repo.delete(attachment.id, user_id).await.unwrap(); assert!(deleted); let fetched = repo.get_by_id(attachment.id, user_id).await.unwrap(); assert!(fetched.is_none()); } #[tokio::test] async fn test_list_by_blob_hash() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let task_id = common::create_test_task(&pool, user_id).await; let repo = SqliteAttachmentRepository::new(pool); // Two attachments sharing the same blob hash (dedup scenario) let shared_hash = "shared_blob_hash_123"; for name in ["copy1.pdf", "copy2.pdf"] { repo.create(user_id, NewAttachment { task_id: Some(task_id), project_id: None, filename: name.into(), file_size: 500, mime_type: "application/pdf".into(), blob_hash: shared_hash.into(), source_email_id: None, }) .await .unwrap(); } let by_hash = repo.list_by_blob_hash(shared_hash, user_id).await.unwrap(); assert_eq!(by_hash.len(), 2); } #[tokio::test] async fn test_email_attachment_meta_persisted() { let pool = common::setup_test_db().await; let user_id = common::create_test_user(&pool).await; let email_repo = SqliteEmailRepository::new(pool); let meta_json = r#"[{"filename":"doc.pdf","mime_type":"application/pdf","size":1234,"blob_hash":"abc"}]"#; let email = email_repo .create_with_tracking(user_id, NewEmailWithTracking { project_id: None, from_address: "alice@example.com".into(), to_address: "bob@example.com".into(), subject: "With attachment".into(), body: "See attached".into(), html_body: None, is_read: false, is_archived: false, received_at: Some(chrono::Utc::now()), message_id: Some("".into()), in_reply_to: None, thread_id: None, email_account_id: None, is_outgoing: false, imap_uid: Some(99), source_folder: Some("INBOX".into()), attachment_meta: Some(meta_json.into()), }) .await .expect("Failed to create email with attachment_meta"); // Read back and verify attachment_meta was persisted let fetched = email_repo.get_by_id(email.id, user_id).await.unwrap().unwrap(); assert_eq!(fetched.attachment_meta.as_deref(), Some(meta_json)); }