about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/store/src/tests/fixtures.rs72
-rw-r--r--tvix/store/src/tests/nar_renderer.rs210
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());
+        }
+    }
 }