1use crate::ipc::{RpcRequest, RpcResponse};
8use serde_json::json;
9
10use super::usecase_container::UseCaseContainer;
11use crate::daemon::handlers;
12use crate::daemon::repository::SessionRepository;
13
14pub struct Router<'a, R: SessionRepository + 'static> {
23 usecases: &'a UseCaseContainer<R>,
24}
25
26impl<'a, R: SessionRepository + 'static> Router<'a, R> {
27 pub fn new(usecases: &'a UseCaseContainer<R>) -> Self {
29 Self { usecases }
30 }
31
32 pub fn route(&self, request: RpcRequest) -> RpcResponse {
37 match request.method.as_str() {
38 "ping" => RpcResponse::success(request.id, json!({ "pong": true })),
39
40 "health" => {
41 handlers::diagnostics::handle_health_uc(&self.usecases.diagnostics.health, request)
42 }
43
44 "metrics" => handlers::diagnostics::handle_metrics_uc(
45 &self.usecases.diagnostics.metrics,
46 request,
47 ),
48
49 "spawn" => handlers::session::handle_spawn(&self.usecases.session.spawn, request),
51 "kill" => handlers::session::handle_kill(&self.usecases.session.kill, request),
52 "restart" => handlers::session::handle_restart(&self.usecases.session.restart, request),
53 "sessions" => {
54 handlers::session::handle_sessions(&self.usecases.session.sessions, request)
55 }
56 "resize" => handlers::session::handle_resize(&self.usecases.session.resize, request),
57 "attach" => handlers::session::handle_attach(&self.usecases.session.attach, request),
58 "cleanup" => handlers::session::handle_cleanup(&self.usecases.session.cleanup, request),
59 "assert" => handlers::session::handle_assert(&self.usecases.session.assert, request),
60
61 "snapshot" => {
63 handlers::elements::handle_snapshot_uc(&self.usecases.elements.snapshot, request)
64 }
65 "accessibility_snapshot" => handlers::elements::handle_accessibility_snapshot_uc(
66 &self.usecases.elements.accessibility_snapshot,
67 request,
68 ),
69 "click" => handlers::elements::handle_click_uc(&self.usecases.elements.click, request),
70 "dbl_click" => {
71 handlers::elements::handle_dbl_click_uc(&self.usecases.elements.dbl_click, request)
72 }
73 "fill" => handlers::elements::handle_fill_uc(&self.usecases.elements.fill, request),
74 "find" => handlers::elements::handle_find_uc(&self.usecases.elements.find, request),
75 "count" => handlers::elements::handle_count_uc(&self.usecases.elements.count, request),
76 "scroll" => {
77 handlers::elements::handle_scroll_uc(&self.usecases.elements.scroll, request)
78 }
79 "scroll_into_view" => handlers::elements::handle_scroll_into_view_uc(
80 &self.usecases.elements.scroll_into_view,
81 request,
82 ),
83 "get_text" => {
84 handlers::elements::handle_get_text_uc(&self.usecases.elements.get_text, request)
85 }
86 "get_value" => {
87 handlers::elements::handle_get_value_uc(&self.usecases.elements.get_value, request)
88 }
89 "is_visible" => handlers::elements::handle_is_visible_uc(
90 &self.usecases.elements.is_visible,
91 request,
92 ),
93 "is_focused" => handlers::elements::handle_is_focused_uc(
94 &self.usecases.elements.is_focused,
95 request,
96 ),
97 "is_enabled" => handlers::elements::handle_is_enabled_uc(
98 &self.usecases.elements.is_enabled,
99 request,
100 ),
101 "is_checked" => handlers::elements::handle_is_checked_uc(
102 &self.usecases.elements.is_checked,
103 request,
104 ),
105 "get_focused" => handlers::elements::handle_get_focused_uc(
106 &self.usecases.elements.get_focused,
107 request,
108 ),
109 "get_title" => {
110 handlers::elements::handle_get_title_uc(&self.usecases.elements.get_title, request)
111 }
112 "focus" => handlers::elements::handle_focus_uc(&self.usecases.elements.focus, request),
113 "clear" => handlers::elements::handle_clear_uc(&self.usecases.elements.clear, request),
114 "select_all" => handlers::elements::handle_select_all_uc(
115 &self.usecases.elements.select_all,
116 request,
117 ),
118 "toggle" => {
119 handlers::elements::handle_toggle_uc(&self.usecases.elements.toggle, request)
120 }
121 "select" => {
122 handlers::elements::handle_select_uc(&self.usecases.elements.select, request)
123 }
124 "multiselect" => handlers::elements::handle_multiselect_uc(
125 &self.usecases.elements.multiselect,
126 request,
127 ),
128
129 "keystroke" => {
131 handlers::input::handle_keystroke_uc(&self.usecases.input.keystroke, request)
132 }
133 "keydown" => handlers::input::handle_keydown_uc(&self.usecases.input.keydown, request),
134 "keyup" => handlers::input::handle_keyup_uc(&self.usecases.input.keyup, request),
135 "type" => handlers::input::handle_type_uc(&self.usecases.input.type_text, request),
136
137 "wait" => handlers::wait::handle_wait_uc(&self.usecases.wait, request),
139
140 "record_start" => handlers::recording::handle_record_start_uc(
142 &self.usecases.recording.record_start,
143 request,
144 ),
145 "record_stop" => handlers::recording::handle_record_stop_uc(
146 &self.usecases.recording.record_stop,
147 request,
148 ),
149 "record_status" => handlers::recording::handle_record_status_uc(
150 &self.usecases.recording.record_status,
151 request,
152 ),
153
154 "trace" => {
156 handlers::diagnostics::handle_trace_uc(&self.usecases.diagnostics.trace, request)
157 }
158 "console" => handlers::diagnostics::handle_console_uc(
159 &self.usecases.diagnostics.console,
160 request,
161 ),
162 "errors" => {
163 handlers::diagnostics::handle_errors_uc(&self.usecases.diagnostics.errors, request)
164 }
165 "pty_read" => handlers::diagnostics::handle_pty_read_uc(
166 &self.usecases.diagnostics.pty_read,
167 request,
168 ),
169 "pty_write" => handlers::diagnostics::handle_pty_write_uc(
170 &self.usecases.diagnostics.pty_write,
171 request,
172 ),
173
174 "screen" => RpcResponse::error(
175 request.id,
176 -32601,
177 "Method 'screen' is deprecated. Use 'snapshot' with strip_ansi=true instead.",
178 ),
179
180 _ => RpcResponse::error(
181 request.id,
182 -32601,
183 &format!("Method not found: {}", request.method),
184 ),
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use crate::daemon::metrics::DaemonMetrics;
193 use crate::daemon::session::SessionManager;
194 use std::sync::Arc;
195 use std::sync::atomic::AtomicUsize;
196 use std::time::Instant;
197
198 fn create_test_usecases() -> UseCaseContainer<SessionManager> {
199 let session_manager = Arc::new(SessionManager::new());
200 let metrics = Arc::new(DaemonMetrics::new());
201 let start_time = Instant::now();
202 let active_connections = Arc::new(AtomicUsize::new(0));
203 UseCaseContainer::new(session_manager, metrics, start_time, active_connections)
204 }
205
206 #[test]
207 fn test_router_ping_returns_pong() {
208 let usecases = create_test_usecases();
209 let router = Router::new(&usecases);
210
211 let request = RpcRequest::new(1, "ping".to_string(), None);
212 let response = router.route(request);
213
214 let json_str = serde_json::to_string(&response).unwrap();
215 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
216
217 assert!(parsed.get("error").is_none() || parsed["error"].is_null());
218 assert_eq!(parsed["result"]["pong"], true);
219 }
220
221 #[test]
222 fn test_router_unknown_method_returns_error() {
223 let usecases = create_test_usecases();
224 let router = Router::new(&usecases);
225
226 let request = RpcRequest::new(1, "nonexistent_method".to_string(), None);
227 let response = router.route(request);
228
229 let json_str = serde_json::to_string(&response).unwrap();
230 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
231
232 assert!(parsed.get("error").is_some());
233 assert_eq!(parsed["error"]["code"], -32601);
234 assert!(
235 parsed["error"]["message"]
236 .as_str()
237 .unwrap()
238 .contains("nonexistent_method")
239 );
240 }
241
242 #[test]
243 fn test_router_deprecated_screen_returns_error() {
244 let usecases = create_test_usecases();
245 let router = Router::new(&usecases);
246
247 let request = RpcRequest::new(1, "screen".to_string(), None);
248 let response = router.route(request);
249
250 let json_str = serde_json::to_string(&response).unwrap();
251 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
252
253 assert!(parsed.get("error").is_some());
254 assert_eq!(parsed["error"]["code"], -32601);
255 assert!(
256 parsed["error"]["message"]
257 .as_str()
258 .unwrap()
259 .contains("deprecated")
260 );
261 }
262
263 #[test]
264 fn test_router_health_returns_success() {
265 let usecases = create_test_usecases();
266 let router = Router::new(&usecases);
267
268 let request = RpcRequest::new(1, "health".to_string(), None);
269 let response = router.route(request);
270
271 let json_str = serde_json::to_string(&response).unwrap();
272 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
273
274 assert!(parsed.get("error").is_none() || parsed["error"].is_null());
275 assert!(parsed.get("result").is_some());
276 assert_eq!(parsed["result"]["status"], "healthy");
277 }
278
279 #[test]
280 fn test_router_sessions_returns_empty_list() {
281 let usecases = create_test_usecases();
282 let router = Router::new(&usecases);
283
284 let request = RpcRequest::new(1, "sessions".to_string(), None);
285 let response = router.route(request);
286
287 let json_str = serde_json::to_string(&response).unwrap();
288 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
289
290 assert!(parsed.get("error").is_none() || parsed["error"].is_null());
291 assert!(parsed["result"]["sessions"].is_array());
292 }
293
294 #[test]
295 fn test_router_cleanup_returns_success() {
296 let usecases = create_test_usecases();
297 let router = Router::new(&usecases);
298
299 let request = RpcRequest::new(1, "cleanup".to_string(), None);
300 let response = router.route(request);
301
302 let json_str = serde_json::to_string(&response).unwrap();
303 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
304
305 assert!(parsed.get("error").is_none() || parsed["error"].is_null());
306 assert!(parsed.get("result").is_some());
307 assert_eq!(parsed["result"]["sessions_cleaned"], 0);
308 assert_eq!(parsed["result"]["sessions_failed"], 0);
309 assert!(parsed["result"]["failures"].is_array());
310 }
311
312 #[test]
313 fn test_router_assert_invalid_condition_returns_error() {
314 let usecases = create_test_usecases();
315 let router = Router::new(&usecases);
316
317 let request = RpcRequest::new(
319 1,
320 "assert".to_string(),
321 Some(json!({ "condition": "invalid" })),
322 );
323 let response = router.route(request);
324
325 let json_str = serde_json::to_string(&response).unwrap();
326 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
327
328 assert!(parsed.get("error").is_some());
329 assert_eq!(parsed["error"]["code"], -32602);
330 assert!(
331 parsed["error"]["message"]
332 .as_str()
333 .unwrap()
334 .contains("Invalid condition format")
335 );
336 }
337
338 #[test]
339 fn test_router_assert_session_condition_not_found() {
340 let usecases = create_test_usecases();
341 let router = Router::new(&usecases);
342
343 let request = RpcRequest::new(
345 1,
346 "assert".to_string(),
347 Some(json!({ "condition": "session:nonexistent" })),
348 );
349 let response = router.route(request);
350
351 let json_str = serde_json::to_string(&response).unwrap();
352 let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
353
354 assert!(parsed.get("error").is_none() || parsed["error"].is_null());
355 assert_eq!(parsed["result"]["passed"], false);
356 assert_eq!(parsed["result"]["condition"], "session:nonexistent");
357 }
358}