diff options
-rw-r--r-- | tvix/store/src/tests/fixtures.rs | 72 | ||||
-rw-r--r-- | tvix/store/src/tests/nar_renderer.rs | 210 |
2 files changed, 103 insertions, 179 deletions
diff --git a/tvix/store/src/tests/fixtures.rs b/tvix/store/src/tests/fixtures.rs index 1c8359a2c0c7..48cc365365a9 100644 --- a/tvix/store/src/tests/fixtures.rs +++ b/tvix/store/src/tests/fixtures.rs @@ -1,11 +1,13 @@ use lazy_static::lazy_static; -use rstest::*; +use rstest::{self, *}; +use rstest_reuse::*; +use std::io; use std::sync::Arc; pub use tvix_castore::fixtures::*; use tvix_castore::{ blobservice::{BlobService, MemoryBlobService}, directoryservice::{DirectoryService, MemoryDirectoryService}, - proto as castorepb, + proto as castorepb, Node, }; use crate::proto::{ @@ -17,6 +19,10 @@ pub const DUMMY_PATH: &str = "00000000000000000000000000000000-dummy"; pub const DUMMY_PATH_DIGEST: [u8; 20] = [0; 20]; lazy_static! { + pub static ref CASTORE_NODE_SYMLINK: Node = Node::Symlink { + target: "/nix/store/somewhereelse".try_into().unwrap(), + }; + /// The NAR representation of a symlink pointing to `/nix/store/somewhereelse` pub static ref NAR_CONTENTS_SYMLINK: Vec<u8> = vec![ 13, 0, 0, 0, 0, 0, 0, 0, b'n', b'i', b'x', b'-', b'a', b'r', b'c', b'h', b'i', b'v', b'e', b'-', b'1', 0, @@ -31,6 +37,12 @@ lazy_static! { 1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0 // ")" ]; + pub static ref CASTORE_NODE_HELLOWORLD: Node = Node::File { + digest: HELLOWORLD_BLOB_DIGEST.clone(), + size: HELLOWORLD_BLOB_CONTENTS.len() as u64, + executable: false, + }; + /// The NAR representation of a regular file with the contents "Hello World!" pub static ref NAR_CONTENTS_HELLOWORLD: Vec<u8> = vec![ 13, 0, 0, 0, 0, 0, 0, 0, b'n', b'i', b'x', b'-', b'a', b'r', b'c', b'h', b'i', b'v', b'e', b'-', b'1', 0, @@ -44,6 +56,22 @@ lazy_static! { 1, 0, 0, 0, 0, 0, 0, 0, b')', 0, 0, 0, 0, 0, 0, 0 // ")" ]; + pub static ref CASTORE_NODE_TOO_BIG: Node = Node::File { + digest: HELLOWORLD_BLOB_DIGEST.clone(), + size: 42, // <- note the wrong size here! + executable: false, + }; + pub static ref CASTORE_NODE_TOO_SMALL: Node = Node::File { + digest: HELLOWORLD_BLOB_DIGEST.clone(), + size: 2, // <- note the wrong size here! + executable: false, + }; + + pub static ref CASTORE_NODE_COMPLICATED: Node = Node::Directory { + digest: DIRECTORY_COMPLICATED.digest(), + size: DIRECTORY_COMPLICATED.size(), + }; + /// The NAR representation of a more complicated directory structure. pub static ref NAR_CONTENTS_COMPLICATED: Vec<u8> = vec![ 13, 0, 0, 0, 0, 0, 0, 0, b'n', b'i', b'x', b'-', b'a', b'r', b'c', b'h', b'i', b'v', b'e', b'-', b'1', 0, @@ -137,6 +165,46 @@ pub(crate) fn blob_service() -> Arc<dyn BlobService> { } #[fixture] +pub(crate) async fn blob_service_with_contents() -> Arc<dyn BlobService> { + let blob_service = Arc::from(MemoryBlobService::default()); + for (blob_contents, blob_digest) in [ + (EMPTY_BLOB_CONTENTS, &*EMPTY_BLOB_DIGEST), + (HELLOWORLD_BLOB_CONTENTS, &*HELLOWORLD_BLOB_DIGEST), + ] { + // put all data into the stores. + // insert blob into the store + let mut writer = blob_service.open_write().await; + tokio::io::copy(&mut io::Cursor::new(blob_contents), &mut writer) + .await + .unwrap(); + assert_eq!(blob_digest.clone(), writer.close().await.unwrap()); + } + blob_service +} + +#[fixture] pub(crate) fn directory_service() -> Arc<dyn DirectoryService> { Arc::from(MemoryDirectoryService::default()) } + +#[fixture] +pub(crate) async fn directory_service_with_contents() -> Arc<dyn DirectoryService> { + let directory_service = Arc::from(MemoryDirectoryService::default()); + for directory in [&*DIRECTORY_WITH_KEEP, &*DIRECTORY_COMPLICATED] { + directory_service.put(directory.clone()).await.unwrap(); + } + directory_service +} + +#[template] +#[rstest] +#[case::symlink (&*CASTORE_NODE_SYMLINK, Ok(Ok(&*NAR_CONTENTS_SYMLINK)))] +#[case::helloworld (&*CASTORE_NODE_HELLOWORLD, Ok(Ok(&*NAR_CONTENTS_HELLOWORLD)))] +#[case::too_big (&*CASTORE_NODE_TOO_BIG, Ok(Err(io::ErrorKind::UnexpectedEof)))] +#[case::too_small (&*CASTORE_NODE_TOO_SMALL, Ok(Err(io::ErrorKind::InvalidInput)))] +#[case::complicated(&*CASTORE_NODE_COMPLICATED, Ok(Ok(&*NAR_CONTENTS_COMPLICATED)))] +fn castore_fixtures_template( + #[case] test_input: &Node, + #[case] test_output: Result<Result<&Vec<u8>, io::ErrorKind>, crate::nar::RenderError>, +) { +} diff --git a/tvix/store/src/tests/nar_renderer.rs b/tvix/store/src/tests/nar_renderer.rs index 03eaa28aaac8..6314ba6ccc49 100644 --- a/tvix/store/src/tests/nar_renderer.rs +++ b/tvix/store/src/tests/nar_renderer.rs @@ -1,10 +1,7 @@ -use crate::nar::calculate_size_and_sha256; use crate::nar::write_nar; -use crate::tests::fixtures::blob_service; -use crate::tests::fixtures::directory_service; use crate::tests::fixtures::*; use rstest::*; -use sha2::{Digest, Sha256}; +use rstest_reuse::*; use std::io; use std::sync::Arc; use tokio::io::sink; @@ -12,29 +9,6 @@ use tvix_castore::blobservice::BlobService; use tvix_castore::directoryservice::DirectoryService; use tvix_castore::Node; -#[rstest] -#[tokio::test] -async fn single_symlink( - blob_service: Arc<dyn BlobService>, - directory_service: Arc<dyn DirectoryService>, -) { - let mut buf: Vec<u8> = vec![]; - - write_nar( - &mut buf, - &Node::Symlink { - target: "/nix/store/somewhereelse".try_into().unwrap(), - }, - // don't put anything in the stores, as we don't actually do any requests. - blob_service, - directory_service, - ) - .await - .expect("must succeed"); - - assert_eq!(buf, NAR_CONTENTS_SYMLINK.to_vec()); -} - /// Make sure the NARRenderer fails if a referred blob doesn't exist. #[rstest] #[tokio::test] @@ -44,11 +18,7 @@ async fn single_file_missing_blob( ) { let e = write_nar( sink(), - &Node::File { - digest: HELLOWORLD_BLOB_DIGEST.clone(), - size: HELLOWORLD_BLOB_CONTENTS.len() as u64, - executable: false, - }, + &CASTORE_NODE_HELLOWORLD, // the blobservice is empty intentionally, to provoke the error. blob_service, directory_service, @@ -64,158 +34,44 @@ async fn single_file_missing_blob( } } -/// Make sure the NAR Renderer fails if the returned blob meta has another size -/// than specified in the proto node. -#[rstest] -#[tokio::test] -async fn single_file_wrong_blob_size( - blob_service: Arc<dyn BlobService>, - directory_service: Arc<dyn DirectoryService>, -) { - // insert blob into the store - let mut writer = blob_service.open_write().await; - tokio::io::copy( - &mut io::Cursor::new(HELLOWORLD_BLOB_CONTENTS.to_vec()), - &mut writer, - ) - .await - .unwrap(); - assert_eq!( - HELLOWORLD_BLOB_DIGEST.clone(), - writer.close().await.unwrap() - ); - - // Test with a root FileNode of a too big size - let e = write_nar( - sink(), - &Node::File { - digest: HELLOWORLD_BLOB_DIGEST.clone(), - size: 42, // <- note the wrong size here! - executable: false, - }, - blob_service.clone(), - directory_service.clone(), - ) - .await - .expect_err("must fail"); - - match e { - crate::nar::RenderError::NARWriterError(e) => { - assert_eq!(io::ErrorKind::UnexpectedEof, e.kind()); - } - _ => panic!("unexpected error: {:?}", e), - } - - // Test with a root FileNode of a too small size - let e = write_nar( - sink(), - &Node::File { - digest: HELLOWORLD_BLOB_DIGEST.clone(), - size: 2, // <- note the wrong size here! - executable: false, - }, - blob_service, - directory_service, - ) - .await - .expect_err("must fail"); - - match e { - crate::nar::RenderError::NARWriterError(e) => { - assert_eq!(io::ErrorKind::InvalidInput, e.kind()); - } - _ => panic!("unexpected error: {:?}", e), - } -} - -#[rstest] -#[tokio::test] -async fn single_file( - blob_service: Arc<dyn BlobService>, - directory_service: Arc<dyn DirectoryService>, -) { - // insert blob into the store - let mut writer = blob_service.open_write().await; - tokio::io::copy(&mut io::Cursor::new(HELLOWORLD_BLOB_CONTENTS), &mut writer) - .await - .unwrap(); - - assert_eq!( - HELLOWORLD_BLOB_DIGEST.clone(), - writer.close().await.unwrap() - ); - - let mut buf: Vec<u8> = vec![]; - - write_nar( - &mut buf, - &Node::File { - digest: HELLOWORLD_BLOB_DIGEST.clone(), - size: HELLOWORLD_BLOB_CONTENTS.len() as u64, - executable: false, - }, - blob_service, - directory_service, - ) - .await - .expect("must succeed"); - - assert_eq!(buf, NAR_CONTENTS_HELLOWORLD.to_vec()); -} - -#[rstest] +#[apply(castore_fixtures_template)] #[tokio::test] -async fn test_complicated( - blob_service: Arc<dyn BlobService>, - directory_service: Arc<dyn DirectoryService>, +async fn seekable( + #[future] blob_service_with_contents: Arc<dyn BlobService>, + #[future] directory_service_with_contents: Arc<dyn DirectoryService>, + #[case] test_input: &Node, + #[case] test_output: Result<Result<&Vec<u8>, io::ErrorKind>, crate::nar::RenderError>, ) { - // put all data into the stores. - // insert blob into the store - let mut writer = blob_service.open_write().await; - tokio::io::copy(&mut io::Cursor::new(EMPTY_BLOB_CONTENTS), &mut writer) - .await - .unwrap(); - assert_eq!(EMPTY_BLOB_DIGEST.clone(), writer.close().await.unwrap()); - - // insert directories - directory_service - .put(DIRECTORY_WITH_KEEP.clone()) - .await - .unwrap(); - directory_service - .put(DIRECTORY_COMPLICATED.clone()) - .await - .unwrap(); + let blob_service = blob_service_with_contents.await; + let directory_service = directory_service_with_contents.await; let mut buf: Vec<u8> = vec![]; - - write_nar( + let read_result = write_nar( &mut buf, - &Node::Directory { - digest: DIRECTORY_COMPLICATED.digest(), - size: DIRECTORY_COMPLICATED.size(), - }, - blob_service.clone(), - directory_service.clone(), - ) - .await - .expect("must succeed"); - - assert_eq!(buf, NAR_CONTENTS_COMPLICATED.to_vec()); - - // ensure calculate_nar does return the correct sha256 digest and sum. - let (nar_size, nar_digest) = calculate_size_and_sha256( - &Node::Directory { - digest: DIRECTORY_COMPLICATED.digest(), - size: DIRECTORY_COMPLICATED.size(), - }, + test_input, + // don't put anything in the stores, as we don't actually do any requests. blob_service, directory_service, ) - .await - .expect("must succeed"); + .await; - assert_eq!(NAR_CONTENTS_COMPLICATED.len() as u64, nar_size); - let d = Sha256::digest(NAR_CONTENTS_COMPLICATED.clone()); - assert_eq!(d.as_slice(), nar_digest); + match (read_result, test_output) { + (Ok(_), Err(_)) => panic!("creating reader should have failed but succeeded"), + (Ok(_), Ok(Err(_))) => panic!("creating reader should have failed but succeeded"), + (Err(err), Ok(Ok(_))) => { + panic!("creating reader should have succeeded but failed: {}", err) + } + (Err(reader_err), Err(expected_err)) => { + assert_eq!(format!("{}", reader_err), format!("{}", expected_err)); + } + (Err(reader_err), Ok(Err(expected_err))) => { + let crate::nar::RenderError::NARWriterError(e) = reader_err else { + panic!("expected nar writer error") + }; + assert_eq!(e.kind(), expected_err); + } + (Ok(_n), Ok(Ok(expected_read_result))) => { + assert_eq!(buf, expected_read_result.to_vec()); + } + } } |