opencode_voice/approval/
queue.rs1use std::collections::VecDeque;
4
5use crate::approval::types::{PendingApproval, PermissionRequest, QuestionRequest};
6
7pub struct ApprovalQueue {
9 items: VecDeque<PendingApproval>,
10}
11
12impl ApprovalQueue {
13 pub fn new() -> Self {
14 ApprovalQueue {
15 items: VecDeque::new(),
16 }
17 }
18
19 pub fn len(&self) -> usize {
21 self.items.len()
22 }
23
24 pub fn has_pending(&self) -> bool {
26 !self.items.is_empty()
27 }
28
29 pub fn peek(&self) -> Option<&PendingApproval> {
31 self.items.front()
32 }
33
34 pub fn add_permission(&mut self, request: PermissionRequest) {
36 self.items.push_back(PendingApproval::Permission(request));
37 }
38
39 pub fn add_question(&mut self, request: QuestionRequest) {
41 self.items.push_back(PendingApproval::Question(request));
42 }
43
44 pub fn clear(&mut self) {
46 self.items.clear();
47 }
48
49 pub fn remove(&mut self, request_id: &str) -> bool {
51 if let Some(pos) = self.items.iter().position(|item| item.id() == request_id) {
52 self.items.remove(pos);
53 true
54 } else {
55 false
56 }
57 }
58}
59
60impl Default for ApprovalQueue {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::approval::types::{PermissionRequest, QuestionRequest};
70
71 fn make_permission(id: &str) -> PermissionRequest {
72 PermissionRequest {
73 id: id.to_string(),
74 permission: "bash".to_string(),
75 metadata: serde_json::Value::Null,
76 }
77 }
78
79 fn make_question(id: &str) -> QuestionRequest {
80 QuestionRequest {
81 id: id.to_string(),
82 questions: vec![],
83 }
84 }
85
86 #[test]
87 fn test_new_queue_is_empty() {
88 let q = ApprovalQueue::new();
89 assert!(!q.has_pending());
90 assert_eq!(q.len(), 0);
91 assert!(q.peek().is_none());
92 }
93
94 #[test]
95 fn test_add_permission_and_peek() {
96 let mut q = ApprovalQueue::new();
97 q.add_permission(make_permission("perm-1"));
98 assert_eq!(q.len(), 1);
99 assert!(q.has_pending());
100 let item = q.peek().unwrap();
101 assert_eq!(item.id(), "perm-1");
102 assert!(matches!(item, PendingApproval::Permission(_)));
103 }
104
105 #[test]
106 fn test_add_question_and_peek() {
107 let mut q = ApprovalQueue::new();
108 q.add_question(make_question("q-1"));
109 assert_eq!(q.len(), 1);
110 let item = q.peek().unwrap();
111 assert_eq!(item.id(), "q-1");
112 assert!(matches!(item, PendingApproval::Question(_)));
113 }
114
115 #[test]
116 fn test_fifo_ordering() {
117 let mut q = ApprovalQueue::new();
118 q.add_permission(make_permission("first"));
119 q.add_question(make_question("second"));
120 q.add_permission(make_permission("third"));
121
122 assert_eq!(q.peek().unwrap().id(), "first");
124 assert_eq!(q.len(), 3);
125 }
126
127 #[test]
128 fn test_remove_found() {
129 let mut q = ApprovalQueue::new();
130 q.add_permission(make_permission("perm-1"));
131 q.add_question(make_question("q-1"));
132
133 let removed = q.remove("perm-1");
134 assert!(removed);
135 assert_eq!(q.len(), 1);
136 assert_eq!(q.peek().unwrap().id(), "q-1");
137 }
138
139 #[test]
140 fn test_remove_not_found() {
141 let mut q = ApprovalQueue::new();
142 q.add_permission(make_permission("perm-1"));
143
144 let removed = q.remove("nonexistent");
145 assert!(!removed);
146 assert_eq!(q.len(), 1);
147 }
148
149 #[test]
150 fn test_remove_middle_item() {
151 let mut q = ApprovalQueue::new();
152 q.add_permission(make_permission("a"));
153 q.add_permission(make_permission("b"));
154 q.add_permission(make_permission("c"));
155
156 q.remove("b");
157 assert_eq!(q.len(), 2);
158 assert_eq!(q.peek().unwrap().id(), "a");
159 }
160
161 #[test]
162 fn test_has_pending_tracks_state() {
163 let mut q = ApprovalQueue::new();
164 assert!(!q.has_pending());
165
166 q.add_permission(make_permission("x"));
167 assert!(q.has_pending());
168 }
169
170 #[test]
171 fn test_clear_empties_queue() {
172 let mut q = ApprovalQueue::new();
173 q.add_permission(make_permission("a"));
174 q.add_question(make_question("b"));
175 q.add_permission(make_permission("c"));
176 assert_eq!(q.len(), 3);
177
178 q.clear();
179 assert_eq!(q.len(), 0);
180 assert!(!q.has_pending());
181 assert!(q.peek().is_none());
182 }
183
184 #[test]
185 fn test_clear_on_empty_queue() {
186 let mut q = ApprovalQueue::new();
187 q.clear(); assert_eq!(q.len(), 0);
189 }
190
191 #[test]
192 fn test_clear_then_reuse() {
193 let mut q = ApprovalQueue::new();
194 q.add_permission(make_permission("a"));
195 q.add_question(make_question("b"));
196 q.clear();
197
198 q.add_permission(make_permission("c"));
200 assert_eq!(q.len(), 1);
201 assert!(q.has_pending());
202 assert_eq!(q.peek().unwrap().id(), "c");
203 }
204
205 #[test]
208 fn test_default_creates_empty_queue() {
209 let q = ApprovalQueue::default();
210 assert_eq!(q.len(), 0);
211 }
212
213 #[test]
214 fn test_remove_last_item_leaves_empty() {
215 let mut q = ApprovalQueue::new();
216 q.add_permission(make_permission("only"));
217 let removed = q.remove("only");
218 assert!(removed);
219 assert_eq!(q.len(), 0);
220 assert!(q.peek().is_none());
221 }
222
223 #[test]
224 fn test_remove_preserves_fifo_order() {
225 let mut q = ApprovalQueue::new();
226 q.add_permission(make_permission("a"));
227 q.add_question(make_question("b"));
228 q.add_permission(make_permission("c"));
229
230 q.remove("b");
232
233 assert_eq!(q.len(), 2);
235 assert_eq!(q.peek().unwrap().id(), "a");
236 }
237
238 #[test]
239 fn test_peek_does_not_remove() {
240 let mut q = ApprovalQueue::new();
241 q.add_permission(make_permission("p1"));
242 q.add_permission(make_permission("p2"));
243
244 assert_eq!(q.peek().unwrap().id(), "p1");
246 assert_eq!(q.peek().unwrap().id(), "p1");
247 assert_eq!(q.len(), 2);
248 }
249
250 #[test]
251 fn test_insertion_order_preserved() {
252 let mut q = ApprovalQueue::new();
253 q.add_permission(make_permission("first"));
254 q.add_question(make_question("second"));
255 q.add_permission(make_permission("third"));
256
257 assert_eq!(q.len(), 3);
258 assert_eq!(q.peek().unwrap().id(), "first");
259 }
260
261 #[test]
262 fn test_len_increments_correctly() {
263 let mut q = ApprovalQueue::new();
264 assert_eq!(q.len(), 0);
265 q.add_permission(make_permission("a"));
266 assert_eq!(q.len(), 1);
267 q.add_question(make_question("b"));
268 assert_eq!(q.len(), 2);
269 q.add_permission(make_permission("c"));
270 assert_eq!(q.len(), 3);
271 }
272
273 #[test]
274 fn test_remove_not_found_does_not_change_len() {
275 let mut q = ApprovalQueue::new();
276 q.add_permission(make_permission("a"));
277 q.add_permission(make_permission("b"));
278
279 let removed = q.remove("nonexistent");
280 assert!(!removed);
281 assert_eq!(q.len(), 2);
282 }
283
284 #[test]
285 fn test_mixed_permission_and_question_types() {
286 let mut q = ApprovalQueue::new();
287 q.add_permission(make_permission("perm"));
288 q.add_question(make_question("quest"));
289
290 let first = q.peek().unwrap();
291 assert!(matches!(first, PendingApproval::Permission(_)));
292 assert_eq!(first.id(), "perm");
293
294 q.remove("perm");
295 let second = q.peek().unwrap();
296 assert!(matches!(second, PendingApproval::Question(_)));
297 assert_eq!(second.id(), "quest");
298 }
299}