use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use futures::FutureExt;
use kitsune_p2p_types::dependencies::lair_keystore_api::lair_client::traits::AsLairClient;
use kitsune_p2p_types::dependencies::lair_keystore_api::prelude::{LairApiEnum, LairClient};
use kitsune_p2p_types::dependencies::lair_keystore_api::LairResult;
use crate::test_keystore::spawn_test_keystore;
use crate::MetaLairClient;
pub async fn spawn_crude_mock_keystore<F>(err_fn: F) -> MetaLairClient
where
F: Fn() -> one_err::OneErr + Send + Sync + 'static,
{
let (s, _) = tokio::sync::mpsc::unbounded_channel();
MetaLairClient(
Arc::new(parking_lot::Mutex::new(LairClient(Arc::new(
CrudeMockKeystore(Arc::new(err_fn)),
)))),
s,
)
}
pub async fn spawn_real_or_mock_keystore<F>(
func: F,
) -> LairResult<(MetaLairClient, MockLairControl)>
where
F: Fn(LairApiEnum) -> LairResult<LairApiEnum> + Send + Sync + 'static,
{
let real = spawn_test_keystore().await?;
let use_mock = Arc::new(AtomicBool::new(false));
let mock = RealOrMockKeystore {
mock: Box::new(func),
real,
use_mock: use_mock.clone(),
};
let control = MockLairControl(use_mock);
let (s, _) = tokio::sync::mpsc::unbounded_channel();
Ok((
MetaLairClient(
Arc::new(parking_lot::Mutex::new(LairClient(Arc::new(mock)))),
s,
),
control,
))
}
struct RealOrMockKeystore {
mock: Box<dyn Fn(LairApiEnum) -> LairResult<LairApiEnum> + Send + Sync + 'static>,
real: MetaLairClient,
use_mock: Arc<AtomicBool>,
}
pub struct MockLairControl(Arc<AtomicBool>);
impl MockLairControl {
pub fn use_mock(&self) {
self.0.store(true, std::sync::atomic::Ordering::SeqCst);
}
pub fn use_real(&self) {
self.0.store(false, std::sync::atomic::Ordering::SeqCst);
}
}
struct CrudeMockKeystore(Arc<dyn Fn() -> one_err::OneErr + Send + Sync + 'static>);
impl AsLairClient for CrudeMockKeystore {
fn get_enc_ctx_key(&self) -> sodoken::BufReadSized<32> {
unimplemented!()
}
fn get_dec_ctx_key(&self) -> sodoken::BufReadSized<32> {
unimplemented!()
}
fn shutdown(&self) -> futures::future::BoxFuture<'static, LairResult<()>> {
unimplemented!()
}
fn request(
&self,
_request: LairApiEnum,
) -> futures::future::BoxFuture<'static, LairResult<LairApiEnum>> {
let err = (self.0)();
async move { Err(err) }.boxed()
}
}
impl AsLairClient for RealOrMockKeystore {
fn get_enc_ctx_key(&self) -> sodoken::BufReadSized<32> {
self.real.cli().0.get_enc_ctx_key()
}
fn get_dec_ctx_key(&self) -> sodoken::BufReadSized<32> {
self.real.cli().0.get_dec_ctx_key()
}
fn shutdown(&self) -> futures::future::BoxFuture<'static, LairResult<()>> {
self.real.cli().0.shutdown().boxed()
}
fn request(
&self,
request: LairApiEnum,
) -> futures::future::BoxFuture<'static, LairResult<LairApiEnum>> {
if self.use_mock.load(std::sync::atomic::Ordering::SeqCst) {
let r = (self.mock)(request);
async move { r }.boxed()
} else {
AsLairClient::request(&*self.real.cli().0 .0, request)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agent_pubkey_ext::AgentPubKeyExt;
#[tokio::test(flavor = "multi_thread")]
async fn test_crude_mock_keystore() {
tokio::task::spawn(async move {
let keystore = spawn_crude_mock_keystore(|| "err".into()).await;
assert_eq!(
holo_hash::AgentPubKey::new_random(&keystore).await,
Err(one_err::OneErr::new("err"))
);
})
.await
.unwrap();
}
}