use async_trait::async_trait;
use notedthat_core::{KbSlug, Storage};
use std::collections::BTreeMap;
use std::sync::Arc;
pub use notedthat_core::testing::{InMemoryStorage, compute_etag, reserve_addr};
pub struct NoopSearcher;
#[async_trait]
impl notedthat_indexer::Searcher for NoopSearcher {
async fn search(
&self,
_kb: &KbSlug,
_request: notedthat_core::search::ValidatedRequest,
_key_filter: Option<notedthat_indexer::KeyPredicate<'_>>,
) -> Result<notedthat_core::search::SearchResponse, notedthat_core::search::SearchError> {
Ok(notedthat_core::search::SearchResponse::empty())
}
}
#[cfg(feature = "test-support")]
pub struct MockSearcher {
responses: std::sync::Mutex<
std::collections::VecDeque<
Result<notedthat_core::search::SearchResponse, notedthat_core::search::SearchError>,
>,
>,
calls: std::sync::atomic::AtomicUsize,
scoped_calls: std::sync::atomic::AtomicUsize,
}
#[cfg(feature = "test-support")]
impl MockSearcher {
pub fn new() -> Self {
Self {
responses: std::sync::Mutex::new(std::collections::VecDeque::new()),
calls: std::sync::atomic::AtomicUsize::new(0),
scoped_calls: std::sync::atomic::AtomicUsize::new(0),
}
}
pub fn call_count(&self) -> usize {
self.calls.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn scoped_calls(&self) -> usize {
self.scoped_calls.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn push_response(
&self,
r: Result<notedthat_core::search::SearchResponse, notedthat_core::search::SearchError>,
) {
self.responses.lock().unwrap().push_back(r);
}
pub fn set_response(
&self,
r: Result<notedthat_core::search::SearchResponse, notedthat_core::search::SearchError>,
) {
self.push_response(r);
}
}
#[cfg(feature = "test-support")]
#[async_trait]
impl notedthat_indexer::Searcher for MockSearcher {
async fn search(
&self,
_kb: &KbSlug,
_request: notedthat_core::search::ValidatedRequest,
key_filter: Option<notedthat_indexer::KeyPredicate<'_>>,
) -> Result<notedthat_core::search::SearchResponse, notedthat_core::search::SearchError> {
self.calls.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
if key_filter.is_some() {
self.scoped_calls
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
let mut response = self
.responses
.lock()
.unwrap()
.pop_front()
.unwrap_or_else(|| Ok(notedthat_core::search::SearchResponse::empty()))?;
if let Some(allows) = key_filter {
response.hits.retain(|hit| allows(hit.object_key.as_str()));
}
Ok(response)
}
}
#[cfg(feature = "test-support")]
impl Default for MockSearcher {
fn default() -> Self {
Self::new()
}
}
pub fn test_app_state_with_default_channel(
storage: Arc<dyn Storage>,
declared_kbs: Arc<BTreeMap<String, KbSlug>>,
authenticator: Arc<notedthat_core::Authenticator>,
max_body_size: u64,
) -> crate::state::AppState {
let (indexer_tx, _) = tokio::sync::mpsc::channel(1024);
let kb_details = Arc::new(notedthat_core::slug_kb_details(&declared_kbs));
crate::state::AppState {
storage,
declared_kbs,
access_policies: Arc::new(BTreeMap::new()),
kb_details,
authenticator,
max_body_size,
max_patchable_size: max_body_size,
indexer_tx,
searcher: Arc::new(NoopSearcher),
events: None,
index_health: Arc::new(notedthat_indexer::IndexHealth::new()),
}
}
pub fn test_app_state_with_channel(
storage: Arc<dyn Storage>,
declared_kbs: Arc<BTreeMap<String, KbSlug>>,
authenticator: Arc<notedthat_core::Authenticator>,
max_body_size: u64,
) -> (
crate::state::AppState,
tokio::sync::mpsc::Receiver<notedthat_indexer::IndexEvent>,
) {
let (indexer_tx, rx) = tokio::sync::mpsc::channel(1024);
let kb_details = Arc::new(notedthat_core::slug_kb_details(&declared_kbs));
(
crate::state::AppState {
storage,
declared_kbs,
access_policies: Arc::new(BTreeMap::new()),
kb_details,
authenticator,
max_body_size,
max_patchable_size: max_body_size,
indexer_tx,
searcher: Arc::new(NoopSearcher),
events: None,
index_health: Arc::new(notedthat_indexer::IndexHealth::new()),
},
rx,
)
}