1616from unittest import mock
1717from unittest .mock import AsyncMock
1818from google .cloud import _storage_v2
19+ from google .api_core import exceptions
20+ from google_crc32c import Checksum
1921
2022from google .cloud .storage ._experimental .asyncio .async_multi_range_downloader import (
2123 AsyncMultiRangeDownloader ,
2224)
25+ from google .cloud .storage ._experimental .asyncio import async_read_object_stream
2326from io import BytesIO
27+ from google .cloud .storage .exceptions import DataCorruption
2428
2529
2630_TEST_BUCKET_NAME = "test-bucket"
@@ -114,6 +118,10 @@ async def test_download_ranges(
114118 self , mock_grpc_client , mock_cls_async_read_object_stream
115119 ):
116120 # Arrange
121+ data = b"these_are_18_chars"
122+ crc32c = Checksum (data ).digest ()
123+ crc32c_int = int .from_bytes (crc32c , "big" )
124+
117125 mock_mrd = await self ._make_mock_mrd (
118126 mock_grpc_client , mock_cls_async_read_object_stream
119127 )
@@ -123,7 +131,7 @@ async def test_download_ranges(
123131 object_data_ranges = [
124132 _storage_v2 .ObjectRangeData (
125133 checksummed_data = _storage_v2 .ChecksummedData (
126- content = b"these_are_18_chars" , crc32c = 123
134+ content = data , crc32c = crc32c_int
127135 ),
128136 range_end = True ,
129137 read_range = _storage_v2 .ReadRange (
@@ -148,7 +156,7 @@ async def test_download_ranges(
148156 assert len (results ) == 1
149157 assert results [0 ].bytes_requested == 18
150158 assert results [0 ].bytes_written == 18
151- assert buffer .getvalue () == b"these_are_18_chars"
159+ assert buffer .getvalue () == data
152160
153161 @mock .patch (
154162 "google.cloud.storage._experimental.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client"
@@ -251,3 +259,55 @@ async def test_downloading_without_opening_should_throw_error(
251259 # Assert
252260 assert str (exc .value ) == "Underlying bidi-gRPC stream is not open"
253261 assert not mrd .is_stream_open
262+
263+ @mock .patch ("google.cloud.storage._experimental.asyncio.async_multi_range_downloader.google_crc32c" )
264+ @mock .patch ("google.cloud.storage._experimental.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client" )
265+ def test_init_raises_if_crc32c_c_extension_is_missing (
266+ self , mock_grpc_client , mock_google_crc32c
267+ ):
268+ mock_google_crc32c .implementation = "python"
269+
270+ with pytest .raises (exceptions .NotFound ) as exc_info :
271+ AsyncMultiRangeDownloader (
272+ mock_grpc_client , "bucket" , "object"
273+ )
274+
275+ assert "The google-crc32c package is not installed with C support" in str (exc_info .value )
276+
277+ @pytest .mark .asyncio
278+ @mock .patch ("google.cloud.storage._experimental.asyncio.async_multi_range_downloader.Checksum" )
279+ @mock .patch ("google.cloud.storage._experimental.asyncio.async_grpc_client.AsyncGrpcClient.grpc_client" )
280+ async def test_download_ranges_raises_on_checksum_mismatch (self , mock_client , mock_checksum_class ):
281+ mock_stream = mock .AsyncMock (spec = async_read_object_stream ._AsyncReadObjectStream )
282+
283+ test_data = b"some-data"
284+ server_checksum = 12345
285+ mock_checksum_instance = mock_checksum_class .return_value
286+ mock_checksum_instance .digest .return_value = (54321 ).to_bytes (4 , "big" )
287+
288+ mock_response = _storage_v2 .BidiReadObjectResponse (
289+ object_data_ranges = [
290+ _storage_v2 .ObjectRangeData (
291+ checksummed_data = _storage_v2 .ChecksummedData (
292+ content = test_data , crc32c = server_checksum
293+ ),
294+ read_range = _storage_v2 .ReadRange (read_id = 0 ),
295+ range_end = True ,
296+ )
297+ ]
298+ )
299+
300+ mock_stream .recv .side_effect = [mock_response , None ]
301+
302+ mrd = AsyncMultiRangeDownloader (
303+ mock_client , "bucket" , "object"
304+ )
305+ mrd .read_obj_str = mock_stream
306+ mrd ._is_stream_open = True
307+
308+ with pytest .raises (DataCorruption ) as exc_info :
309+ await mrd .download_ranges ([(0 , len (test_data ), BytesIO ())])
310+
311+ assert "Checksum mismatch" in str (exc_info .value )
312+ mock_checksum_class .assert_called_once_with (test_data )
313+
0 commit comments