diff options
Diffstat (limited to 'tvix/store/src/pathinfoservice')
-rw-r--r-- | tvix/store/src/pathinfoservice/mod.rs | 3 | ||||
-rw-r--r-- | tvix/store/src/pathinfoservice/tests/mod.rs | 112 | ||||
-rw-r--r-- | tvix/store/src/pathinfoservice/tests/utils.rs | 60 |
3 files changed, 175 insertions, 0 deletions
diff --git a/tvix/store/src/pathinfoservice/mod.rs b/tvix/store/src/pathinfoservice/mod.rs index 1f52d20caed7..c334c8dc5631 100644 --- a/tvix/store/src/pathinfoservice/mod.rs +++ b/tvix/store/src/pathinfoservice/mod.rs @@ -7,6 +7,9 @@ mod sled; #[cfg(any(feature = "fuse", feature = "virtiofs"))] mod fs; +#[cfg(test)] +mod tests; + use futures::stream::BoxStream; use tonic::async_trait; use tvix_castore::proto as castorepb; diff --git a/tvix/store/src/pathinfoservice/tests/mod.rs b/tvix/store/src/pathinfoservice/tests/mod.rs new file mode 100644 index 000000000000..a3035d094d1e --- /dev/null +++ b/tvix/store/src/pathinfoservice/tests/mod.rs @@ -0,0 +1,112 @@ +//! This contains test scenarios that a given [PathInfoService] needs to pass. +//! We use [rstest] and [rstest_reuse] to provide all services we want to test +//! against, and then apply this template to all test functions. + +use rstest::*; +use rstest_reuse::{self, *}; +use std::sync::Arc; +use tvix_castore::proto as castorepb; +use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService}; + +use super::PathInfoService; +use crate::proto::PathInfo; +use crate::tests::fixtures::DUMMY_OUTPUT_HASH; + +mod utils; +use self::utils::make_grpc_path_info_service_client; + +/// Convenience type alias batching all three servives together. +#[allow(clippy::upper_case_acronyms)] +type BSDSPS = ( + Arc<dyn BlobService>, + Arc<dyn DirectoryService>, + Box<dyn PathInfoService>, +); + +/// Creates a PathInfoService using a new Memory{Blob,Directory}Service. +/// We return a 3-tuple containing all of them, as some tests want to interact +/// with all three. +pub async fn make_path_info_service(uri: &str) -> BSDSPS { + let blob_service: Arc<dyn BlobService> = tvix_castore::blobservice::from_addr("memory://") + .await + .unwrap() + .into(); + let directory_service: Arc<dyn DirectoryService> = + tvix_castore::directoryservice::from_addr("memory://") + .await + .unwrap() + .into(); + + ( + blob_service.clone(), + directory_service.clone(), + crate::pathinfoservice::from_addr(uri, blob_service, directory_service) + .await + .unwrap(), + ) +} + +#[template] +#[rstest] +#[case::memory(make_path_info_service("memory://").await)] +#[case::grpc(make_grpc_path_info_service_client().await)] +#[case::sled(make_path_info_service("sled://").await)] +pub fn path_info_services( + #[case] services: ( + impl BlobService, + impl DirectoryService, + impl PathInfoService, + ), +) { +} + +// FUTUREWORK: add more tests rejecting invalid PathInfo messages. +// A subset of them should also ensure references to other PathInfos, or +// elements in {Blob,Directory}Service do exist. + +/// Trying to get a non-existent PathInfo should return Ok(None). +#[apply(path_info_services)] +#[tokio::test] +async fn not_found(services: BSDSPS) { + let (_, _, path_info_service) = services; + assert!(path_info_service + .get(DUMMY_OUTPUT_HASH) + .await + .expect("must succeed") + .is_none()); +} + +/// Put a PathInfo into the store, get it back. +#[apply(path_info_services)] +#[tokio::test] +async fn put_get(services: BSDSPS) { + let (_, _, path_info_service) = services; + + let path_info = PathInfo { + node: Some(castorepb::Node { + node: Some(castorepb::node::Node::Symlink(castorepb::SymlinkNode { + name: "00000000000000000000000000000000-foo".into(), + target: "doesntmatter".into(), + })), + }), + ..Default::default() + }; + + // insert + let resp = path_info_service + .put(path_info.clone()) + .await + .expect("must succeed"); + + // expect the returned PathInfo to be equal (for now) + // in the future, some stores might add additional fields/signatures. + assert_eq!(path_info, resp); + + // get it back + let resp = path_info_service + .get(DUMMY_OUTPUT_HASH) + .await + .expect("must succeed"); + + assert_eq!(Some(path_info), resp); +} diff --git a/tvix/store/src/pathinfoservice/tests/utils.rs b/tvix/store/src/pathinfoservice/tests/utils.rs new file mode 100644 index 000000000000..31ec57aade73 --- /dev/null +++ b/tvix/store/src/pathinfoservice/tests/utils.rs @@ -0,0 +1,60 @@ +use std::sync::Arc; + +use tonic::transport::{Endpoint, Server, Uri}; + +use crate::{ + pathinfoservice::{GRPCPathInfoService, MemoryPathInfoService, PathInfoService}, + proto::{ + path_info_service_client::PathInfoServiceClient, + path_info_service_server::PathInfoServiceServer, GRPCPathInfoServiceWrapper, + }, + tests::fixtures::{blob_service, directory_service}, +}; + +/// Constructs and returns a gRPC PathInfoService. +/// We also return memory-based {Blob,Directory}Service, +/// as the consumer of this function accepts a 3-tuple. +pub async fn make_grpc_path_info_service_client() -> super::BSDSPS { + let (left, right) = tokio::io::duplex(64); + + let blob_service = blob_service(); + let directory_service = directory_service(); + + // spin up a server, which will only connect once, to the left side. + tokio::spawn({ + let blob_service = blob_service.clone(); + let directory_service = directory_service.clone(); + async move { + let path_info_service: Arc<dyn PathInfoService> = + Arc::from(MemoryPathInfoService::new(blob_service, directory_service)); + + // spin up a new DirectoryService + let mut server = Server::builder(); + let router = server.add_service(PathInfoServiceServer::new( + GRPCPathInfoServiceWrapper::new(path_info_service), + )); + + router + .serve_with_incoming(tokio_stream::once(Ok::<_, std::io::Error>(left))) + .await + } + }); + + // Create a client, connecting to the right side. The URI is unused. + let mut maybe_right = Some(right); + + let path_info_service = Box::new(GRPCPathInfoService::from_client( + PathInfoServiceClient::new( + Endpoint::try_from("http://[::]:50051") + .unwrap() + .connect_with_connector(tower::service_fn(move |_: Uri| { + let right = maybe_right.take().unwrap(); + async move { Ok::<_, std::io::Error>(right) } + })) + .await + .unwrap(), + ), + )); + + (blob_service, directory_service, path_info_service) +} |