@@ -255,3 +255,55 @@ async def test_mrd_open_with_read_handle():
255255 del mrd
256256 del new_mrd
257257 gc .collect ()
258+
259+
260+ @pytest .mark .asyncio
261+ async def test_read_unfinalized_appendable_object_with_generation (
262+ storage_client , blobs_to_delete
263+ ):
264+ object_name = f"read_unfinalized_appendable_object-{ str (uuid .uuid4 ())[:4 ]} "
265+ grpc_client = AsyncGrpcClient (attempt_direct_path = True ).grpc_client
266+
267+ async def _read_and_verify (expected_content , generation = None ):
268+ """Helper to read object content and verify against expected."""
269+ mrd = AsyncMultiRangeDownloader (
270+ grpc_client , _ZONAL_BUCKET , object_name , generation
271+ )
272+ buffer = BytesIO ()
273+ await mrd .open ()
274+ try :
275+ assert mrd .persisted_size == len (expected_content )
276+ await mrd .download_ranges ([(0 , 0 , buffer )])
277+ assert buffer .getvalue () == expected_content
278+ finally :
279+ await mrd .close ()
280+ return mrd
281+
282+ # First write
283+ writer = AsyncAppendableObjectWriter (grpc_client , _ZONAL_BUCKET , object_name )
284+ await writer .open ()
285+ await writer .append (_BYTES_TO_UPLOAD )
286+ await writer .flush ()
287+ generation = writer .generation
288+
289+ # First read
290+ mrd = await _read_and_verify (_BYTES_TO_UPLOAD )
291+
292+ # Second write, using generation from the first write.
293+ writer_2 = AsyncAppendableObjectWriter (
294+ grpc_client , _ZONAL_BUCKET , object_name , generation = generation
295+ )
296+ await writer_2 .open ()
297+ await writer_2 .append (_BYTES_TO_UPLOAD )
298+ await writer_2 .flush ()
299+
300+ # Second read
301+ mrd_2 = await _read_and_verify (_BYTES_TO_UPLOAD + _BYTES_TO_UPLOAD , generation )
302+
303+ # Clean up
304+ blobs_to_delete .append (storage_client .bucket (_ZONAL_BUCKET ).blob (object_name ))
305+ del writer
306+ del writer_2
307+ del mrd
308+ del mrd_2
309+ gc .collect ()
0 commit comments