| 2 |
2 |
|
|
| 3 |
3 |
|
mod common;
|
| 4 |
4 |
|
|
| 5 |
|
- |
use goingson_core::TaskAnnotations;
|
|
5 |
+ |
use goingson_core::{TaskAnnotations, TaskCrud};
|
| 6 |
6 |
|
use goingson_db_sqlite::SqliteTaskRepository;
|
| 7 |
7 |
|
|
| 8 |
8 |
|
#[tokio::test]
|
| 314 |
314 |
|
.expect("Failed to get subtasks");
|
| 315 |
315 |
|
assert!(subtasks.is_empty(), "New task should have no subtasks");
|
| 316 |
316 |
|
}
|
|
317 |
+ |
|
|
318 |
+ |
#[tokio::test]
|
|
319 |
+ |
async fn test_linked_subtask_text_follows_the_linked_task_description() {
|
|
320 |
+ |
let pool = common::setup_test_db().await;
|
|
321 |
+ |
let user_id = common::create_test_user(&pool).await;
|
|
322 |
+ |
let parent_task_id = common::create_test_task(&pool, user_id).await;
|
|
323 |
+ |
let linked_task_id = common::create_test_task(&pool, user_id).await;
|
|
324 |
+ |
|
|
325 |
+ |
let repo = SqliteTaskRepository::new(pool.clone());
|
|
326 |
+ |
repo.add_subtask_link(parent_task_id, user_id, linked_task_id)
|
|
327 |
+ |
.await
|
|
328 |
+ |
.expect("Failed to add subtask link")
|
|
329 |
+ |
.expect("Should return Some for valid link");
|
|
330 |
+ |
|
|
331 |
+ |
// Edit the linked task after the link was made. The subtask stored a copy
|
|
332 |
+ |
// of the old description; reads must not serve that copy.
|
|
333 |
+ |
sqlx::query("UPDATE tasks SET description = ? WHERE id = ?")
|
|
334 |
+ |
.bind("renamed after linking")
|
|
335 |
+ |
.bind(linked_task_id.to_string())
|
|
336 |
+ |
.execute(&pool)
|
|
337 |
+ |
.await
|
|
338 |
+ |
.unwrap();
|
|
339 |
+ |
|
|
340 |
+ |
let subtasks = repo
|
|
341 |
+ |
.get_subtasks_for_task(parent_task_id)
|
|
342 |
+ |
.await
|
|
343 |
+ |
.expect("Failed to get subtasks");
|
|
344 |
+ |
assert_eq!(subtasks.len(), 1);
|
|
345 |
+ |
assert_eq!(
|
|
346 |
+ |
subtasks[0].text, "renamed after linking",
|
|
347 |
+ |
"Linked subtask should read the linked task's current description"
|
|
348 |
+ |
);
|
|
349 |
+ |
|
|
350 |
+ |
// Listing uses a separate batch query that drifted the same way.
|
|
351 |
+ |
let listed = repo.list_all(user_id).await.expect("Failed to list tasks");
|
|
352 |
+ |
let parent = listed
|
|
353 |
+ |
.iter()
|
|
354 |
+ |
.find(|t| t.id == parent_task_id)
|
|
355 |
+ |
.expect("parent present");
|
|
356 |
+ |
assert_eq!(
|
|
357 |
+ |
parent.subtasks[0].text, "renamed after linking",
|
|
358 |
+ |
"Batch read should resolve the same as the single read"
|
|
359 |
+ |
);
|
|
360 |
+ |
}
|
|
361 |
+ |
|
|
362 |
+ |
#[tokio::test]
|
|
363 |
+ |
async fn test_linked_subtask_falls_back_to_stored_text_when_task_is_gone() {
|
|
364 |
+ |
let pool = common::setup_test_db().await;
|
|
365 |
+ |
let user_id = common::create_test_user(&pool).await;
|
|
366 |
+ |
let parent_task_id = common::create_test_task(&pool, user_id).await;
|
|
367 |
+ |
let linked_task_id = common::create_test_task(&pool, user_id).await;
|
|
368 |
+ |
|
|
369 |
+ |
let repo = SqliteTaskRepository::new(pool.clone());
|
|
370 |
+ |
let linked = repo
|
|
371 |
+ |
.add_subtask_link(parent_task_id, user_id, linked_task_id)
|
|
372 |
+ |
.await
|
|
373 |
+ |
.expect("Failed to add subtask link")
|
|
374 |
+ |
.expect("Should return Some for valid link");
|
|
375 |
+ |
let text_at_link_time = linked.text.clone();
|
|
376 |
+ |
|
|
377 |
+ |
sqlx::query("DELETE FROM tasks WHERE id = ?")
|
|
378 |
+ |
.bind(linked_task_id.to_string())
|
|
379 |
+ |
.execute(&pool)
|
|
380 |
+ |
.await
|
|
381 |
+ |
.unwrap();
|
|
382 |
+ |
|
|
383 |
+ |
let subtasks = repo
|
|
384 |
+ |
.get_subtasks_for_task(parent_task_id)
|
|
385 |
+ |
.await
|
|
386 |
+ |
.expect("Failed to get subtasks");
|
|
387 |
+ |
assert_eq!(subtasks.len(), 1, "Dangling link should still be listed");
|
|
388 |
+ |
assert_eq!(
|
|
389 |
+ |
subtasks[0].text, text_at_link_time,
|
|
390 |
+ |
"A dangling link should read as the task it used to name, not empty"
|
|
391 |
+ |
);
|
|
392 |
+ |
}
|