chore(tvix/store): move protos into separate mod.rs

This allows adding more stuff into this namespace, from different files.

Also move tests on proto-related code from src/tests to src/proto/tests.

Change-Id: I49e066fce90efbc18e16d68f94497b32ed5625c0
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8091
Reviewed-by: tazjin <tazjin@tvl.su>
Reviewed-by: raitobezarius <tvl@lahfa.xyz>
Tested-by: BuildkiteCI
This commit is contained in:
Florian Klink 2023-02-12 12:50:00 +01:00 committed by flokli
parent 60abca1d8e
commit 80f68bf828
6 changed files with 6 additions and 3 deletions

View file

@ -1,285 +0,0 @@
use crate::proto::{Directory, DirectoryNode, FileNode, SymlinkNode, ValidateDirectoryError};
use lazy_static::lazy_static;
lazy_static! {
static ref DUMMY_DIGEST: Vec<u8> = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
}
#[test]
fn size() {
{
let d = Directory::default();
assert_eq!(d.size(), 0);
}
{
let d = Directory {
directories: vec![DirectoryNode {
name: String::from("foo"),
digest: DUMMY_DIGEST.to_vec(),
size: 0,
}],
..Default::default()
};
assert_eq!(d.size(), 1);
}
{
let d = Directory {
directories: vec![DirectoryNode {
name: String::from("foo"),
digest: DUMMY_DIGEST.to_vec(),
size: 4,
}],
..Default::default()
};
assert_eq!(d.size(), 5);
}
{
let d = Directory {
files: vec![FileNode {
name: String::from("foo"),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
executable: false,
}],
..Default::default()
};
assert_eq!(d.size(), 1);
}
{
let d = Directory {
symlinks: vec![SymlinkNode {
name: String::from("foo"),
target: String::from("bar"),
}],
..Default::default()
};
assert_eq!(d.size(), 1);
}
}
#[test]
fn digest() {
let d = Directory::default();
assert_eq!(
d.digest(),
vec![
0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc,
0xc9, 0x49, 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca,
0xe4, 0x1f, 0x32, 0x62
]
)
}
#[test]
fn validate_empty() {
let d = Directory::default();
assert_eq!(d.validate(), Ok(()));
}
#[test]
fn validate_invalid_names() {
{
let d = Directory {
directories: vec![DirectoryNode {
name: "".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
}],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::InvalidName(n) => {
assert_eq!(n, "")
}
_ => panic!("unexpected error"),
};
}
{
let d = Directory {
directories: vec![DirectoryNode {
name: ".".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
}],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::InvalidName(n) => {
assert_eq!(n, ".")
}
_ => panic!("unexpected error"),
};
}
{
let d = Directory {
files: vec![FileNode {
name: "..".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
executable: false,
}],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::InvalidName(n) => {
assert_eq!(n, "..")
}
_ => panic!("unexpected error"),
};
}
{
let d = Directory {
symlinks: vec![SymlinkNode {
name: "\x00".to_string(),
target: "foo".to_string(),
}],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::InvalidName(n) => {
assert_eq!(n, "\x00")
}
_ => panic!("unexpected error"),
};
}
{
let d = Directory {
symlinks: vec![SymlinkNode {
name: "foo/bar".to_string(),
target: "foo".to_string(),
}],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::InvalidName(n) => {
assert_eq!(n, "foo/bar")
}
_ => panic!("unexpected error"),
};
}
}
#[test]
fn validate_invalid_digest() {
let d = Directory {
directories: vec![DirectoryNode {
name: "foo".to_string(),
digest: vec![0x00, 0x42], // invalid length
size: 42,
}],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::InvalidDigestLen(n) => {
assert_eq!(n, 2)
}
_ => panic!("unexpected error"),
}
}
#[test]
fn validate_sorting() {
// "b" comes before "a", bad.
{
let d = Directory {
directories: vec![
DirectoryNode {
name: "b".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
DirectoryNode {
name: "a".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::WrongSorting(s) => {
assert_eq!(s, "a".to_string());
}
_ => panic!("unexpected error"),
}
}
// "a" exists twice, bad.
{
let d = Directory {
directories: vec![
DirectoryNode {
name: "a".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
DirectoryNode {
name: "a".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
],
..Default::default()
};
match d.validate().expect_err("must fail") {
ValidateDirectoryError::DuplicateName(s) => {
assert_eq!(s, "a".to_string());
}
_ => panic!("unexpected error"),
}
}
// "a" comes before "b", all good.
{
let d = Directory {
directories: vec![
DirectoryNode {
name: "a".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
DirectoryNode {
name: "b".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
],
..Default::default()
};
d.validate().expect("validate shouldn't error");
}
// [b, c] and [a] are both properly sorted.
{
let d = Directory {
directories: vec![
DirectoryNode {
name: "b".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
DirectoryNode {
name: "c".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 42,
},
],
symlinks: vec![SymlinkNode {
name: "a".to_string(),
target: "foo".to_string(),
}],
..Default::default()
};
d.validate().expect("validate shouldn't error");
}
}

View file

@ -1,82 +0,0 @@
use crate::proto::node::Node;
use crate::proto::Directory;
use crate::proto::DirectoryNode;
use crate::proto::FileNode;
use crate::proto::SymlinkNode;
#[test]
fn iterator() -> anyhow::Result<()> {
let d = Directory {
directories: vec![
DirectoryNode {
name: "c".to_string(),
..DirectoryNode::default()
},
DirectoryNode {
name: "d".to_string(),
..DirectoryNode::default()
},
DirectoryNode {
name: "h".to_string(),
..DirectoryNode::default()
},
DirectoryNode {
name: "l".to_string(),
..DirectoryNode::default()
},
],
files: vec![
FileNode {
name: "b".to_string(),
..FileNode::default()
},
FileNode {
name: "e".to_string(),
..FileNode::default()
},
FileNode {
name: "g".to_string(),
..FileNode::default()
},
FileNode {
name: "j".to_string(),
..FileNode::default()
},
],
symlinks: vec![
SymlinkNode {
name: "a".to_string(),
..SymlinkNode::default()
},
SymlinkNode {
name: "f".to_string(),
..SymlinkNode::default()
},
SymlinkNode {
name: "i".to_string(),
..SymlinkNode::default()
},
SymlinkNode {
name: "k".to_string(),
..SymlinkNode::default()
},
],
};
let mut node_names: Vec<String> = vec![];
for node in d.nodes() {
match node {
Node::Directory(n) => node_names.push(n.name),
Node::File(n) => node_names.push(n.name),
Node::Symlink(n) => node_names.push(n.name),
};
}
assert_eq!(
vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
node_names
);
Ok(())
}

View file

@ -1,6 +1,3 @@
mod directory;
mod directory_nodes_iterator;
mod directory_service;
mod nar;
mod path_info_service;
mod pathinfo;

View file

@ -1,207 +0,0 @@
use crate::proto::{self, Node, PathInfo, ValidatePathInfoError};
use lazy_static::lazy_static;
use nix_compat::store_path::{ParseStorePathError, StorePath};
use test_case::test_case;
lazy_static! {
static ref DUMMY_DIGEST: Vec<u8> = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
static ref DUMMY_DIGEST_2: Vec<u8> = vec![
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
}
const DUMMY_NAME: &str = "00000000000000000000000000000000-dummy";
#[test_case(
None,
Err(ValidatePathInfoError::NoNodePresent()) ;
"No node"
)]
#[test_case(
Some(Node { node: None }),
Err(ValidatePathInfoError::NoNodePresent());
"No node 2"
)]
fn validate_no_node(
t_node: Option<proto::Node>,
t_result: Result<StorePath, ValidatePathInfoError>,
) {
// construct the PathInfo object
let p = PathInfo {
node: t_node,
..Default::default()
};
assert_eq!(t_result, p.validate());
}
#[test_case(
proto::DirectoryNode {
name: DUMMY_NAME.to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 0,
},
Ok(StorePath::from_string(DUMMY_NAME).expect("must succeed"));
"ok"
)]
#[test_case(
proto::DirectoryNode {
name: DUMMY_NAME.to_string(),
digest: vec![],
size: 0,
},
Err(ValidatePathInfoError::InvalidDigestLen(0));
"invalid digest length"
)]
#[test_case(
proto::DirectoryNode {
name: "invalid".to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 0,
},
Err(ValidatePathInfoError::InvalidNodeName(
"invalid".to_string(),
ParseStorePathError::InvalidName("".to_string())
));
"invalid node name"
)]
fn validate_directory(
t_directory_node: proto::DirectoryNode,
t_result: Result<StorePath, ValidatePathInfoError>,
) {
// construct the PathInfo object
let p = PathInfo {
node: Some(Node {
node: Some(proto::node::Node::Directory(t_directory_node)),
}),
..Default::default()
};
assert_eq!(t_result, p.validate());
}
#[test_case(
proto::FileNode {
name: DUMMY_NAME.to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 0,
executable: false,
},
Ok(StorePath::from_string(DUMMY_NAME).expect("must succeed"));
"ok"
)]
#[test_case(
proto::FileNode {
name: DUMMY_NAME.to_string(),
digest: vec![],
..Default::default()
},
Err(ValidatePathInfoError::InvalidDigestLen(0));
"invalid digest length"
)]
#[test_case(
proto::FileNode {
name: "invalid".to_string(),
digest: DUMMY_DIGEST.to_vec(),
..Default::default()
},
Err(ValidatePathInfoError::InvalidNodeName(
"invalid".to_string(),
ParseStorePathError::InvalidName("".to_string())
));
"invalid node name"
)]
fn validate_file(t_file_node: proto::FileNode, t_result: Result<StorePath, ValidatePathInfoError>) {
// construct the PathInfo object
let p = PathInfo {
node: Some(Node {
node: Some(proto::node::Node::File(t_file_node)),
}),
..Default::default()
};
assert_eq!(t_result, p.validate());
}
#[test_case(
proto::SymlinkNode {
name: DUMMY_NAME.to_string(),
..Default::default()
},
Ok(StorePath::from_string(DUMMY_NAME).expect("must succeed"));
"ok"
)]
#[test_case(
proto::SymlinkNode {
name: "invalid".to_string(),
..Default::default()
},
Err(ValidatePathInfoError::InvalidNodeName(
"invalid".to_string(),
ParseStorePathError::InvalidName("".to_string())
));
"invalid node name"
)]
fn validate_symlink(
t_symlink_node: proto::SymlinkNode,
t_result: Result<StorePath, ValidatePathInfoError>,
) {
// construct the PathInfo object
let p = PathInfo {
node: Some(Node {
node: Some(proto::node::Node::Symlink(t_symlink_node)),
}),
..Default::default()
};
assert_eq!(t_result, p.validate());
}
#[test]
fn validate_references() {
// create a PathInfo without narinfo field.
let path_info = PathInfo {
node: Some(Node {
node: Some(proto::node::Node::Directory(proto::DirectoryNode {
name: DUMMY_NAME.to_string(),
digest: DUMMY_DIGEST.to_vec(),
size: 0,
})),
}),
references: vec![DUMMY_DIGEST_2.to_vec()],
narinfo: None,
};
assert!(path_info.validate().is_ok());
// create a PathInfo with a narinfo field, but an inconsistent set of references
let path_info_with_narinfo_missing_refs = PathInfo {
narinfo: Some(proto::NarInfo {
nar_size: 0,
nar_sha256: DUMMY_DIGEST.to_vec(),
signatures: vec![],
reference_names: vec![],
}),
..path_info.clone()
};
match path_info_with_narinfo_missing_refs
.validate()
.expect_err("must_fail")
{
ValidatePathInfoError::InconsistentNumberOfReferences(_, _) => {}
_ => panic!("unexpected error"),
};
// create a pathinfo with the correct number of references, should suceed
let path_info_with_narinfo = PathInfo {
narinfo: Some(proto::NarInfo {
nar_size: 0,
nar_sha256: DUMMY_DIGEST.to_vec(),
signatures: vec![],
reference_names: vec![format!("/nix/store/{}", DUMMY_NAME)],
}),
..path_info.clone()
};
assert!(path_info_with_narinfo.validate().is_ok());
}