agentmux_srv/server/service/
window_query.rs1use crate::backend::obj::*;
9use crate::backend::service::{self, WebCallType, WebReturnType};
10
11use super::super::AppState;
12
13pub(crate) async fn handle_get_window(state: &AppState, call: &WebCallType) -> WebReturnType {
14 let store = &state.mstore;
15 let args = &call.args;
16 let window_id: String = match service::get_arg(args, 0) {
17 Ok(v) => v,
18 Err(e) => return WebReturnType::error(e),
19 };
20 match store.must_get::<Window>(&window_id) {
21 Ok(win) => WebReturnType::success(serde_json::to_value(&win).unwrap_or_default()),
22 Err(e) => WebReturnType::error(e.to_string()),
23 }
24}
25
26pub(crate) async fn handle_find_window_by_label(state: &AppState, call: &WebCallType) -> WebReturnType {
34 let store = &state.mstore;
35 let args = &call.args;
36 let label: String = match service::get_arg(args, 0) {
37 Ok(v) => v,
38 Err(e) => return WebReturnType::error(e),
39 };
40 if label.is_empty() {
41 return WebReturnType::error("FindWindowByLabel: empty label".to_string());
42 }
43 match store.get_all::<Window>() {
44 Ok(wins) => {
45 let ids: Vec<String> = wins
46 .into_iter()
47 .filter(|w| {
48 w.meta.get("host:label").and_then(|v| v.as_str())
49 == Some(label.as_str())
50 })
51 .map(|w| w.oid)
52 .collect();
53 WebReturnType::success(serde_json::json!(ids))
54 }
55 Err(e) => WebReturnType::error(e.to_string()),
56 }
57}
58
59#[cfg(test)]
60mod window_label_crumb_tests {
61 use super::super::window::handle_window_service;
62 use crate::backend::obj::Window;
63 use crate::backend::service::WebCallType;
64 use crate::server::tests::test_state;
65
66 fn create_call(workspace_id: &str, label: Option<&str>) -> WebCallType {
67 let mut args = vec![
68 serde_json::Value::Null,
69 serde_json::Value::String(workspace_id.to_string()),
70 ];
71 if let Some(l) = label {
72 args.push(serde_json::Value::String(l.to_string()));
73 }
74 WebCallType {
75 service: "window".to_string(),
76 method: "CreateWindow".to_string(),
77 uicontext: None,
78 args,
79 }
80 }
81
82 fn find_call(label: &str) -> WebCallType {
83 WebCallType {
84 service: "window".to_string(),
85 method: "FindWindowByLabel".to_string(),
86 uicontext: None,
87 args: vec![serde_json::Value::String(label.to_string())],
88 }
89 }
90
91 #[tokio::test]
95 async fn crumb_written_at_create_and_resolvable_by_label() {
96 let state = test_state();
97 let ret = handle_window_service(
98 &state,
99 &create_call("", Some("window-pool-crumbtest1")),
100 )
101 .await;
102 assert!(ret.success, "CreateWindow failed: {:?}", ret.error);
103 let created_id = ret
104 .data
105 .as_ref()
106 .and_then(|d| d.get("oid"))
107 .and_then(|v| v.as_str())
108 .expect("CreateWindow returns the Window with oid")
109 .to_string();
110
111 let row = state
113 .mstore
114 .must_get::<Window>(&created_id)
115 .expect("created window row exists");
116 assert_eq!(
117 row.meta.get("host:label").and_then(|v| v.as_str()),
118 Some("window-pool-crumbtest1"),
119 "host:label crumb missing from the Window row meta"
120 );
121
122 let found = handle_window_service(&state, &find_call("window-pool-crumbtest1")).await;
124 assert!(found.success, "FindWindowByLabel failed: {:?}", found.error);
125 let ids: Vec<String> =
126 serde_json::from_value(found.data.expect("find returns ids")).unwrap();
127 assert_eq!(ids, vec![created_id]);
128 }
129
130 #[tokio::test]
133 async fn two_arg_create_window_writes_no_crumb() {
134 let state = test_state();
135 let ret = handle_window_service(&state, &create_call("", None)).await;
136 assert!(ret.success, "CreateWindow failed: {:?}", ret.error);
137 let created_id = ret
138 .data
139 .as_ref()
140 .and_then(|d| d.get("oid"))
141 .and_then(|v| v.as_str())
142 .unwrap()
143 .to_string();
144 let row = state.mstore.must_get::<Window>(&created_id).unwrap();
145 assert!(
146 !row.meta.contains_key("host:label"),
147 "two-arg CreateWindow must not invent a crumb"
148 );
149 }
150
151 #[tokio::test]
154 async fn unknown_label_returns_empty_not_error() {
155 let state = test_state();
156 let found = handle_window_service(&state, &find_call("window-never-existed")).await;
157 assert!(found.success);
158 let ids: Vec<String> =
159 serde_json::from_value(found.data.expect("find returns ids")).unwrap();
160 assert!(ids.is_empty());
161 }
162
163 #[tokio::test]
166 async fn empty_label_is_an_error() {
167 let state = test_state();
168 let found = handle_window_service(&state, &find_call("")).await;
169 assert!(!found.success);
170 }
171}