chore(tvix/nix-compat): basic daemon handler tests

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>
This commit is contained in:
Vova Kryachko 2024-11-29 10:31:47 -05:00 committed by clbot
parent 8d4a0ac008
commit 88d51c9c16
5 changed files with 361 additions and 32 deletions

View file

@ -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()
);
}
}

View file

@ -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,