diff options
Diffstat (limited to 'tvix/castore/src/directoryservice/tests/mod.rs')
-rw-r--r-- | tvix/castore/src/directoryservice/tests/mod.rs | 115 |
1 files changed, 63 insertions, 52 deletions
diff --git a/tvix/castore/src/directoryservice/tests/mod.rs b/tvix/castore/src/directoryservice/tests/mod.rs index cc3c5b788a2c..ad189564bfe7 100644 --- a/tvix/castore/src/directoryservice/tests/mod.rs +++ b/tvix/castore/src/directoryservice/tests/mod.rs @@ -8,10 +8,8 @@ use rstest_reuse::{self, *}; use super::DirectoryService; use crate::directoryservice; -use crate::{ - fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C}, - proto::{self, Directory}, -}; +use crate::fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C, DIRECTORY_D}; +use crate::{Directory, Node}; mod utils; use self::utils::make_grpc_directory_service_client; @@ -26,16 +24,27 @@ use self::utils::make_grpc_directory_service_client; #[case::grpc(make_grpc_directory_service_client().await)] #[case::memory(directoryservice::from_addr("memory://").await.unwrap())] #[case::sled(directoryservice::from_addr("sled://").await.unwrap())] +#[case::redb(directoryservice::from_addr("redb://").await.unwrap())] #[case::objectstore(directoryservice::from_addr("objectstore+memory://").await.unwrap())] #[cfg_attr(all(feature = "cloud", feature = "integration"), case::bigtable(directoryservice::from_addr("bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1").await.unwrap()))] pub fn directory_services(#[case] directory_service: impl DirectoryService) {} -/// Ensures asking for a directory that doesn't exist returns a Ok(None). +/// Ensures asking for a directory that doesn't exist returns a Ok(None), and a get_recursive +/// returns an empty stream. #[apply(directory_services)] #[tokio::test] async fn test_non_exist(directory_service: impl DirectoryService) { - let resp = directory_service.get(&DIRECTORY_A.digest()).await; - assert!(resp.unwrap().is_none()) + // single get + assert_eq!(Ok(None), directory_service.get(&DIRECTORY_A.digest()).await); + + // recursive get + assert_eq!( + Vec::<Result<Directory, crate::Error>>::new(), + directory_service + .get_recursive(&DIRECTORY_A.digest()) + .collect::<Vec<Result<Directory, crate::Error>>>() + .await + ); } /// Putting a single directory into the store, and then getting it out both via @@ -123,6 +132,46 @@ async fn put_get_multiple_dedup(directory_service: impl DirectoryService) { ) } +/// This tests the insertion and retrieval of a closure which contains a duplicated directory +/// (DIRECTORY_A, which is an empty directory), once in the root, and once in a subdir. +#[apply(directory_services)] +#[tokio::test] +async fn put_get_foo(directory_service: impl DirectoryService) { + let mut handle = directory_service.put_multiple_start(); + handle.put(DIRECTORY_A.clone()).await.unwrap(); + handle.put(DIRECTORY_B.clone()).await.unwrap(); + handle.put(DIRECTORY_D.clone()).await.unwrap(); + let root_digest = handle.close().await.unwrap(); + assert_eq!( + DIRECTORY_D.digest(), + root_digest, + "root digest should match" + ); + + // Ensure we can get the closure back out of the service, and it is returned in a valid order + // (there are multiple valid possibilities) + let retrieved_closure = directory_service + .get_recursive(&DIRECTORY_D.digest()) + .collect::<Vec<_>>() + .await; + + let valid_closures = [ + vec![ + Ok(DIRECTORY_D.clone()), + Ok(DIRECTORY_B.clone()), + Ok(DIRECTORY_A.clone()), + ], + vec![ + Ok(DIRECTORY_D.clone()), + Ok(DIRECTORY_A.clone()), + Ok(DIRECTORY_B.clone()), + ], + ]; + if !valid_closures.contains(&retrieved_closure) { + panic!("invalid closure returned: {:?}", retrieved_closure); + } +} + /// Uploading A, then C (referring to A twice), then B (itself referring to A) should fail during close, /// as B itself would be left unconnected. #[apply(directory_services)] @@ -161,58 +210,20 @@ async fn upload_reject_dangling_pointer(directory_service: impl DirectoryService } } -/// Try uploading a Directory failing its internal validation, ensure it gets -/// rejected. -#[apply(directory_services)] -#[tokio::test] -async fn upload_reject_failing_validation(directory_service: impl DirectoryService) { - let broken_directory = Directory { - symlinks: vec![proto::SymlinkNode { - name: "".into(), // wrong! - target: "doesntmatter".into(), - }], - ..Default::default() - }; - assert!(broken_directory.validate().is_err()); - - // Try to upload via single upload. - assert!( - directory_service - .put(broken_directory.clone()) - .await - .is_err(), - "single upload must fail" - ); - - // Try to upload via put_multiple. We're a bit more permissive here, the - // intermediate .put() might succeed, due to client-side bursting (in the - // case of gRPC), but then the close MUST fail. - let mut handle = directory_service.put_multiple_start(); - if handle.put(broken_directory).await.is_ok() { - assert!( - handle.close().await.is_err(), - "when succeeding put, close must fail" - ) - } -} - /// Try uploading a Directory that refers to a previously-uploaded directory. /// Both pass their isolated validation, but the size field in the parent is wrong. /// This should be rejected. #[apply(directory_services)] #[tokio::test] async fn upload_reject_wrong_size(directory_service: impl DirectoryService) { - let wrong_parent_directory = Directory { - directories: vec![proto::DirectoryNode { - name: "foo".into(), - digest: DIRECTORY_A.digest().into(), + let wrong_parent_directory = Directory::try_from_iter([( + "foo".try_into().unwrap(), + Node::Directory { + digest: DIRECTORY_A.digest(), size: DIRECTORY_A.size() + 42, // wrong! - }], - ..Default::default() - }; - - // Make sure isolated validation itself is ok - assert!(wrong_parent_directory.validate().is_ok()); + }, + )]) + .unwrap(); // Now upload both. Ensure it either fails during the second put, or during // the close. |