nix_compat/nix_daemon/
mod.rs1pub mod worker_protocol;
2
3use std::io::Result;
4
5use futures::future::try_join_all;
6use tokio::io::AsyncRead;
7use tracing::warn;
8use types::{AddToStoreNarRequest, QueryValidPaths, UnkeyedValidPathInfo};
9
10use crate::store_path::StorePath;
11
12pub mod framing;
13pub mod handler;
14pub mod types;
15
16#[cfg(test)]
17use mockall::automock;
18
19#[cfg_attr(test, automock)]
21pub trait NixDaemonIO: Sync {
22 fn is_valid_path(
23 &self,
24 path: &StorePath<String>,
25 ) -> impl std::future::Future<Output = Result<bool>> + Send {
26 async move { Ok(self.query_path_info(path).await?.is_some()) }
27 }
28
29 fn query_path_info(
30 &self,
31 path: &StorePath<String>,
32 ) -> impl std::future::Future<Output = Result<Option<UnkeyedValidPathInfo>>> + Send;
33
34 fn query_path_from_hash_part(
35 &self,
36 hash: &[u8],
37 ) -> impl std::future::Future<Output = Result<Option<UnkeyedValidPathInfo>>> + Send;
38
39 fn query_valid_paths(
40 &self,
41 request: &QueryValidPaths,
42 ) -> impl std::future::Future<Output = Result<Vec<UnkeyedValidPathInfo>>> + Send {
43 async move {
44 if request.substitute {
45 warn!("tvix does not yet support substitution, ignoring the 'substitute' flag...");
46 }
47 let results =
52 try_join_all(request.paths.iter().map(|path| self.query_path_info(path))).await?;
53
54 Ok(results.into_iter().flatten().collect())
55 }
56 }
57
58 fn query_valid_derivers(
59 &self,
60 path: &StorePath<String>,
61 ) -> impl std::future::Future<Output = Result<Vec<StorePath<String>>>> + Send {
62 async move {
63 let result = self.query_path_info(path).await?;
64 let result: Vec<_> = result.into_iter().filter_map(|info| info.deriver).collect();
65 Ok(result)
66 }
67 }
68
69 #[cfg_attr(test, mockall::concretize)]
70 fn add_to_store_nar<R>(
71 &self,
72 request: AddToStoreNarRequest,
73 reader: &mut R,
74 ) -> impl std::future::Future<Output = Result<()>> + Send
75 where
76 R: AsyncRead + Send + Unpin;
77}
78
79#[cfg(test)]
80mod tests {
81
82 use crate::{nix_daemon::types::QueryValidPaths, store_path::StorePath};
83
84 use super::{types::UnkeyedValidPathInfo, NixDaemonIO};
85
86 pub struct MockNixDaemonIO {
89 query_path_info_result: Option<UnkeyedValidPathInfo>,
90 }
91
92 impl NixDaemonIO for MockNixDaemonIO {
93 async fn query_path_info(
94 &self,
95 _path: &StorePath<String>,
96 ) -> std::io::Result<Option<UnkeyedValidPathInfo>> {
97 Ok(self.query_path_info_result.clone())
98 }
99
100 async fn query_path_from_hash_part(
101 &self,
102 _hash: &[u8],
103 ) -> std::io::Result<Option<UnkeyedValidPathInfo>> {
104 Ok(None)
105 }
106
107 async fn add_to_store_nar<R>(
108 &self,
109 _request: super::types::AddToStoreNarRequest,
110 _reader: &mut R,
111 ) -> std::io::Result<()>
112 where
113 R: tokio::io::AsyncRead + Send + Unpin,
114 {
115 Ok(())
116 }
117 }
118
119 #[tokio::test]
120 async fn test_is_valid_path_returns_true() {
121 let path =
122 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
123 .unwrap();
124 let io = MockNixDaemonIO {
125 query_path_info_result: Some(UnkeyedValidPathInfo::default()),
126 };
127
128 let result = io
129 .is_valid_path(&path)
130 .await
131 .expect("expected to get a non-empty response");
132 assert!(result, "expected to get true");
133 }
134
135 #[tokio::test]
136 async fn test_is_valid_path_returns_false() {
137 let path =
138 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
139 .unwrap();
140 let io = MockNixDaemonIO {
141 query_path_info_result: None,
142 };
143
144 let result = io
145 .is_valid_path(&path)
146 .await
147 .expect("expected to get a non-empty response");
148 assert!(!result, "expected to get false");
149 }
150
151 #[tokio::test]
152 async fn test_query_valid_paths_returns_empty_response() {
153 let path =
154 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
155 .unwrap();
156 let io = MockNixDaemonIO {
157 query_path_info_result: None,
158 };
159
160 let result = io
161 .query_valid_paths(&QueryValidPaths {
162 paths: vec![path],
163 substitute: false,
164 })
165 .await
166 .expect("expected to get a non-empty response");
167 assert_eq!(result, vec![], "expected to get empty response");
168 }
169
170 #[tokio::test]
171 async fn test_query_valid_paths_returns_non_empty_response() {
172 let path =
173 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
174 .unwrap();
175 let io = MockNixDaemonIO {
176 query_path_info_result: Some(UnkeyedValidPathInfo::default()),
177 };
178
179 let result = io
180 .query_valid_paths(&QueryValidPaths {
181 paths: vec![path],
182 substitute: false,
183 })
184 .await
185 .expect("expected to get a non-empty response");
186 assert_eq!(
187 result,
188 vec![UnkeyedValidPathInfo::default()],
189 "expected to get non empty response"
190 );
191 }
192
193 #[tokio::test]
194 async fn test_query_valid_derivers_returns_empty_response() {
195 let path =
196 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
197 .unwrap();
198 let io = MockNixDaemonIO {
199 query_path_info_result: None,
200 };
201
202 let result = io
203 .query_valid_derivers(&path)
204 .await
205 .expect("expected to get a non-empty response");
206 assert_eq!(result, vec![], "expected to get empty response");
207 }
208
209 #[tokio::test]
210 async fn test_query_valid_derivers_returns_non_empty_response() {
211 let path =
212 StorePath::<String>::from_bytes("z6r3bn5l51679pwkvh9nalp6c317z34m-hello".as_bytes())
213 .unwrap();
214 let deriver = StorePath::<String>::from_bytes(
215 "z6r3bn5l51679pwkvh9nalp6c317z34m-hello.drv".as_bytes(),
216 )
217 .unwrap();
218 let io = MockNixDaemonIO {
219 query_path_info_result: Some(UnkeyedValidPathInfo {
220 deriver: Some(deriver.clone()),
221 nar_hash: "".to_owned(),
222 references: vec![],
223 registration_time: 0,
224 nar_size: 1,
225 ultimate: true,
226 signatures: vec![],
227 ca: None,
228 }),
229 };
230
231 let result = io
232 .query_valid_derivers(&path)
233 .await
234 .expect("expected to get a non-empty response");
235 assert_eq!(result, vec![deriver], "expected to get non empty response");
236 }
237}