QuickDID is a high-performance AT Protocol identity resolution service written in Rust. It provides handle-to-DID resolution with Redis-backed caching and queue processing.
at main 5.7 kB view raw
1//! No-operation queue adapter implementation. 2//! 3//! This module provides a queue adapter that discards all work items, 4//! useful for testing or when queue processing is disabled. 5 6use async_trait::async_trait; 7use std::time::Duration; 8use tokio::time::sleep; 9 10use super::adapter::QueueAdapter; 11use super::error::Result; 12 13/// No-operation queue adapter that discards all work items. 14/// 15/// This adapter is useful for configurations where queuing is disabled 16/// or as a fallback when other queue adapters fail to initialize. 17/// All work items pushed to this queue are silently discarded. 18/// 19/// # Features 20/// 21/// - Zero resource usage 22/// - Always healthy 23/// - Discards all work items 24/// - Never returns items from pull 25/// 26/// # Use Cases 27/// 28/// - Testing environments where queue processing isn't needed 29/// - Graceful degradation when queue backends are unavailable 30/// - Configurations where queue processing is explicitly disabled 31/// 32/// # Examples 33/// 34/// ``` 35/// use quickdid::queue::NoopQueueAdapter; 36/// use quickdid::queue::QueueAdapter; 37/// 38/// # async fn example() -> anyhow::Result<()> { 39/// let queue = NoopQueueAdapter::<String>::new(); 40/// 41/// // Push is silently discarded 42/// queue.push("ignored".to_string()).await?; 43/// 44/// // Pull never returns items (blocks indefinitely) 45/// // let item = queue.pull().await; // Would block forever 46/// 47/// // Always reports healthy 48/// assert!(queue.is_healthy().await); 49/// 50/// // Always reports empty 51/// assert_eq!(queue.depth().await, Some(0)); 52/// # Ok(()) 53/// # } 54/// ``` 55pub struct NoopQueueAdapter<T> 56where 57 T: Send + Sync + 'static, 58{ 59 _phantom: std::marker::PhantomData<T>, 60} 61 62impl<T> NoopQueueAdapter<T> 63where 64 T: Send + Sync + 'static, 65{ 66 /// Create a new no-op queue adapter. 67 /// 68 /// # Examples 69 /// 70 /// ``` 71 /// use quickdid::queue::NoopQueueAdapter; 72 /// 73 /// let queue = NoopQueueAdapter::<String>::new(); 74 /// ``` 75 pub fn new() -> Self { 76 Self { 77 _phantom: std::marker::PhantomData, 78 } 79 } 80} 81 82impl<T> Default for NoopQueueAdapter<T> 83where 84 T: Send + Sync + 'static, 85{ 86 fn default() -> Self { 87 Self::new() 88 } 89} 90 91#[async_trait] 92impl<T> QueueAdapter<T> for NoopQueueAdapter<T> 93where 94 T: Send + Sync + 'static, 95{ 96 async fn pull(&self) -> Option<T> { 97 // Never returns any work - sleeps to avoid busy-waiting 98 sleep(Duration::from_secs(60)).await; 99 None 100 } 101 102 async fn push(&self, _work: T) -> Result<()> { 103 // Silently discard the work 104 Ok(()) 105 } 106 107 async fn ack(&self, _item: &T) -> Result<()> { 108 // No-op 109 Ok(()) 110 } 111 112 async fn try_push(&self, _work: T) -> Result<()> { 113 // Silently discard the work 114 Ok(()) 115 } 116 117 async fn depth(&self) -> Option<usize> { 118 // Always empty 119 Some(0) 120 } 121 122 async fn is_healthy(&self) -> bool { 123 // Always healthy 124 true 125 } 126} 127 128#[cfg(test)] 129mod tests { 130 use super::*; 131 132 #[tokio::test] 133 async fn test_noop_queue_push() { 134 let queue = NoopQueueAdapter::<String>::new(); 135 136 // Push should always succeed 137 queue.push("test1".to_string()).await.unwrap(); 138 queue.push("test2".to_string()).await.unwrap(); 139 queue.push("test3".to_string()).await.unwrap(); 140 } 141 142 #[tokio::test] 143 async fn test_noop_queue_try_push() { 144 let queue = NoopQueueAdapter::<i32>::new(); 145 146 // Try push should always succeed 147 queue.try_push(1).await.unwrap(); 148 queue.try_push(2).await.unwrap(); 149 queue.try_push(3).await.unwrap(); 150 } 151 152 #[tokio::test] 153 async fn test_noop_queue_ack() { 154 let queue = NoopQueueAdapter::<String>::new(); 155 156 // Ack should always succeed 157 queue.ack(&"any".to_string()).await.unwrap(); 158 } 159 160 #[tokio::test] 161 async fn test_noop_queue_depth() { 162 let queue = NoopQueueAdapter::<String>::new(); 163 164 // Should always report empty 165 assert_eq!(queue.depth().await, Some(0)); 166 167 // Even after pushing items 168 queue.push("item".to_string()).await.unwrap(); 169 assert_eq!(queue.depth().await, Some(0)); 170 } 171 172 #[tokio::test] 173 async fn test_noop_queue_health() { 174 let queue = NoopQueueAdapter::<String>::new(); 175 176 // Should always be healthy 177 assert!(queue.is_healthy().await); 178 } 179 180 #[tokio::test] 181 async fn test_noop_queue_default() { 182 let queue: NoopQueueAdapter<String> = Default::default(); 183 184 // Default instance should work normally 185 queue.push("test".to_string()).await.unwrap(); 186 assert!(queue.is_healthy().await); 187 } 188 189 #[tokio::test(flavor = "multi_thread")] 190 async fn test_noop_queue_pull_blocks() { 191 use tokio::time::timeout; 192 193 let queue = NoopQueueAdapter::<String>::new(); 194 195 // Pull should block and not return immediately 196 let result = timeout(Duration::from_millis(100), queue.pull()).await; 197 assert!(result.is_err(), "Pull should have timed out"); 198 } 199 200 #[tokio::test] 201 async fn test_noop_queue_with_custom_type() { 202 use serde::{Deserialize, Serialize}; 203 204 #[derive(Debug, Clone, Serialize, Deserialize)] 205 struct CustomWork { 206 id: u64, 207 data: Vec<String>, 208 } 209 210 let queue = NoopQueueAdapter::<CustomWork>::new(); 211 212 let work = CustomWork { 213 id: 123, 214 data: vec!["test".to_string()], 215 }; 216 217 // Should handle custom types without issue 218 queue.push(work.clone()).await.unwrap(); 219 queue.ack(&work).await.unwrap(); 220 assert_eq!(queue.depth().await, Some(0)); 221 } 222}