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 remove(&mut self, request_id: &str) -> bool {
46 if let Some(pos) = self.items.iter().position(|item| item.id() == request_id) {
47 self.items.remove(pos);
48 true
49 } else {
50 false
51 }
52 }
53}
54
55impl Default for ApprovalQueue {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::approval::types::{PermissionRequest, QuestionRequest};
65
66 fn make_permission(id: &str) -> PermissionRequest {
67 PermissionRequest {
68 id: id.to_string(),
69 permission: "bash".to_string(),
70 metadata: serde_json::Value::Null,
71 }
72 }
73
74 fn make_question(id: &str) -> QuestionRequest {
75 QuestionRequest {
76 id: id.to_string(),
77 questions: vec![],
78 }
79 }
80
81 #[test]
82 fn test_new_queue_is_empty() {
83 let q = ApprovalQueue::new();
84 assert!(!q.has_pending());
85 assert_eq!(q.len(), 0);
86 assert!(q.peek().is_none());
87 }
88
89 #[test]
90 fn test_add_permission_and_peek() {
91 let mut q = ApprovalQueue::new();
92 q.add_permission(make_permission("perm-1"));
93 assert_eq!(q.len(), 1);
94 assert!(q.has_pending());
95 let item = q.peek().unwrap();
96 assert_eq!(item.id(), "perm-1");
97 assert!(matches!(item, PendingApproval::Permission(_)));
98 }
99
100 #[test]
101 fn test_add_question_and_peek() {
102 let mut q = ApprovalQueue::new();
103 q.add_question(make_question("q-1"));
104 assert_eq!(q.len(), 1);
105 let item = q.peek().unwrap();
106 assert_eq!(item.id(), "q-1");
107 assert!(matches!(item, PendingApproval::Question(_)));
108 }
109
110 #[test]
111 fn test_fifo_ordering() {
112 let mut q = ApprovalQueue::new();
113 q.add_permission(make_permission("first"));
114 q.add_question(make_question("second"));
115 q.add_permission(make_permission("third"));
116
117 assert_eq!(q.peek().unwrap().id(), "first");
119 assert_eq!(q.len(), 3);
120 }
121
122 #[test]
123 fn test_remove_found() {
124 let mut q = ApprovalQueue::new();
125 q.add_permission(make_permission("perm-1"));
126 q.add_question(make_question("q-1"));
127
128 let removed = q.remove("perm-1");
129 assert!(removed);
130 assert_eq!(q.len(), 1);
131 assert_eq!(q.peek().unwrap().id(), "q-1");
132 }
133
134 #[test]
135 fn test_remove_not_found() {
136 let mut q = ApprovalQueue::new();
137 q.add_permission(make_permission("perm-1"));
138
139 let removed = q.remove("nonexistent");
140 assert!(!removed);
141 assert_eq!(q.len(), 1);
142 }
143
144 #[test]
145 fn test_remove_middle_item() {
146 let mut q = ApprovalQueue::new();
147 q.add_permission(make_permission("a"));
148 q.add_permission(make_permission("b"));
149 q.add_permission(make_permission("c"));
150
151 q.remove("b");
152 assert_eq!(q.len(), 2);
153 assert_eq!(q.peek().unwrap().id(), "a");
154 }
155
156 #[test]
157 fn test_has_pending_tracks_state() {
158 let mut q = ApprovalQueue::new();
159 assert!(!q.has_pending());
160
161 q.add_permission(make_permission("x"));
162 assert!(q.has_pending());
163 }
164
165 #[test]
168 fn test_default_creates_empty_queue() {
169 let q = ApprovalQueue::default();
170 assert_eq!(q.len(), 0);
171 }
172
173 #[test]
174 fn test_remove_last_item_leaves_empty() {
175 let mut q = ApprovalQueue::new();
176 q.add_permission(make_permission("only"));
177 let removed = q.remove("only");
178 assert!(removed);
179 assert_eq!(q.len(), 0);
180 assert!(q.peek().is_none());
181 }
182
183 #[test]
184 fn test_remove_preserves_fifo_order() {
185 let mut q = ApprovalQueue::new();
186 q.add_permission(make_permission("a"));
187 q.add_question(make_question("b"));
188 q.add_permission(make_permission("c"));
189
190 q.remove("b");
192
193 assert_eq!(q.len(), 2);
195 assert_eq!(q.peek().unwrap().id(), "a");
196 }
197
198 #[test]
199 fn test_peek_does_not_remove() {
200 let mut q = ApprovalQueue::new();
201 q.add_permission(make_permission("p1"));
202 q.add_permission(make_permission("p2"));
203
204 assert_eq!(q.peek().unwrap().id(), "p1");
206 assert_eq!(q.peek().unwrap().id(), "p1");
207 assert_eq!(q.len(), 2);
208 }
209
210 #[test]
211 fn test_insertion_order_preserved() {
212 let mut q = ApprovalQueue::new();
213 q.add_permission(make_permission("first"));
214 q.add_question(make_question("second"));
215 q.add_permission(make_permission("third"));
216
217 assert_eq!(q.len(), 3);
218 assert_eq!(q.peek().unwrap().id(), "first");
219 }
220
221 #[test]
222 fn test_len_increments_correctly() {
223 let mut q = ApprovalQueue::new();
224 assert_eq!(q.len(), 0);
225 q.add_permission(make_permission("a"));
226 assert_eq!(q.len(), 1);
227 q.add_question(make_question("b"));
228 assert_eq!(q.len(), 2);
229 q.add_permission(make_permission("c"));
230 assert_eq!(q.len(), 3);
231 }
232
233 #[test]
234 fn test_remove_not_found_does_not_change_len() {
235 let mut q = ApprovalQueue::new();
236 q.add_permission(make_permission("a"));
237 q.add_permission(make_permission("b"));
238
239 let removed = q.remove("nonexistent");
240 assert!(!removed);
241 assert_eq!(q.len(), 2);
242 }
243
244 #[test]
245 fn test_mixed_permission_and_question_types() {
246 let mut q = ApprovalQueue::new();
247 q.add_permission(make_permission("perm"));
248 q.add_question(make_question("quest"));
249
250 let first = q.peek().unwrap();
251 assert!(matches!(first, PendingApproval::Permission(_)));
252 assert_eq!(first.id(), "perm");
253
254 q.remove("perm");
255 let second = q.peek().unwrap();
256 assert!(matches!(second, PendingApproval::Question(_)));
257 assert_eq!(second.id(), "quest");
258 }
259}