agentmux_srv/reducer/
snapshot.rs1use agentmux_common::ipc::Event;
5
6use crate::state::State;
7
8
9pub(super) fn handle_get_srv_snapshot(state: &mut State) -> Vec<Event> {
10 let v = state.bump_version();
11 let mut workspaces: Vec<(String, String)> = state
12 .workspaces
13 .values()
14 .map(|w| (w.workspace_id.clone(), w.name.clone()))
15 .collect();
16 workspaces.sort_by(|a, b| a.0.cmp(&b.0));
19 let mut tabs: Vec<(String, String, String)> = state
20 .tabs
21 .values()
22 .map(|t| (t.tab_id.clone(), t.workspace_id.clone(), t.name.clone()))
23 .collect();
24 tabs.sort_by(|a, b| a.0.cmp(&b.0));
25 let mut active_tabs: Vec<(String, String)> = state
26 .workspaces
27 .values()
28 .filter_map(|w| {
29 w.active_tab_id
30 .as_ref()
31 .map(|t| (w.workspace_id.clone(), t.clone()))
32 })
33 .collect();
34 active_tabs.sort_by(|a, b| a.0.cmp(&b.0));
35 let mut blocks: Vec<(String, String)> = state
36 .blocks
37 .values()
38 .map(|b| (b.block_id.clone(), b.tab_id.clone()))
39 .collect();
40 blocks.sort_by(|a, b| a.0.cmp(&b.0));
41 vec![Event::SrvSnapshot {
42 version: v,
43 lifecycle: state.lifecycle,
44 workspaces,
45 tabs,
46 active_tabs,
47 blocks,
48 }]
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::reducer::test_support::*;
55 use crate::reducer::update;
56 use agentmux_common::ipc::Command;
57
58 #[test]
59 fn get_srv_snapshot_returns_lifecycle_and_bumps_version() {
60 let mut state = State::default();
61 let v0 = state.event_version;
62 let events = update(&mut state, Command::GetSrvSnapshot, &ctx(1));
63 assert_eq!(events.len(), 1);
64 let Event::SrvSnapshot { version, lifecycle, .. } = events[0].clone() else {
65 panic!("expected SrvSnapshot, got {:?}", events[0]);
66 };
67 assert_eq!(lifecycle, agentmux_common::ipc::LifecyclePhase::Starting);
68 assert!(version > v0);
69 }
70
71 #[test]
72 fn snapshot_includes_workspaces_sorted_by_id() {
73 let mut state = State::default();
74 let _ = update(
75 &mut state,
76 Command::CreateWorkspace { name: "a".into() },
77 &ctx(1),
78 );
79 let _ = update(
80 &mut state,
81 Command::CreateWorkspace { name: "b".into() },
82 &ctx(2),
83 );
84 let events = update(&mut state, Command::GetSrvSnapshot, &ctx(3));
85 let Event::SrvSnapshot { workspaces, .. } = &events[0] else {
86 panic!();
87 };
88 assert_eq!(workspaces.len(), 2);
89 assert!(workspaces[0].0 < workspaces[1].0);
91 }
92
93 #[test]
94 fn snapshot_includes_blocks() {
95 let mut state = State::default();
96 let ws_id = create_workspace(&mut state, "w");
97 let tab_id = create_tab(&mut state, &ws_id, "t");
98 let _ = update(
99 &mut state,
100 Command::CreateBlock { tab_id, meta: serde_json::Value::Null },
101 &ctx(2),
102 );
103 let events = update(&mut state, Command::GetSrvSnapshot, &ctx(3));
104 let Event::SrvSnapshot { blocks, .. } = &events[0] else {
105 panic!("expected SrvSnapshot");
106 };
107 assert_eq!(blocks.len(), 1);
108 }
109
110 #[test]
111 fn snapshot_includes_tabs_and_active_tabs() {
112 let mut state = State::default();
113 let ws_id = create_workspace(&mut state, "w");
114 let _ = update(
115 &mut state,
116 Command::CreateTab {
117 workspace_id: ws_id.clone(),
118 name: "t1".into(),
119 },
120 &ctx(1),
121 );
122 let events = update(&mut state, Command::GetSrvSnapshot, &ctx(2));
123 let Event::SrvSnapshot {
124 tabs, active_tabs, ..
125 } = &events[0]
126 else {
127 panic!("expected SrvSnapshot");
128 };
129 assert_eq!(tabs.len(), 1);
130 assert_eq!(active_tabs.len(), 1);
131 assert_eq!(active_tabs[0].0, ws_id);
132 }
133}