Skip to main content

max / goingson

6.3 KB · 193 lines History Blame Raw
1 //! Integration tests for SqliteAttachmentRepository.
2
3 mod common;
4
5 use goingson_core::{
6 AttachmentRepository, EmailRepository, NewAttachment, NewEmailWithTracking,
7 ProjectId,
8 };
9 use goingson_db_sqlite::{SqliteAttachmentRepository, SqliteEmailRepository};
10 use uuid::Uuid;
11
12 #[tokio::test]
13 async fn test_create_and_get_attachment() {
14 let pool = common::setup_test_db().await;
15 let user_id = common::create_test_user(&pool).await;
16 let task_id = common::create_test_task(&pool, user_id).await;
17 let repo = SqliteAttachmentRepository::new(pool);
18
19 let attachment = repo
20 .create(user_id, NewAttachment {
21 task_id: Some(task_id),
22 project_id: None,
23 filename: "test.pdf".into(),
24 file_size: 1024,
25 mime_type: "application/pdf".into(),
26 blob_hash: "abc123".into(),
27 source_email_id: None,
28 })
29 .await
30 .expect("Failed to create attachment");
31
32 assert_eq!(attachment.filename, "test.pdf");
33 assert_eq!(attachment.file_size, 1024);
34 assert_eq!(attachment.blob_hash, "abc123");
35 assert_eq!(attachment.task_id, Some(task_id));
36
37 let fetched = repo.get_by_id(attachment.id, user_id).await.unwrap();
38 assert!(fetched.is_some());
39 assert_eq!(fetched.unwrap().id, attachment.id);
40 }
41
42 #[tokio::test]
43 async fn test_list_for_task() {
44 let pool = common::setup_test_db().await;
45 let user_id = common::create_test_user(&pool).await;
46 let task_id = common::create_test_task(&pool, user_id).await;
47 let repo = SqliteAttachmentRepository::new(pool);
48
49 // Create two attachments for the same task
50 for i in 0..2 {
51 repo.create(user_id, NewAttachment {
52 task_id: Some(task_id),
53 project_id: None,
54 filename: format!("file{}.txt", i),
55 file_size: 100 * (i + 1),
56 mime_type: "text/plain".into(),
57 blob_hash: format!("hash{}", i),
58 source_email_id: None,
59 })
60 .await
61 .unwrap();
62 }
63
64 let list = repo.list_for_task(task_id, user_id).await.unwrap();
65 assert_eq!(list.len(), 2);
66 }
67
68 #[tokio::test]
69 async fn test_list_for_project() {
70 let pool = common::setup_test_db().await;
71 let user_id = common::create_test_user(&pool).await;
72 let repo = SqliteAttachmentRepository::new(pool.clone());
73
74 // Create a project
75 let project_id = ProjectId::from(Uuid::new_v4());
76 let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string();
77 sqlx::query("INSERT INTO projects (id, user_id, name, status, created_at) VALUES (?, ?, ?, ?, ?)")
78 .bind(project_id.to_string())
79 .bind(user_id.to_string())
80 .bind("Test Project")
81 .bind("Active")
82 .bind(&now)
83 .execute(&pool)
84 .await
85 .unwrap();
86
87 repo.create(user_id, NewAttachment {
88 task_id: None,
89 project_id: Some(project_id),
90 filename: "spec.pdf".into(),
91 file_size: 5000,
92 mime_type: "application/pdf".into(),
93 blob_hash: "projhash".into(),
94 source_email_id: None,
95 })
96 .await
97 .unwrap();
98
99 let list = repo.list_for_project(project_id, user_id).await.unwrap();
100 assert_eq!(list.len(), 1);
101 assert_eq!(list[0].filename, "spec.pdf");
102 }
103
104 #[tokio::test]
105 async fn test_delete_attachment() {
106 let pool = common::setup_test_db().await;
107 let user_id = common::create_test_user(&pool).await;
108 let task_id = common::create_test_task(&pool, user_id).await;
109 let repo = SqliteAttachmentRepository::new(pool);
110
111 let attachment = repo
112 .create(user_id, NewAttachment {
113 task_id: Some(task_id),
114 project_id: None,
115 filename: "delete-me.txt".into(),
116 file_size: 10,
117 mime_type: "text/plain".into(),
118 blob_hash: "delhash".into(),
119 source_email_id: None,
120 })
121 .await
122 .unwrap();
123
124 let deleted = repo.delete(attachment.id, user_id).await.unwrap();
125 assert!(deleted);
126
127 let fetched = repo.get_by_id(attachment.id, user_id).await.unwrap();
128 assert!(fetched.is_none());
129 }
130
131 #[tokio::test]
132 async fn test_list_by_blob_hash() {
133 let pool = common::setup_test_db().await;
134 let user_id = common::create_test_user(&pool).await;
135 let task_id = common::create_test_task(&pool, user_id).await;
136 let repo = SqliteAttachmentRepository::new(pool);
137
138 // Two attachments sharing the same blob hash (dedup scenario)
139 let shared_hash = "shared_blob_hash_123";
140 for name in ["copy1.pdf", "copy2.pdf"] {
141 repo.create(user_id, NewAttachment {
142 task_id: Some(task_id),
143 project_id: None,
144 filename: name.into(),
145 file_size: 500,
146 mime_type: "application/pdf".into(),
147 blob_hash: shared_hash.into(),
148 source_email_id: None,
149 })
150 .await
151 .unwrap();
152 }
153
154 let by_hash = repo.list_by_blob_hash(shared_hash, user_id).await.unwrap();
155 assert_eq!(by_hash.len(), 2);
156 }
157
158 #[tokio::test]
159 async fn test_email_attachment_meta_persisted() {
160 let pool = common::setup_test_db().await;
161 let user_id = common::create_test_user(&pool).await;
162 let email_repo = SqliteEmailRepository::new(pool);
163
164 let meta_json = r#"[{"filename":"doc.pdf","mime_type":"application/pdf","size":1234,"blob_hash":"abc"}]"#;
165
166 let email = email_repo
167 .create_with_tracking(user_id, NewEmailWithTracking {
168 project_id: None,
169 from_address: "alice@example.com".into(),
170 to_address: "bob@example.com".into(),
171 subject: "With attachment".into(),
172 body: "See attached".into(),
173 html_body: None,
174 is_read: false,
175 is_archived: false,
176 received_at: Some(chrono::Utc::now()),
177 message_id: Some("<attach-test@example.com>".into()),
178 in_reply_to: None,
179 thread_id: None,
180 email_account_id: None,
181 is_outgoing: false,
182 imap_uid: Some(99),
183 source_folder: Some("INBOX".into()),
184 attachment_meta: Some(meta_json.into()),
185 })
186 .await
187 .expect("Failed to create email with attachment_meta");
188
189 // Read back and verify attachment_meta was persisted
190 let fetched = email_repo.get_by_id(email.id, user_id).await.unwrap().unwrap();
191 assert_eq!(fetched.attachment_meta.as_deref(), Some(meta_json));
192 }
193