about summary refs log tree commit diff
path: root/tvix/store/src/pathinfoservice/tests/mod.rs
//! 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)]
#[cfg_attr(feature = "cloud", case::bigtable(make_path_info_service("bigtable://instance-1?project_id=project-1&table_name=table-1&family_name=cf1").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);
}