Skip to main content

max / goingson

9.5 KB · 289 lines History Blame Raw
1 //! Integration tests for subtask methods on SqliteTaskRepository.
2
3 mod common;
4
5 use goingson_core::TaskRepository;
6 use goingson_db_sqlite::SqliteTaskRepository;
7
8 #[tokio::test]
9 async fn test_add_subtask_and_get() {
10 let pool = common::setup_test_db().await;
11 let user_id = common::create_test_user(&pool).await;
12 let task_id = common::create_test_task(&pool, user_id).await;
13 let repo = SqliteTaskRepository::new(pool);
14
15 let subtask = repo
16 .add_subtask(task_id, user_id, "First subtask")
17 .await
18 .expect("Failed to add subtask");
19 assert!(subtask.is_some(), "Should return Some for valid task/user");
20
21 let subtask = subtask.unwrap();
22 assert_eq!(subtask.task_id, task_id);
23 assert_eq!(subtask.text, "First subtask");
24 assert!(!subtask.is_completed);
25 assert!(subtask.linked_task_id.is_none());
26
27 let subtasks = repo
28 .get_subtasks_for_task(task_id)
29 .await
30 .expect("Failed to get subtasks");
31 assert_eq!(subtasks.len(), 1);
32 assert_eq!(subtasks[0].id, subtask.id);
33 assert_eq!(subtasks[0].text, "First subtask");
34 }
35
36 #[tokio::test]
37 async fn test_subtasks_ordered_by_position() {
38 let pool = common::setup_test_db().await;
39 let user_id = common::create_test_user(&pool).await;
40 let task_id = common::create_test_task(&pool, user_id).await;
41 let repo = SqliteTaskRepository::new(pool);
42
43 let first = repo
44 .add_subtask(task_id, user_id, "Step 1")
45 .await
46 .expect("Failed to add subtask")
47 .unwrap();
48
49 let second = repo
50 .add_subtask(task_id, user_id, "Step 2")
51 .await
52 .expect("Failed to add subtask")
53 .unwrap();
54
55 let third = repo
56 .add_subtask(task_id, user_id, "Step 3")
57 .await
58 .expect("Failed to add subtask")
59 .unwrap();
60
61 let subtasks = repo
62 .get_subtasks_for_task(task_id)
63 .await
64 .expect("Failed to get subtasks");
65 assert_eq!(subtasks.len(), 3);
66
67 // Should be ordered by position ascending
68 assert_eq!(subtasks[0].id, first.id);
69 assert_eq!(subtasks[0].text, "Step 1");
70 assert_eq!(subtasks[1].id, second.id);
71 assert_eq!(subtasks[1].text, "Step 2");
72 assert_eq!(subtasks[2].id, third.id);
73 assert_eq!(subtasks[2].text, "Step 3");
74
75 assert!(subtasks[0].position <= subtasks[1].position);
76 assert!(subtasks[1].position <= subtasks[2].position);
77 }
78
79 #[tokio::test]
80 async fn test_subtask_position_auto_increments() {
81 let pool = common::setup_test_db().await;
82 let user_id = common::create_test_user(&pool).await;
83 let task_id = common::create_test_task(&pool, user_id).await;
84 let repo = SqliteTaskRepository::new(pool);
85
86 let a = repo
87 .add_subtask(task_id, user_id, "A")
88 .await
89 .expect("Failed to add subtask")
90 .unwrap();
91
92 let b = repo
93 .add_subtask(task_id, user_id, "B")
94 .await
95 .expect("Failed to add subtask")
96 .unwrap();
97
98 let c = repo
99 .add_subtask(task_id, user_id, "C")
100 .await
101 .expect("Failed to add subtask")
102 .unwrap();
103
104 assert_eq!(a.position, 0);
105 assert_eq!(b.position, 1);
106 assert_eq!(c.position, 2);
107 }
108
109 #[tokio::test]
110 async fn test_add_subtask_wrong_user_returns_none() {
111 let pool = common::setup_test_db().await;
112 let user1 = common::create_test_user(&pool).await;
113 let user2 = common::create_test_user(&pool).await;
114 let task_id = common::create_test_task(&pool, user1).await;
115 let repo = SqliteTaskRepository::new(pool);
116
117 let result = repo
118 .add_subtask(task_id, user2, "Unauthorized subtask")
119 .await
120 .expect("Should not error, just return None");
121 assert!(result.is_none(), "Should return None when user doesn't own the task");
122 }
123
124 #[tokio::test]
125 async fn test_toggle_subtask_completion() {
126 let pool = common::setup_test_db().await;
127 let user_id = common::create_test_user(&pool).await;
128 let task_id = common::create_test_task(&pool, user_id).await;
129 let repo = SqliteTaskRepository::new(pool);
130
131 let subtask = repo
132 .add_subtask(task_id, user_id, "Toggle me")
133 .await
134 .expect("Failed to add subtask")
135 .unwrap();
136 assert!(!subtask.is_completed, "New subtask should be incomplete");
137
138 // Toggle to completed
139 let toggled = repo
140 .toggle_subtask(subtask.id, user_id)
141 .await
142 .expect("Failed to toggle subtask");
143 assert!(toggled.is_some());
144 assert!(toggled.unwrap().is_completed, "Should be completed after first toggle");
145
146 // Toggle back to incomplete
147 let toggled_back = repo
148 .toggle_subtask(subtask.id, user_id)
149 .await
150 .expect("Failed to toggle subtask");
151 assert!(toggled_back.is_some());
152 assert!(!toggled_back.unwrap().is_completed, "Should be incomplete after second toggle");
153 }
154
155 #[tokio::test]
156 async fn test_update_subtask_text() {
157 let pool = common::setup_test_db().await;
158 let user_id = common::create_test_user(&pool).await;
159 let task_id = common::create_test_task(&pool, user_id).await;
160 let repo = SqliteTaskRepository::new(pool);
161
162 let subtask = repo
163 .add_subtask(task_id, user_id, "Original text")
164 .await
165 .expect("Failed to add subtask")
166 .unwrap();
167
168 let updated = repo
169 .update_subtask(subtask.id, user_id, "Updated text")
170 .await
171 .expect("Failed to update subtask");
172 assert!(updated.is_some());
173 assert_eq!(updated.unwrap().text, "Updated text");
174
175 // Verify via get
176 let subtasks = repo
177 .get_subtasks_for_task(task_id)
178 .await
179 .expect("Failed to get subtasks");
180 assert_eq!(subtasks.len(), 1);
181 assert_eq!(subtasks[0].text, "Updated text");
182 }
183
184 #[tokio::test]
185 async fn test_delete_subtask() {
186 let pool = common::setup_test_db().await;
187 let user_id = common::create_test_user(&pool).await;
188 let task_id = common::create_test_task(&pool, user_id).await;
189 let repo = SqliteTaskRepository::new(pool);
190
191 let subtask = repo
192 .add_subtask(task_id, user_id, "To be deleted")
193 .await
194 .expect("Failed to add subtask")
195 .unwrap();
196
197 let deleted = repo
198 .delete_subtask(subtask.id, user_id)
199 .await
200 .expect("Failed to delete subtask");
201 assert!(deleted, "Should return true for successful deletion");
202
203 let subtasks = repo
204 .get_subtasks_for_task(task_id)
205 .await
206 .expect("Failed to get subtasks");
207 assert!(subtasks.is_empty(), "Subtask should be gone after deletion");
208 }
209
210 #[tokio::test]
211 async fn test_delete_subtask_wrong_user_returns_false() {
212 let pool = common::setup_test_db().await;
213 let user1 = common::create_test_user(&pool).await;
214 let user2 = common::create_test_user(&pool).await;
215 let task_id = common::create_test_task(&pool, user1).await;
216 let repo = SqliteTaskRepository::new(pool);
217
218 let subtask = repo
219 .add_subtask(task_id, user1, "User1's subtask")
220 .await
221 .expect("Failed to add subtask")
222 .unwrap();
223
224 let deleted = repo
225 .delete_subtask(subtask.id, user2)
226 .await
227 .expect("Should not error, just return false");
228 assert!(!deleted, "Should return false when user doesn't own the subtask");
229
230 // Verify the subtask still exists
231 let subtasks = repo
232 .get_subtasks_for_task(task_id)
233 .await
234 .expect("Failed to get subtasks");
235 assert_eq!(subtasks.len(), 1, "Subtask should still exist after unauthorized delete");
236 }
237
238 #[tokio::test]
239 async fn test_add_subtask_link_syncs_status() {
240 let pool = common::setup_test_db().await;
241 let user_id = common::create_test_user(&pool).await;
242 let parent_task_id = common::create_test_task(&pool, user_id).await;
243 let completed_task_id = common::create_test_task(&pool, user_id).await;
244 let pending_task_id = common::create_test_task(&pool, user_id).await;
245
246 // Mark the first linked task as Completed
247 sqlx::query("UPDATE tasks SET status = 'Completed' WHERE id = ?")
248 .bind(completed_task_id.to_string())
249 .execute(&pool)
250 .await
251 .unwrap();
252
253 let repo = SqliteTaskRepository::new(pool);
254
255 // Link the completed task — subtask should be marked completed
256 let linked_completed = repo
257 .add_subtask_link(parent_task_id, user_id, completed_task_id)
258 .await
259 .expect("Failed to add subtask link");
260 assert!(linked_completed.is_some(), "Should return Some for valid link");
261 let linked_completed = linked_completed.unwrap();
262 assert_eq!(linked_completed.linked_task_id, Some(completed_task_id));
263 assert!(linked_completed.is_completed, "Linked subtask should be completed when linked task is Completed");
264
265 // Link the pending task — subtask should be incomplete
266 let linked_pending = repo
267 .add_subtask_link(parent_task_id, user_id, pending_task_id)
268 .await
269 .expect("Failed to add subtask link");
270 assert!(linked_pending.is_some(), "Should return Some for valid link");
271 let linked_pending = linked_pending.unwrap();
272 assert_eq!(linked_pending.linked_task_id, Some(pending_task_id));
273 assert!(!linked_pending.is_completed, "Linked subtask should be incomplete when linked task is Pending");
274 }
275
276 #[tokio::test]
277 async fn test_get_subtasks_empty_task() {
278 let pool = common::setup_test_db().await;
279 let user_id = common::create_test_user(&pool).await;
280 let task_id = common::create_test_task(&pool, user_id).await;
281 let repo = SqliteTaskRepository::new(pool);
282
283 let subtasks = repo
284 .get_subtasks_for_task(task_id)
285 .await
286 .expect("Failed to get subtasks");
287 assert!(subtasks.is_empty(), "New task should have no subtasks");
288 }
289