Skip to main content

max / goingson

6.7 KB · 228 lines History Blame Raw
1 //! Integration tests for time tracking commands.
2 //!
3 //! Tests start/stop/discard timer and session listing through the repository
4 //! layer, since time tracking commands are thin wrappers that require Tauri `State<'_>`.
5
6 use chrono::{Duration, Utc};
7
8 use goingson_core::{NewTask, Priority};
9
10 use crate::test_utils::setup_test_state;
11
12 // ============ Start Timer ============
13
14 #[tokio::test]
15 async fn test_start_timer_success() {
16 let (state, user_id) = setup_test_state().await;
17
18 let new_task = NewTask::builder("Timed task")
19 .priority(Priority::Medium)
20 .build();
21 let task = state.tasks.create(user_id, new_task).await.unwrap();
22
23 let session = state.tasks.start_timer(task.id, user_id).await.unwrap();
24 assert_eq!(session.task_id, task.id);
25 assert!(session.is_active());
26 assert!(session.ended_at.is_none());
27 assert!(session.duration_minutes.is_none());
28 }
29
30 #[tokio::test]
31 async fn test_start_timer_conflict() {
32 let (state, user_id) = setup_test_state().await;
33
34 let task1 = NewTask::builder("First task")
35 .priority(Priority::Medium)
36 .build();
37 let task1 = state.tasks.create(user_id, task1).await.unwrap();
38
39 let task2 = NewTask::builder("Second task")
40 .priority(Priority::Medium)
41 .build();
42 let task2 = state.tasks.create(user_id, task2).await.unwrap();
43
44 // Start timer on first task
45 state.tasks.start_timer(task1.id, user_id).await.unwrap();
46
47 // Starting a timer on second task should fail (only one active timer allowed)
48 let result = state.tasks.start_timer(task2.id, user_id).await;
49 assert!(result.is_err());
50 }
51
52 // ============ Stop Timer ============
53
54 #[tokio::test]
55 async fn test_stop_timer_success() {
56 let (state, user_id) = setup_test_state().await;
57
58 let new_task = NewTask::builder("Task to time")
59 .priority(Priority::High)
60 .build();
61 let task = state.tasks.create(user_id, new_task).await.unwrap();
62
63 // Start and then stop the timer
64 state.tasks.start_timer(task.id, user_id).await.unwrap();
65 let stopped = state.tasks.stop_timer(task.id, user_id).await.unwrap();
66
67 assert!(stopped.is_some());
68 let session = stopped.unwrap();
69 assert_eq!(session.task_id, task.id);
70 assert!(session.ended_at.is_some());
71 assert!(!session.is_active());
72 }
73
74 #[tokio::test]
75 async fn test_stop_timer_no_active_session() {
76 let (state, user_id) = setup_test_state().await;
77
78 let new_task = NewTask::builder("Never timed")
79 .priority(Priority::Low)
80 .build();
81 let task = state.tasks.create(user_id, new_task).await.unwrap();
82
83 // Stopping a timer that was never started returns None
84 let stopped = state.tasks.stop_timer(task.id, user_id).await.unwrap();
85 assert!(stopped.is_none());
86 }
87
88 // ============ Get Active Timer ============
89
90 #[tokio::test]
91 async fn test_get_active_timer_none() {
92 let (state, user_id) = setup_test_state().await;
93
94 // No tasks, no timers
95 let active = state.tasks.get_active_timer(user_id).await.unwrap();
96 assert!(active.is_none());
97 }
98
99 #[tokio::test]
100 async fn test_get_active_timer_some() {
101 let (state, user_id) = setup_test_state().await;
102
103 let new_task = NewTask::builder("Active timer task")
104 .priority(Priority::Medium)
105 .build();
106 let task = state.tasks.create(user_id, new_task).await.unwrap();
107
108 state.tasks.start_timer(task.id, user_id).await.unwrap();
109
110 let active = state.tasks.get_active_timer(user_id).await.unwrap();
111 assert!(active.is_some());
112
113 let (session, description) = active.unwrap();
114 assert_eq!(session.task_id, task.id);
115 assert!(session.is_active());
116 assert_eq!(description, "Active timer task");
117 }
118
119 #[tokio::test]
120 async fn test_get_active_timer_after_stop() {
121 let (state, user_id) = setup_test_state().await;
122
123 let new_task = NewTask::builder("Start then stop")
124 .priority(Priority::Medium)
125 .build();
126 let task = state.tasks.create(user_id, new_task).await.unwrap();
127
128 state.tasks.start_timer(task.id, user_id).await.unwrap();
129 state.tasks.stop_timer(task.id, user_id).await.unwrap();
130
131 // After stopping, no active timer
132 let active = state.tasks.get_active_timer(user_id).await.unwrap();
133 assert!(active.is_none());
134 }
135
136 // ============ Discard Timer ============
137
138 #[tokio::test]
139 async fn test_discard_timer_success() {
140 let (state, user_id) = setup_test_state().await;
141
142 let new_task = NewTask::builder("Discard me")
143 .priority(Priority::Low)
144 .build();
145 let task = state.tasks.create(user_id, new_task).await.unwrap();
146
147 state.tasks.start_timer(task.id, user_id).await.unwrap();
148
149 let discarded = state.tasks.discard_timer(task.id, user_id).await.unwrap();
150 assert!(discarded);
151
152 // No active timer after discard
153 let active = state.tasks.get_active_timer(user_id).await.unwrap();
154 assert!(active.is_none());
155 }
156
157 #[tokio::test]
158 async fn test_discard_timer_no_active() {
159 let (state, user_id) = setup_test_state().await;
160
161 let new_task = NewTask::builder("Not timed")
162 .priority(Priority::Medium)
163 .build();
164 let task = state.tasks.create(user_id, new_task).await.unwrap();
165
166 // Discarding when no timer is active returns false
167 let discarded = state.tasks.discard_timer(task.id, user_id).await.unwrap();
168 assert!(!discarded);
169 }
170
171 // ============ List Time Sessions ============
172
173 #[tokio::test]
174 async fn test_list_time_sessions_empty() {
175 let (state, user_id) = setup_test_state().await;
176
177 let new_task = NewTask::builder("No sessions")
178 .priority(Priority::Medium)
179 .build();
180 let task = state.tasks.create(user_id, new_task).await.unwrap();
181
182 let sessions = state
183 .tasks
184 .list_time_sessions(task.id, user_id)
185 .await
186 .unwrap();
187 assert!(sessions.is_empty());
188 }
189
190 #[tokio::test]
191 async fn test_list_time_sessions_after_start_stop() {
192 let (state, user_id) = setup_test_state().await;
193
194 let new_task = NewTask::builder("Tracked task")
195 .priority(Priority::Medium)
196 .build();
197 let task = state.tasks.create(user_id, new_task).await.unwrap();
198
199 // Complete one session
200 state.tasks.start_timer(task.id, user_id).await.unwrap();
201 state.tasks.stop_timer(task.id, user_id).await.unwrap();
202
203 let sessions = state
204 .tasks
205 .list_time_sessions(task.id, user_id)
206 .await
207 .unwrap();
208 assert_eq!(sessions.len(), 1);
209 assert!(sessions[0].ended_at.is_some());
210 }
211
212 // ============ Time Summary ============
213
214 #[tokio::test]
215 async fn test_get_time_summary_empty() {
216 let (state, user_id) = setup_test_state().await;
217
218 let start = Utc::now() - Duration::days(7);
219 let end = Utc::now();
220
221 let summaries = state
222 .tasks
223 .get_time_summary(user_id, start, end)
224 .await
225 .unwrap();
226 assert!(summaries.is_empty());
227 }
228