1use crate::db::models::Event;
2use crate::error::{IntentError, Result};
3use chrono::Utc;
4use sqlx::SqlitePool;
5
6pub struct EventManager<'a> {
7 pool: &'a SqlitePool,
8}
9
10impl<'a> EventManager<'a> {
11 pub fn new(pool: &'a SqlitePool) -> Self {
12 Self { pool }
13 }
14
15 pub async fn add_event(
17 &self,
18 task_id: i64,
19 log_type: &str,
20 discussion_data: &str,
21 ) -> Result<Event> {
22 let task_exists: bool =
24 sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM tasks WHERE id = ?)")
25 .bind(task_id)
26 .fetch_one(self.pool)
27 .await?;
28
29 if !task_exists {
30 return Err(IntentError::TaskNotFound(task_id));
31 }
32
33 let now = Utc::now();
34
35 let result = sqlx::query(
36 r#"
37 INSERT INTO events (task_id, log_type, discussion_data, timestamp)
38 VALUES (?, ?, ?, ?)
39 "#,
40 )
41 .bind(task_id)
42 .bind(log_type)
43 .bind(discussion_data)
44 .bind(now)
45 .execute(self.pool)
46 .await?;
47
48 let id = result.last_insert_rowid();
49
50 Ok(Event {
51 id,
52 task_id,
53 timestamp: now,
54 log_type: log_type.to_string(),
55 discussion_data: discussion_data.to_string(),
56 })
57 }
58
59 pub async fn list_events(&self, task_id: i64, limit: Option<i64>) -> Result<Vec<Event>> {
61 let task_exists: bool =
63 sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM tasks WHERE id = ?)")
64 .bind(task_id)
65 .fetch_one(self.pool)
66 .await?;
67
68 if !task_exists {
69 return Err(IntentError::TaskNotFound(task_id));
70 }
71
72 let limit = limit.unwrap_or(100);
73
74 let events = sqlx::query_as::<_, Event>(
75 r#"
76 SELECT id, task_id, timestamp, log_type, discussion_data
77 FROM events
78 WHERE task_id = ?
79 ORDER BY timestamp DESC
80 LIMIT ?
81 "#,
82 )
83 .bind(task_id)
84 .bind(limit)
85 .fetch_all(self.pool)
86 .await?;
87
88 Ok(events)
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::tasks::TaskManager;
96 use crate::test_utils::test_helpers::TestContext;
97
98 #[tokio::test]
99 async fn test_add_event() {
100 let ctx = TestContext::new().await;
101 let task_mgr = TaskManager::new(ctx.pool());
102 let event_mgr = EventManager::new(ctx.pool());
103
104 let task = task_mgr.add_task("Test task", None, None).await.unwrap();
105 let event = event_mgr
106 .add_event(task.id, "decision", "Test decision")
107 .await
108 .unwrap();
109
110 assert_eq!(event.task_id, task.id);
111 assert_eq!(event.log_type, "decision");
112 assert_eq!(event.discussion_data, "Test decision");
113 }
114
115 #[tokio::test]
116 async fn test_add_event_nonexistent_task() {
117 let ctx = TestContext::new().await;
118 let event_mgr = EventManager::new(ctx.pool());
119
120 let result = event_mgr.add_event(999, "decision", "Test").await;
121 assert!(matches!(result, Err(IntentError::TaskNotFound(999))));
122 }
123
124 #[tokio::test]
125 async fn test_list_events() {
126 let ctx = TestContext::new().await;
127 let task_mgr = TaskManager::new(ctx.pool());
128 let event_mgr = EventManager::new(ctx.pool());
129
130 let task = task_mgr.add_task("Test task", None, None).await.unwrap();
131
132 event_mgr
134 .add_event(task.id, "decision", "Decision 1")
135 .await
136 .unwrap();
137 event_mgr
138 .add_event(task.id, "blocker", "Blocker 1")
139 .await
140 .unwrap();
141 event_mgr
142 .add_event(task.id, "milestone", "Milestone 1")
143 .await
144 .unwrap();
145
146 let events = event_mgr.list_events(task.id, None).await.unwrap();
147 assert_eq!(events.len(), 3);
148
149 assert_eq!(events[0].log_type, "milestone");
151 assert_eq!(events[1].log_type, "blocker");
152 assert_eq!(events[2].log_type, "decision");
153 }
154
155 #[tokio::test]
156 async fn test_list_events_with_limit() {
157 let ctx = TestContext::new().await;
158 let task_mgr = TaskManager::new(ctx.pool());
159 let event_mgr = EventManager::new(ctx.pool());
160
161 let task = task_mgr.add_task("Test task", None, None).await.unwrap();
162
163 for i in 0..5 {
165 event_mgr
166 .add_event(task.id, "test", &format!("Event {}", i))
167 .await
168 .unwrap();
169 }
170
171 let events = event_mgr.list_events(task.id, Some(3)).await.unwrap();
172 assert_eq!(events.len(), 3);
173 }
174
175 #[tokio::test]
176 async fn test_list_events_nonexistent_task() {
177 let ctx = TestContext::new().await;
178 let event_mgr = EventManager::new(ctx.pool());
179
180 let result = event_mgr.list_events(999, None).await;
181 assert!(matches!(result, Err(IntentError::TaskNotFound(999))));
182 }
183
184 #[tokio::test]
185 async fn test_list_events_empty() {
186 let ctx = TestContext::new().await;
187 let task_mgr = TaskManager::new(ctx.pool());
188 let event_mgr = EventManager::new(ctx.pool());
189
190 let task = task_mgr.add_task("Test task", None, None).await.unwrap();
191
192 let events = event_mgr.list_events(task.id, None).await.unwrap();
193 assert_eq!(events.len(), 0);
194 }
195}