use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;
use bytes::Bytes;
use smallvec::SmallVec;
use crate::core::Broker;
use crate::core::Envelope;
use crate::core::Subscription;
pub(crate) fn make_envelope(stream: &str, id: &str) -> Envelope {
Envelope {
stream: Arc::from(stream),
id: Arc::from(id),
payload: Bytes::from_static(b"{}"),
headers: SmallVec::new(),
ts_unix_ms: 0,
}
}
pub(crate) fn token(id: &str) -> String { id.to_owned() }
#[derive(Debug, Clone, Default)]
pub(crate) struct MockSubscription {
pub streams: Vec<Arc<str>>,
}
impl Subscription for MockSubscription {
fn set_streams(&mut self, streams: Vec<Arc<str>>) { self.streams = streams; }
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("mock broker error")]
pub(crate) struct MockError;
#[derive(Default)]
struct MockState {
ack_attempts: AtomicUsize,
ack_success: AtomicUsize,
ack_fail_remaining: AtomicUsize,
dead_letters: Mutex<Vec<(String, String)>>,
poll_batches: Mutex<VecDeque<Vec<(Envelope, String)>>>,
reclaim_batches: Mutex<VecDeque<Vec<(Envelope, String)>>>,
}
#[derive(Clone, Default)]
pub(crate) struct MockBroker {
inner: Arc<MockState>,
}
impl MockBroker {
pub(crate) fn ack_count(&self) -> usize { self.inner.ack_success.load(Ordering::SeqCst) }
pub(crate) fn ack_attempts(&self) -> usize { self.inner.ack_attempts.load(Ordering::SeqCst) }
pub(crate) fn fail_next_acks(&self, n: usize) { self.inner.ack_fail_remaining.store(n, Ordering::SeqCst); }
pub(crate) fn dead_letters(&self) -> Vec<(String, String)> { self.inner.dead_letters.lock().unwrap().clone() }
pub(crate) fn enqueue_poll(&self, batch: Vec<(Envelope, String)>) {
self.inner.poll_batches.lock().unwrap().push_back(batch);
}
pub(crate) fn enqueue_reclaim(&self, batch: Vec<(Envelope, String)>) {
self.inner.reclaim_batches.lock().unwrap().push_back(batch);
}
}
impl Broker for MockBroker {
type AckToken = String;
type Subscription = MockSubscription;
type Error = MockError;
async fn poll(&self, _sub: &Self::Subscription) -> Result<Vec<(Envelope, String)>, MockError> {
let batch = self.inner.poll_batches.lock().unwrap().pop_front();
match batch {
Some(batch) => Ok(batch),
None => {
tokio::time::sleep(Duration::from_millis(5)).await;
Ok(Vec::new())
}
}
}
async fn ack(&self, _token: String) -> Result<(), MockError> {
self.inner.ack_attempts.fetch_add(1, Ordering::SeqCst);
let mut remaining = self.inner.ack_fail_remaining.load(Ordering::SeqCst);
while remaining > 0 {
match self.inner.ack_fail_remaining.compare_exchange(
remaining,
remaining - 1,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => return Err(MockError),
Err(actual) => remaining = actual,
}
}
self.inner.ack_success.fetch_add(1, Ordering::SeqCst);
Ok(())
}
async fn reclaim(&self, _sub: &Self::Subscription) -> Result<Vec<(Envelope, String)>, MockError> {
let batch = self.inner.reclaim_batches.lock().unwrap().pop_front();
Ok(batch.unwrap_or_default())
}
async fn dead_letter(&self, envelope: &Envelope, reason: &str) -> Result<(), MockError> {
self.inner.dead_letters.lock().unwrap().push((envelope.id.to_string(), reason.to_owned()));
Ok(())
}
}