20
20
21
21
from botocore .exceptions import ClientError
22
22
from s3transfer .compat import SOCKET_ERROR
23
- from s3transfer .exceptions import RetriesExceededError , S3DownloadFailedError
23
+ from s3transfer .exceptions import (
24
+ RetriesExceededError ,
25
+ S3DownloadFailedError ,
26
+ S3ValidationError ,
27
+ )
24
28
from s3transfer .manager import TransferConfig , TransferManager
25
29
26
30
from tests import (
@@ -109,7 +113,7 @@ def add_head_object_response(self, expected_params=None):
109
113
self .stubber .add_response (** head_response )
110
114
111
115
def add_successful_get_object_responses (
112
- self , expected_params = None , expected_ranges = None
116
+ self , expected_params = None , expected_ranges = None , extras = None
113
117
):
114
118
# Add all get_object responses needed to complete the download.
115
119
# Should account for both ranged and nonranged downloads.
@@ -124,6 +128,8 @@ def add_successful_get_object_responses(
124
128
stubbed_response ['expected_params' ]['Range' ] = (
125
129
expected_ranges [i ]
126
130
)
131
+ if extras :
132
+ stubbed_response ['service_response' ].update (extras [i ])
127
133
self .stubber .add_response (** stubbed_response )
128
134
129
135
def add_n_retryable_get_object_responses (self , n , num_reads = 0 ):
@@ -511,9 +517,12 @@ def test_download(self):
511
517
'RequestPayer' : 'requester' ,
512
518
}
513
519
expected_ranges = ['bytes=0-3' , 'bytes=4-7' , 'bytes=8-' ]
520
+ stubbed_ranges = ['bytes 0-3/10' , 'bytes 4-7/10' , 'bytes 8-9/10' ]
514
521
self .add_head_object_response (expected_params )
515
522
self .add_successful_get_object_responses (
516
- {** expected_params , 'IfMatch' : self .etag }, expected_ranges
523
+ {** expected_params , 'IfMatch' : self .etag },
524
+ expected_ranges ,
525
+ [{"ContentRange" : r } for r in stubbed_ranges ],
517
526
)
518
527
519
528
future = self .manager .download (
@@ -547,6 +556,28 @@ def test_download_with_checksum_enabled(self):
547
556
with open (self .filename , 'rb' ) as f :
548
557
self .assertEqual (self .content , f .read ())
549
558
559
+ def test_download_raises_if_content_range_mismatch (self ):
560
+ expected_params = {
561
+ 'Bucket' : self .bucket ,
562
+ 'Key' : self .key ,
563
+ }
564
+ expected_ranges = ['bytes=0-3' , 'bytes=4-7' , 'bytes=8-' ]
565
+ # Note that the final retrieved range should be `bytes 8-9/10`.
566
+ stubbed_ranges = ['bytes 0-3/10' , 'bytes 4-7/10' , 'bytes 7-8/10' ]
567
+ self .add_head_object_response (expected_params )
568
+ self .add_successful_get_object_responses (
569
+ {** expected_params , 'IfMatch' : self .etag },
570
+ expected_ranges ,
571
+ [{"ContentRange" : r } for r in stubbed_ranges ],
572
+ )
573
+
574
+ future = self .manager .download (
575
+ self .bucket , self .key , self .filename , self .extra_args
576
+ )
577
+ with self .assertRaises (S3ValidationError ) as e :
578
+ future .result ()
579
+ self .assertIn ('does not match content range' , str (e .exception ))
580
+
550
581
def test_download_raises_if_etag_validation_fails (self ):
551
582
expected_params = {
552
583
'Bucket' : self .bucket ,
0 commit comments