diff options
author | Vova Kryachko <v.kryachko@gmail.com> | 2024-11-29T15·31-0500 |
---|---|---|
committer | clbot <clbot@tvl.fyi> | 2024-12-01T17·58+0000 |
commit | 88d51c9c1647c583fd70a46fec4b3d6f7ac72797 (patch) | |
tree | e747c1b7c55274c8ed26ddc515e0d0aca5d179c5 | |
parent | 8d4a0ac008f4e86e340242e394c74d08ca401f92 (diff) |
chore(tvix/nix-compat): basic daemon handler tests r/8970
This change adds tests for basic request/response handling as per nix daemon STDERR_* protocol. Change-Id: Ia6a1904e14955551b11f776b6ccb595fa8984513 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12852 Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de> Autosubmit: Vladimir Kryachko <v.kryachko@gmail.com>
-rw-r--r-- | tvix/Cargo.lock | 71 | ||||
-rw-r--r-- | tvix/Cargo.nix | 165 | ||||
-rw-r--r-- | tvix/nix-compat/Cargo.toml | 1 | ||||
-rw-r--r-- | tvix/nix-compat/src/nix_daemon/handler.rs | 151 | ||||
-rw-r--r-- | tvix/nix-compat/src/nix_daemon/mod.rs | 5 |
5 files changed, 361 insertions, 32 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock index 80e31a8ec9d4..73724b57a0e1 100644 --- a/tvix/Cargo.lock +++ b/tvix/Cargo.lock @@ -1046,6 +1046,12 @@ dependencies = [ ] [[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1249,6 +1255,12 @@ dependencies = [ ] [[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] name = "fuse-backend-rs" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2175,6 +2187,32 @@ dependencies = [ ] [[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] name = "multimap" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2290,6 +2328,7 @@ dependencies = [ "glob", "hex-literal", "mimalloc", + "mockall", "nix-compat-derive", "nom", "num-traits", @@ -2793,6 +2832,32 @@ dependencies = [ ] [[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] name = "pretty_assertions" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3932,6 +3997,12 @@ dependencies = [ ] [[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] name = "test-strategy" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix index 42f793d1d881..5045d5149ea8 100644 --- a/tvix/Cargo.nix +++ b/tvix/Cargo.nix @@ -3332,6 +3332,19 @@ rec { features = { }; resolvedDefaultFeatures = [ "default" ]; }; + "downcast" = rec { + crateName = "downcast"; + version = "0.11.0"; + edition = "2018"; + sha256 = "1wa78ahlc57wmqyq2ncr80l7plrkgz57xsg7kfzgpcnqac8gld8l"; + authors = [ + "Felix Köpge <fkoep@mailbox.org>" + ]; + features = { + "default" = [ "std" ]; + }; + resolvedDefaultFeatures = [ "default" "std" ]; + }; "ed25519" = rec { crateName = "ed25519"; version = "2.2.3"; @@ -3930,6 +3943,18 @@ rec { }; resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; + "fragile" = rec { + crateName = "fragile"; + version = "2.0.0"; + edition = "2018"; + sha256 = "1ajfdnwdn921bhjlzyvsqvdgci8ab40ln6w9ly422lf8svb428bc"; + authors = [ + "Armin Ronacher <armin.ronacher@active-4.com>" + ]; + features = { + "slab" = [ "dep:slab" ]; + }; + }; "fuse-backend-rs" = rec { crateName = "fuse-backend-rs"; version = "0.12.0"; @@ -6794,6 +6819,77 @@ rec { }; resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ]; }; + "mockall" = rec { + crateName = "mockall"; + version = "0.13.1"; + edition = "2021"; + sha256 = "1lir70dd9cnsjlf20gi3i51ha9n7mlrkx74bx5gfszlcdk6bz9ir"; + authors = [ + "Alan Somers <asomers@gmail.com>" + ]; + dependencies = [ + { + name = "cfg-if"; + packageId = "cfg-if"; + } + { + name = "downcast"; + packageId = "downcast"; + } + { + name = "fragile"; + packageId = "fragile"; + } + { + name = "mockall_derive"; + packageId = "mockall_derive"; + } + { + name = "predicates"; + packageId = "predicates"; + usesDefaultFeatures = false; + } + { + name = "predicates-tree"; + packageId = "predicates-tree"; + } + ]; + features = { + "nightly" = [ "mockall_derive/nightly_derive" "downcast/nightly" ]; + }; + }; + "mockall_derive" = rec { + crateName = "mockall_derive"; + version = "0.13.1"; + edition = "2021"; + sha256 = "1608qajqrz23xbvv81alc6wm4l24as1bsqg4shdh3sggq8231ji5"; + procMacro = true; + authors = [ + "Alan Somers <asomers@gmail.com>" + ]; + dependencies = [ + { + name = "cfg-if"; + packageId = "cfg-if"; + } + { + name = "proc-macro2"; + packageId = "proc-macro2"; + } + { + name = "quote"; + packageId = "quote"; + } + { + name = "syn"; + packageId = "syn 2.0.79"; + features = [ "extra-traits" "full" ]; + } + ]; + features = { + "nightly_derive" = [ "proc-macro2/nightly" ]; + }; + }; "multimap" = rec { crateName = "multimap"; version = "0.10.0"; @@ -7296,6 +7392,10 @@ rec { packageId = "mimalloc"; } { + name = "mockall"; + packageId = "mockall"; + } + { name = "pretty_assertions"; packageId = "pretty_assertions"; } @@ -8891,6 +8991,64 @@ rec { }; resolvedDefaultFeatures = [ "simd" "std" ]; }; + "predicates" = rec { + crateName = "predicates"; + version = "3.1.2"; + edition = "2021"; + sha256 = "15rcyjax4ykflw5425wsyzcfkgl08c9zsa8sdlsrmhj0fv68d43y"; + authors = [ + "Nick Stevens <nick@bitcurry.com>" + ]; + dependencies = [ + { + name = "anstyle"; + packageId = "anstyle"; + } + { + name = "predicates-core"; + packageId = "predicates-core"; + } + ]; + features = { + "default" = [ "diff" "regex" "float-cmp" "normalize-line-endings" "color" ]; + "diff" = [ "dep:difflib" ]; + "float-cmp" = [ "dep:float-cmp" ]; + "normalize-line-endings" = [ "dep:normalize-line-endings" ]; + "regex" = [ "dep:regex" ]; + }; + }; + "predicates-core" = rec { + crateName = "predicates-core"; + version = "1.0.8"; + edition = "2021"; + sha256 = "0c8rl6d7qkcl773fw539h61fhlgdg7v9yswwb536hpg7x2z7g0df"; + libName = "predicates_core"; + authors = [ + "Nick Stevens <nick@bitcurry.com>" + ]; + + }; + "predicates-tree" = rec { + crateName = "predicates-tree"; + version = "1.0.11"; + edition = "2021"; + sha256 = "04zv0i9pjfrldnvyxf4y07n243nvk3n4g03w2k6nccgdjp8l1ds1"; + libName = "predicates_tree"; + authors = [ + "Nick Stevens <nick@bitcurry.com>" + ]; + dependencies = [ + { + name = "predicates-core"; + packageId = "predicates-core"; + } + { + name = "termtree"; + packageId = "termtree"; + } + ]; + + }; "pretty_assertions" = rec { crateName = "pretty_assertions"; version = "1.4.1"; @@ -12655,6 +12813,13 @@ rec { ]; }; + "termtree" = rec { + crateName = "termtree"; + version = "0.4.1"; + edition = "2018"; + sha256 = "0xkal5l2r3r9p9j90x35qy4npbdwxz4gskvbijs6msymaangas9k"; + + }; "test-strategy" = rec { crateName = "test-strategy"; version = "0.2.1"; diff --git a/tvix/nix-compat/Cargo.toml b/tvix/nix-compat/Cargo.toml index 160eb2c20c16..dc0943476437 100644 --- a/tvix/nix-compat/Cargo.toml +++ b/tvix/nix-compat/Cargo.toml @@ -47,6 +47,7 @@ criterion = { workspace = true, features = ["html_reports"] } futures = { workspace = true } hex-literal = { workspace = true } mimalloc = { workspace = true } +mockall = "0.13.1" pretty_assertions = { workspace = true } proptest = { workspace = true, features = ["std", "alloc", "tempfile"] } rstest = { workspace = true } diff --git a/tvix/nix-compat/src/nix_daemon/handler.rs b/tvix/nix-compat/src/nix_daemon/handler.rs index 4f43612114d8..6fb45bdb7e2d 100644 --- a/tvix/nix-compat/src/nix_daemon/handler.rs +++ b/tvix/nix-compat/src/nix_daemon/handler.rs @@ -254,45 +254,17 @@ where #[cfg(test)] mod tests { use super::*; - use std::{io::Result, sync::Arc}; + use std::{io::ErrorKind, sync::Arc}; + use mockall::predicate; use tokio::io::AsyncWriteExt; use crate::{ - nix_daemon::types::UnkeyedValidPathInfo, + nix_daemon::MockNixDaemonIO, wire::ProtocolVersion, worker_protocol::{ClientSettings, WORKER_MAGIC_1, WORKER_MAGIC_2}, }; - struct MockDaemonIO {} - - impl NixDaemonIO for MockDaemonIO { - async fn query_path_info( - &self, - _path: &crate::store_path::StorePath<String>, - ) -> Result<Option<UnkeyedValidPathInfo>> { - Ok(None) - } - - async fn query_path_from_hash_part( - &self, - _hash: &[u8], - ) -> Result<Option<UnkeyedValidPathInfo>> { - Ok(None) - } - - async fn add_to_store_nar<R>( - &self, - _request: crate::nix_daemon::types::AddToStoreNarRequest, - _reader: &mut R, - ) -> Result<()> - where - R: tokio::io::AsyncRead + Send + Unpin, - { - Ok(()) - } - } - #[tokio::test] async fn test_daemon_initialization() { let mut builder = tokio_test::io::Builder::new(); @@ -332,10 +304,125 @@ mod tests { .write(&[115, 116, 108, 97, 0, 0, 0, 0]) .build(); - let daemon = NixDaemon::initialize(Arc::new(MockDaemonIO {}), test_conn) + let mock = MockNixDaemonIO::new(); + let daemon = NixDaemon::initialize(Arc::new(mock), test_conn) .await .unwrap(); assert_eq!(daemon.client_settings, ClientSettings::default()); assert_eq!(daemon.protocol_version, ProtocolVersion::from_parts(1, 35)); } + + async fn serialize<T>(req: &T, protocol_version: ProtocolVersion) -> Vec<u8> + where + T: NixSerialize + Send, + { + let mut result: Vec<u8> = Vec::new(); + let mut w = NixWriter::builder() + .set_version(protocol_version) + .build(&mut result); + w.write_value(req).await.unwrap(); + w.flush().await.unwrap(); + result + } + + async fn respond<T>( + resp: &Result<T, std::io::Error>, + protocol_version: ProtocolVersion, + ) -> Vec<u8> + where + T: NixSerialize + Send, + { + let mut result: Vec<u8> = Vec::new(); + let mut w = NixWriter::builder() + .set_version(protocol_version) + .build(&mut result); + match resp { + Ok(value) => { + w.write_value(&STDERR_LAST).await.unwrap(); + w.write_value(value).await.unwrap(); + } + Err(e) => { + w.write_value(&STDERR_ERROR).await.unwrap(); + w.write_value(&NixError::new(format!("{:?}", e))) + .await + .unwrap(); + } + } + w.flush().await.unwrap(); + result + } + + #[tokio::test] + async fn test_handle_is_valid_path_ok() { + let version = ProtocolVersion::from_parts(1, 37); + let (io, mut handle) = tokio_test::io::Builder::new().build_with_handle(); + let mut mock = MockNixDaemonIO::new(); + let (reader, writer) = split(io); + let path: StorePath<String> = StorePath::<String>::from_absolute_path( + "/nix/store/33l4p0pn0mybmqzaxfkpppyh7vx1c74p-hello-2.12.1".as_bytes(), + ) + .unwrap(); + mock.expect_is_valid_path() + .with(predicate::eq(path.clone())) + .times(1) + .returning(|_| Box::pin(async { Ok(true) })); + + handle.read(&Into::<u64>::into(Operation::IsValidPath).to_le_bytes()); + handle.read(&serialize(&path, version).await); + handle.write(&respond(&Ok(true), version).await); + drop(handle); + + let mut daemon = NixDaemon::new( + Arc::new(mock), + version, + ClientSettings::default(), + NixReader::new(reader), + NixWriter::new(writer), + ); + assert_eq!( + ErrorKind::UnexpectedEof, + daemon + .handle_client() + .await + .expect_err("Expecting eof") + .kind() + ); + } + + #[tokio::test] + async fn test_handle_is_valid_path_err() { + let version = ProtocolVersion::from_parts(1, 37); + let (io, mut handle) = tokio_test::io::Builder::new().build_with_handle(); + let mut mock = MockNixDaemonIO::new(); + let (reader, writer) = split(io); + let path: StorePath<String> = StorePath::<String>::from_absolute_path( + "/nix/store/33l4p0pn0mybmqzaxfkpppyh7vx1c74p-hello-2.12.1".as_bytes(), + ) + .unwrap(); + mock.expect_is_valid_path() + .with(predicate::eq(path.clone())) + .times(1) + .returning(|_| Box::pin(async { Err(std::io::Error::other("hello")) })); + + handle.read(&Into::<u64>::into(Operation::IsValidPath).to_le_bytes()); + handle.read(&serialize(&path, version).await); + handle.write(&respond::<bool>(&Err(std::io::Error::other("hello")), version).await); + drop(handle); + + let mut daemon = NixDaemon::new( + Arc::new(mock), + version, + ClientSettings::default(), + NixReader::new(reader), + NixWriter::new(writer), + ); + assert_eq!( + ErrorKind::UnexpectedEof, + daemon + .handle_client() + .await + .expect_err("Expecting eof") + .kind() + ); + } } diff --git a/tvix/nix-compat/src/nix_daemon/mod.rs b/tvix/nix-compat/src/nix_daemon/mod.rs index b1fd15c04ed1..11d236a6a321 100644 --- a/tvix/nix-compat/src/nix_daemon/mod.rs +++ b/tvix/nix-compat/src/nix_daemon/mod.rs @@ -13,7 +13,11 @@ pub mod framing; pub mod handler; pub mod types; +#[cfg(test)] +use mockall::automock; + /// Represents all possible operations over the nix-daemon protocol. +#[cfg_attr(test, automock)] pub trait NixDaemonIO: Sync { fn is_valid_path( &self, @@ -62,6 +66,7 @@ pub trait NixDaemonIO: Sync { } } + #[cfg_attr(test, mockall::concretize)] fn add_to_store_nar<R>( &self, request: AddToStoreNarRequest, |