@@ -21,6 +21,7 @@ use mock_instant::thread_local::MockClock;
2121use nativelink_config:: stores:: { CommonObjectSpec , ExperimentalGcsSpec } ;
2222use nativelink_error:: { Code , Error , make_err} ;
2323use nativelink_macro:: nativelink_test;
24+ use nativelink_store:: cas_utils:: ZERO_BYTE_DIGESTS ;
2425use nativelink_store:: gcs_client:: client:: GcsOperations ;
2526use nativelink_store:: gcs_client:: mocks:: { MockGcsOperations , MockRequest } ;
2627use nativelink_store:: gcs_client:: types:: { DEFAULT_CONTENT_TYPE , ObjectPath } ;
@@ -230,6 +231,83 @@ async fn simple_update() -> Result<(), Error> {
230231 Ok ( ( ) )
231232}
232233
234+ #[ nativelink_test]
235+ async fn update_zero_length ( ) -> Result < ( ) , Error > {
236+ // Create mock GCS operations
237+ let mock_ops = Arc :: new ( MockGcsOperations :: new ( ) ) ;
238+ let store = create_test_store ( mock_ops. clone ( ) ) . await ?;
239+
240+ let digest = ZERO_BYTE_DIGESTS [ 0 ] ;
241+ let store_key: StoreKey = to_store_key ( digest) ;
242+ let ( mut tx, rx) = make_buf_channel_pair ( ) ;
243+
244+ // Start update operation
245+ let store_clone = store. clone ( ) ;
246+ let update_fut = nativelink_util:: spawn!( "update_task" , async move {
247+ store_clone
248+ . update( store_key, rx, UploadSizeInfo :: ExactSize ( 0 ) )
249+ . await
250+ } ) ;
251+
252+ tx. send_eof ( ) ?;
253+ update_fut. await ??;
254+
255+ // Verify the mock operations were called correctly
256+ let requests = mock_ops. get_requests ( ) . await ;
257+ let write_requests = requests. iter ( ) . filter_map ( |req| {
258+ if let MockRequest :: Write {
259+ object_path,
260+ content_len,
261+ } = req
262+ {
263+ Some ( ( object_path, content_len) )
264+ } else {
265+ None
266+ }
267+ } ) ;
268+
269+ assert_eq ! ( write_requests. count( ) , 0 , "Expected no write request" ) ;
270+
271+ Ok ( ( ) )
272+ }
273+
274+ #[ nativelink_test]
275+ async fn update_zero_digest_with_data ( ) -> Result < ( ) , Error > {
276+ const DATA_SIZE : usize = 50 ;
277+
278+ // Create mock GCS operations
279+ let mock_ops = Arc :: new ( MockGcsOperations :: new ( ) ) ;
280+ let store = create_test_store ( mock_ops. clone ( ) ) . await ?;
281+
282+ // Create test data
283+ let mut send_data = BytesMut :: new ( ) ;
284+ for i in 0 ..DATA_SIZE {
285+ send_data. put_u8 ( u8:: try_from ( ( i % 93 ) + 33 ) . unwrap ( ) ) ;
286+ }
287+ let send_data = send_data. freeze ( ) ;
288+
289+ let digest = ZERO_BYTE_DIGESTS [ 0 ] ;
290+ let store_key: StoreKey = to_store_key ( digest) ;
291+ let ( mut tx, rx) = make_buf_channel_pair ( ) ;
292+
293+ // Start update operation
294+ let store_clone = store. clone ( ) ;
295+ let update_fut = nativelink_util:: spawn!( "update_task" , async move {
296+ store_clone
297+ . update( store_key, rx, UploadSizeInfo :: ExactSize ( DATA_SIZE as u64 ) )
298+ . await
299+ } ) ;
300+
301+ tx. send ( send_data) . await ?;
302+ tx. send_eof ( ) ?;
303+ assert ! (
304+ update_fut. await ?. is_err( ) ,
305+ "No error for zero byte digest with data"
306+ ) ;
307+
308+ Ok ( ( ) )
309+ }
310+
233311#[ nativelink_test]
234312async fn get_part_test ( ) -> Result < ( ) , Error > {
235313 // Create mock GCS operations
0 commit comments