use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use tsoracle_proto::v1::tso_service_server::{TsoService, TsoServiceServer};
use tsoracle_proto::v1::{
AcquireLeaseRequest, AcquireLeaseResponse, GetCurrentMaxSafeRequest, GetCurrentMaxSafeResponse,
GetSafeFrontierRequest, GetSafeFrontierResponse, GetSeqBatchRequest, GetSeqBatchResponse,
GetSeqRequest, GetSeqResponse, GetTsRequest, GetTsResponse, ReleaseLeaseRequest,
ReleaseLeaseResponse, RenewLeaseRequest, RenewLeaseResponse,
};
use crate::RetryPolicy;
pub(crate) fn enable_tracing() {
use tracing_subscriber::filter::LevelFilter;
let _ = tracing_subscriber::fmt()
.with_max_level(LevelFilter::TRACE)
.with_test_writer()
.try_init();
}
pub(crate) fn short_policy() -> RetryPolicy {
RetryPolicy {
max_attempts: 2,
per_attempt_deadline: Duration::from_millis(100),
overall_deadline: Duration::from_millis(300),
base_backoff: Duration::from_millis(1),
leader_ttl: Duration::from_secs(30),
}
}
pub(crate) fn make_status_with_hint(hint: tsoracle_proto::v1::LeaderHint) -> tonic::Status {
use prost::Message;
use tonic::metadata::{BinaryMetadataValue, MetadataKey};
let mut buf = Vec::new();
hint.encode(&mut buf)
.expect("LeaderHint encode is infallible");
let mut status = tonic::Status::failed_precondition("not leader");
let key = MetadataKey::from_bytes(tsoracle_proto::v1::LEADER_HINT_TRAILER_KEY.as_bytes())
.expect("static ASCII key parses");
status
.metadata_mut()
.insert_bin(key, BinaryMetadataValue::from_bytes(&buf));
status
}
type BoxFut<T> = Pin<Box<dyn Future<Output = Result<T, tonic::Status>> + Send>>;
type TsHandler = Arc<dyn Fn(GetTsRequest) -> BoxFut<GetTsResponse> + Send + Sync>;
type SeqHandler = Arc<dyn Fn(GetSeqRequest) -> BoxFut<GetSeqResponse> + Send + Sync>;
type SeqBatchHandler = Arc<dyn Fn(GetSeqBatchRequest) -> BoxFut<GetSeqBatchResponse> + Send + Sync>;
pub(crate) struct FakeTso {
get_ts: TsHandler,
get_seq: SeqHandler,
get_seq_batch: SeqBatchHandler,
}
impl FakeTso {
pub(crate) fn new() -> Self {
FakeTso {
get_ts: Arc::new(|_| {
Box::pin(async { Err(tonic::Status::unimplemented("get_ts not configured")) })
}),
get_seq: Arc::new(|_| {
Box::pin(async { Err(tonic::Status::unimplemented("get_seq not configured")) })
}),
get_seq_batch: Arc::new(|_| {
Box::pin(async {
Err(tonic::Status::unimplemented("get_seq_batch not configured"))
})
}),
}
}
pub(crate) fn on_get_ts<F, Fut>(mut self, f: F) -> Self
where
F: Fn(GetTsRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<GetTsResponse, tonic::Status>> + Send + 'static,
{
self.get_ts = Arc::new(move |req| Box::pin(f(req)));
self
}
pub(crate) fn on_get_seq<F, Fut>(mut self, f: F) -> Self
where
F: Fn(GetSeqRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<GetSeqResponse, tonic::Status>> + Send + 'static,
{
self.get_seq = Arc::new(move |req| Box::pin(f(req)));
self
}
pub(crate) fn on_get_seq_batch<F, Fut>(mut self, f: F) -> Self
where
F: Fn(GetSeqBatchRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<GetSeqBatchResponse, tonic::Status>> + Send + 'static,
{
self.get_seq_batch = Arc::new(move |req| Box::pin(f(req)));
self
}
pub(crate) async fn spawn(self) -> SocketAddr {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind loopback listener");
let addr = listener.local_addr().expect("local_addr");
let incoming = tonic::transport::server::TcpIncoming::from(listener);
tokio::spawn(async move {
tonic::transport::Server::builder()
.add_service(TsoServiceServer::new(self))
.serve_with_incoming(incoming)
.await
.ok();
});
addr
}
}
#[tonic::async_trait]
impl TsoService for FakeTso {
async fn get_ts(
&self,
request: tonic::Request<GetTsRequest>,
) -> Result<tonic::Response<GetTsResponse>, tonic::Status> {
(self.get_ts)(request.into_inner())
.await
.map(tonic::Response::new)
}
async fn get_current_max_safe(
&self,
_request: tonic::Request<GetCurrentMaxSafeRequest>,
) -> Result<tonic::Response<GetCurrentMaxSafeResponse>, tonic::Status> {
Ok(tonic::Response::new(GetCurrentMaxSafeResponse::default()))
}
async fn get_seq(
&self,
request: tonic::Request<GetSeqRequest>,
) -> Result<tonic::Response<GetSeqResponse>, tonic::Status> {
(self.get_seq)(request.into_inner())
.await
.map(tonic::Response::new)
}
async fn get_seq_batch(
&self,
request: tonic::Request<GetSeqBatchRequest>,
) -> Result<tonic::Response<GetSeqBatchResponse>, tonic::Status> {
(self.get_seq_batch)(request.into_inner())
.await
.map(tonic::Response::new)
}
async fn acquire_lease(
&self,
_request: tonic::Request<AcquireLeaseRequest>,
) -> Result<tonic::Response<AcquireLeaseResponse>, tonic::Status> {
Err(tonic::Status::unimplemented(
"acquire_lease not implemented in test stub",
))
}
async fn renew_lease(
&self,
_request: tonic::Request<RenewLeaseRequest>,
) -> Result<tonic::Response<RenewLeaseResponse>, tonic::Status> {
Err(tonic::Status::unimplemented(
"renew_lease not implemented in test stub",
))
}
async fn release_lease(
&self,
_request: tonic::Request<ReleaseLeaseRequest>,
) -> Result<tonic::Response<ReleaseLeaseResponse>, tonic::Status> {
Err(tonic::Status::unimplemented(
"release_lease not implemented in test stub",
))
}
async fn get_safe_frontier(
&self,
_request: tonic::Request<GetSafeFrontierRequest>,
) -> Result<tonic::Response<GetSafeFrontierResponse>, tonic::Status> {
Ok(tonic::Response::new(GetSafeFrontierResponse::default()))
}
}