Skip to main content

max / goingson

6.6 KB · 142 lines History Blame Raw
1 //! Integration tests for status-token methods on SqliteTaskRepository.
2
3 mod common;
4
5 use goingson_core::{StatusToken, TaskAnnotations, TaskCrud, TaskId, TaskStatus, TokenState, TOKEN_KIND_COMMIT};
6 use goingson_db_sqlite::SqliteTaskRepository;
7
8 const COMMIT: &str = TOKEN_KIND_COMMIT;
9
10 #[tokio::test]
11 async fn record_token_appends_and_reads_back() {
12 let pool = common::setup_test_db().await;
13 let user_id = common::create_test_user(&pool).await;
14 let task_id = common::create_test_task(&pool, user_id).await;
15 let repo = SqliteTaskRepository::new(pool);
16
17 let t = repo
18 .record_status_token(task_id, user_id, COMMIT, "deox@7c236fca8", TokenState::Pending, false)
19 .await
20 .expect("record")
21 .expect("some");
22 assert_eq!(t.task_id, task_id);
23 assert_eq!(t.kind, COMMIT);
24 assert_eq!(t.reference, "deox@7c236fca8");
25 assert_eq!(t.state, TokenState::Pending);
26 assert!(!t.is_primary);
27 assert_eq!(t.position, 0);
28
29 let tokens = repo.get_status_tokens_for_task(task_id).await.expect("get");
30 assert_eq!(tokens.len(), 1);
31 assert_eq!(tokens[0].id, t.id);
32 }
33
34 #[tokio::test]
35 async fn record_token_is_idempotent_in_task_kind_ref() {
36 let pool = common::setup_test_db().await;
37 let user_id = common::create_test_user(&pool).await;
38 let task_id = common::create_test_task(&pool, user_id).await;
39 let repo = SqliteTaskRepository::new(pool);
40
41 let a = repo.record_status_token(task_id, user_id, COMMIT, "deox@aaa", TokenState::Pending, false).await.unwrap().unwrap();
42 // Re-record the same token, flipping state to Complete (e.g. it was pushed).
43 let b = repo.record_status_token(task_id, user_id, COMMIT, "deox@aaa", TokenState::Complete, false).await.unwrap().unwrap();
44 assert_eq!(a.id, b.id, "same (task, kind, ref) collapses to one deterministic id");
45
46 let tokens = repo.get_status_tokens_for_task(task_id).await.unwrap();
47 assert_eq!(tokens.len(), 1, "no duplicate row on re-record");
48 assert_eq!(tokens[0].state, TokenState::Complete, "state updated in place");
49 assert_eq!(tokens[0].position, 0);
50 }
51
52 #[tokio::test]
53 async fn record_token_preserves_append_order() {
54 let pool = common::setup_test_db().await;
55 let user_id = common::create_test_user(&pool).await;
56 let task_id = common::create_test_task(&pool, user_id).await;
57 let repo = SqliteTaskRepository::new(pool);
58
59 repo.record_status_token(task_id, user_id, COMMIT, "deox@one", TokenState::Pending, false).await.unwrap();
60 repo.record_status_token(task_id, user_id, COMMIT, "deox@two", TokenState::Pending, false).await.unwrap();
61 repo.record_status_token(task_id, user_id, COMMIT, "deox@three", TokenState::Pending, false).await.unwrap();
62
63 let tokens = repo.get_status_tokens_for_task(task_id).await.unwrap();
64 let refs: Vec<&str> = tokens.iter().map(|t| t.reference.as_str()).collect();
65 assert_eq!(refs, vec!["deox@one", "deox@two", "deox@three"]);
66 }
67
68 #[tokio::test]
69 async fn primary_flag_is_exclusive_and_can_promote_existing() {
70 let pool = common::setup_test_db().await;
71 let user_id = common::create_test_user(&pool).await;
72 let task_id = common::create_test_task(&pool, user_id).await;
73 let repo = SqliteTaskRepository::new(pool);
74
75 repo.record_status_token(task_id, user_id, COMMIT, "deox@one", TokenState::Pending, false).await.unwrap();
76 repo.record_status_token(task_id, user_id, COMMIT, "deox@two", TokenState::Pending, false).await.unwrap();
77 repo.record_status_token(task_id, user_id, COMMIT, "deox@three", TokenState::Complete, true).await.unwrap();
78
79 let commits = repo.get_status_tokens_for_task(task_id).await.unwrap();
80 let primaries: Vec<&str> = commits.iter().filter(|t| t.is_primary).map(|t| t.reference.as_str()).collect();
81 assert_eq!(primaries, vec!["deox@three"], "exactly one primary");
82
83 // Promote an existing token to primary; the prior primary flag clears.
84 repo.record_status_token(task_id, user_id, COMMIT, "deox@one", TokenState::Pending, true).await.unwrap();
85 let commits = repo.get_status_tokens_for_task(task_id).await.unwrap();
86 let primary_refs: Vec<&str> = commits.iter().filter(|t| t.is_primary).map(|t| t.reference.as_str()).collect();
87 assert_eq!(primary_refs, vec!["deox@one"], "primary moved, still exactly one");
88 assert_eq!(commits.len(), 3, "promotion did not add a row");
89 }
90
91 #[tokio::test]
92 async fn delete_token_removes_only_that_token() {
93 let pool = common::setup_test_db().await;
94 let user_id = common::create_test_user(&pool).await;
95 let task_id = common::create_test_task(&pool, user_id).await;
96 let repo = SqliteTaskRepository::new(pool);
97
98 let keep = repo.record_status_token(task_id, user_id, COMMIT, "deox@keep", TokenState::Pending, false).await.unwrap().unwrap();
99 let drop = repo.record_status_token(task_id, user_id, COMMIT, "deox@drop", TokenState::Pending, false).await.unwrap().unwrap();
100
101 let removed = repo.delete_status_token(drop.id, user_id).await.unwrap();
102 assert!(removed);
103
104 let tokens = repo.get_status_tokens_for_task(task_id).await.unwrap();
105 assert_eq!(tokens.len(), 1);
106 assert_eq!(tokens[0].id, keep.id);
107
108 // Deleting an unknown token is a no-op, not an error.
109 assert!(!repo.delete_status_token(StatusToken::deterministic_id(task_id, COMMIT, "deox@nope"), user_id).await.unwrap());
110 }
111
112 #[tokio::test]
113 async fn record_token_rejects_unknown_or_unowned_task() {
114 let pool = common::setup_test_db().await;
115 let user_id = common::create_test_user(&pool).await;
116 let repo = SqliteTaskRepository::new(pool);
117
118 let missing = repo
119 .record_status_token(TaskId::new(), user_id, COMMIT, "deox@x", TokenState::Pending, false)
120 .await
121 .expect("no error");
122 assert!(missing.is_none(), "recording against a missing task returns None");
123 }
124
125 #[tokio::test]
126 async fn tokens_load_onto_the_task_with_rollup() {
127 let pool = common::setup_test_db().await;
128 let user_id = common::create_test_user(&pool).await;
129 let task_id = common::create_test_task(&pool, user_id).await;
130 let repo = SqliteTaskRepository::new(pool);
131
132 repo.record_status_token(task_id, user_id, COMMIT, "deox@one", TokenState::Complete, false).await.unwrap();
133 repo.record_status_token(task_id, user_id, COMMIT, "deox@two", TokenState::Pending, true).await.unwrap();
134
135 let task = repo.get_by_id(task_id, user_id).await.unwrap().unwrap();
136 assert_eq!(task.status_tokens.len(), 2);
137 assert_eq!(task.primary_token().map(|t| t.reference.as_str()), Some("deox@two"));
138 assert_eq!(task.status_token_summary(), "pending", "a pending token keeps rollup pending");
139 // status is unaffected by recording tokens
140 assert_ne!(task.status, TaskStatus::Completed);
141 }
142