|
3 | 3 |
|
4 | 4 | # pylint: skip-file |
5 | 5 |
|
6 | | -import os |
7 | | -import boto3 |
| 6 | +import botocore |
| 7 | +from botocore.stub import Stubber |
8 | 8 | from pytest import fixture, raises |
9 | | -from stubs import stub_s3 |
10 | | -from mock import Mock |
| 9 | +from mock import Mock, patch, mock_open |
11 | 10 | from s3 import S3 |
12 | 11 |
|
13 | 12 | @fixture |
@@ -117,3 +116,263 @@ def test_build_pathing_style_unknown_style(us_east_1_cls): |
117 | 116 |
|
118 | 117 | error_message = str(excinfo.value) |
119 | 118 | assert error_message.find(correct_error_message) >= 0 |
| 119 | + |
| 120 | + |
| 121 | +@patch('s3.S3.build_pathing_style') |
| 122 | +@patch('s3.S3._perform_put_object') |
| 123 | +@patch('s3.S3._does_object_exist') |
| 124 | +def test_put_object_no_checks_always_upload(does_exist, perform_put, |
| 125 | + build_path, eu_west_1_cls): |
| 126 | + object_key = "some" |
| 127 | + object_path = "s3://bucket/{key}".format(key=object_key) |
| 128 | + file_path = "some_imaginary_file.json" |
| 129 | + path_style = "s3-url" |
| 130 | + does_exist.return_value = True |
| 131 | + build_path.return_value = object_path |
| 132 | + |
| 133 | + return_value = eu_west_1_cls.put_object( |
| 134 | + key=object_key, |
| 135 | + file_path=file_path, |
| 136 | + style=path_style, |
| 137 | + pre_check=False, |
| 138 | + ) |
| 139 | + |
| 140 | + assert return_value == object_path |
| 141 | + |
| 142 | + does_exist.assert_not_called() |
| 143 | + perform_put.assert_called_once_with(object_key, file_path) |
| 144 | + build_path.assert_called_once_with(path_style, object_key) |
| 145 | + |
| 146 | + |
| 147 | +@patch('s3.S3.build_pathing_style') |
| 148 | +@patch('s3.S3._perform_put_object') |
| 149 | +@patch('s3.S3._does_object_exist') |
| 150 | +def test_put_object_do_check_upload_when_missing( |
| 151 | + does_exist, perform_put, build_path, eu_west_1_cls): |
| 152 | + object_key = "some" |
| 153 | + object_path = "s3://bucket/{key}".format(key=object_key) |
| 154 | + file_path = "some_imaginary_file.json" |
| 155 | + path_style = "s3-url" |
| 156 | + does_exist.return_value = False |
| 157 | + build_path.return_value = object_path |
| 158 | + |
| 159 | + return_value = eu_west_1_cls.put_object( |
| 160 | + key=object_key, |
| 161 | + file_path=file_path, |
| 162 | + style=path_style, |
| 163 | + pre_check=True, |
| 164 | + ) |
| 165 | + |
| 166 | + assert return_value == object_path |
| 167 | + |
| 168 | + does_exist.assert_called_once_with(object_key) |
| 169 | + perform_put.assert_called_once_with(object_key, file_path) |
| 170 | + build_path.assert_called_once_with(path_style, object_key) |
| 171 | + |
| 172 | + |
| 173 | +@patch('s3.S3.build_pathing_style') |
| 174 | +@patch('s3.S3._perform_put_object') |
| 175 | +@patch('s3.S3._does_object_exist') |
| 176 | +def test_put_object_do_check_no_upload_object_present( |
| 177 | + does_exist, perform_put, build_path, eu_west_1_cls): |
| 178 | + object_key = "some" |
| 179 | + object_path = "s3://bucket/{key}".format(key=object_key) |
| 180 | + file_path = "some_imaginary_file.json" |
| 181 | + path_style = "s3-url" |
| 182 | + does_exist.return_value = True |
| 183 | + build_path.return_value = object_path |
| 184 | + |
| 185 | + return_value = eu_west_1_cls.put_object( |
| 186 | + key=object_key, |
| 187 | + file_path=file_path, |
| 188 | + style=path_style, |
| 189 | + pre_check=True, |
| 190 | + ) |
| 191 | + |
| 192 | + assert return_value == object_path |
| 193 | + |
| 194 | + does_exist.assert_called_once_with(object_key) |
| 195 | + perform_put.assert_not_called() |
| 196 | + build_path.assert_called_once_with(path_style, object_key) |
| 197 | + |
| 198 | + |
| 199 | +@patch('s3.boto3.client') |
| 200 | +def test_does_object_exist_yes(boto3_client): |
| 201 | + s3_client = botocore.session.get_session().create_client('s3') |
| 202 | + s3_client_stubber = Stubber(s3_client) |
| 203 | + boto3_client.return_value = s3_client |
| 204 | + object_key = "some" |
| 205 | + |
| 206 | + s3_cls = S3( |
| 207 | + 'eu-west-1', |
| 208 | + 'some_bucket' |
| 209 | + ) |
| 210 | + response = {} |
| 211 | + expected_params = { |
| 212 | + 'Bucket': s3_cls.bucket, |
| 213 | + 'Key': object_key, |
| 214 | + } |
| 215 | + s3_client_stubber.add_response('get_object', response, expected_params) |
| 216 | + s3_client_stubber.activate() |
| 217 | + |
| 218 | + assert s3_cls._does_object_exist(key=object_key) |
| 219 | + |
| 220 | + boto3_client.assert_called_once_with('s3', region_name='eu-west-1') |
| 221 | + s3_client_stubber.assert_no_pending_responses() |
| 222 | + |
| 223 | + |
| 224 | +@patch('s3.boto3.client') |
| 225 | +def test_does_object_exist_no(boto3_client): |
| 226 | + s3_client = botocore.session.get_session().create_client('s3') |
| 227 | + s3_client_stubber = Stubber(s3_client) |
| 228 | + boto3_client.return_value = s3_client |
| 229 | + object_key = "some" |
| 230 | + |
| 231 | + s3_cls = S3( |
| 232 | + 'eu-west-1', |
| 233 | + 'some_bucket' |
| 234 | + ) |
| 235 | + s3_client_stubber.add_client_error( |
| 236 | + 'get_object', |
| 237 | + expected_params={'Bucket': s3_cls.bucket, 'Key': object_key}, |
| 238 | + http_status_code=404, |
| 239 | + service_error_code='NoSuchKey', |
| 240 | + ) |
| 241 | + s3_client_stubber.activate() |
| 242 | + |
| 243 | + assert not s3_cls._does_object_exist(key=object_key) |
| 244 | + |
| 245 | + boto3_client.assert_called_once_with('s3', region_name='eu-west-1') |
| 246 | + s3_client_stubber.assert_no_pending_responses() |
| 247 | + |
| 248 | + |
| 249 | +@patch('s3.boto3.resource') |
| 250 | +@patch('s3.LOGGER') |
| 251 | +def test_perform_put_object_success(logger, boto3_resource): |
| 252 | + s3_resource = Mock() |
| 253 | + s3_object = Mock() |
| 254 | + s3_resource.Object.return_value = s3_object |
| 255 | + boto3_resource.return_value = s3_resource |
| 256 | + object_key = "some" |
| 257 | + file_path = "some-file.json" |
| 258 | + file_data = 'some file data' |
| 259 | + |
| 260 | + s3_cls = S3( |
| 261 | + 'eu-west-1', |
| 262 | + 'some_bucket' |
| 263 | + ) |
| 264 | + with patch("builtins.open", mock_open(read_data=file_data)) as mock_file: |
| 265 | + s3_cls._perform_put_object( |
| 266 | + key=object_key, |
| 267 | + file_path=file_path, |
| 268 | + ) |
| 269 | + mock_file.assert_called_with(file_path, 'rb') |
| 270 | + s3_resource.Object.assert_called_once_with(s3_cls.bucket, object_key) |
| 271 | + s3_object.put.assert_called_once_with(Body=mock_file.return_value) |
| 272 | + |
| 273 | + logger.info.assert_called_once_with( |
| 274 | + "Uploading %s as %s to S3 Bucket %s in %s", |
| 275 | + file_path, |
| 276 | + object_key, |
| 277 | + s3_cls.bucket, |
| 278 | + s3_cls.region, |
| 279 | + ) |
| 280 | + logger.debug.assert_called_once_with( |
| 281 | + "Upload of %s was successful.", |
| 282 | + object_key, |
| 283 | + ) |
| 284 | + logger.error.assert_not_called() |
| 285 | + boto3_resource.assert_called_with('s3', region_name='eu-west-1') |
| 286 | + |
| 287 | + |
| 288 | +@patch('s3.boto3.resource') |
| 289 | +@patch('s3.LOGGER') |
| 290 | +def test_perform_put_object_no_such_file(logger, boto3_resource): |
| 291 | + s3_resource = Mock() |
| 292 | + s3_object = Mock() |
| 293 | + s3_resource.Object.return_value = s3_object |
| 294 | + boto3_resource.return_value = s3_resource |
| 295 | + object_key = "some" |
| 296 | + file_path = "some-file.json" |
| 297 | + |
| 298 | + s3_cls = S3( |
| 299 | + 'eu-west-1', |
| 300 | + 'some_bucket' |
| 301 | + ) |
| 302 | + correct_error_message = "File not found exception" |
| 303 | + with patch("builtins.open") as mock_file: |
| 304 | + mock_file.side_effect = Exception(correct_error_message) |
| 305 | + with raises(Exception) as excinfo: |
| 306 | + s3_cls._perform_put_object( |
| 307 | + key=object_key, |
| 308 | + file_path=file_path, |
| 309 | + ) |
| 310 | + |
| 311 | + error_message = str(excinfo.value) |
| 312 | + assert error_message.find(correct_error_message) >= 0 |
| 313 | + |
| 314 | + mock_file.assert_called_with(file_path, 'rb') |
| 315 | + s3_resource.Object.assert_not_called() |
| 316 | + s3_object.put.assert_not_called() |
| 317 | + |
| 318 | + logger.info.assert_called_once_with( |
| 319 | + "Uploading %s as %s to S3 Bucket %s in %s", |
| 320 | + file_path, |
| 321 | + object_key, |
| 322 | + s3_cls.bucket, |
| 323 | + s3_cls.region, |
| 324 | + ) |
| 325 | + logger.debug.assert_not_called() |
| 326 | + logger.error.assert_called_once_with( |
| 327 | + "Failed to upload %s", |
| 328 | + object_key, |
| 329 | + exc_info=True, |
| 330 | + ) |
| 331 | + boto3_resource.assert_called_with('s3', region_name='eu-west-1') |
| 332 | + |
| 333 | + |
| 334 | +@patch('s3.boto3.resource') |
| 335 | +@patch('s3.LOGGER') |
| 336 | +def test_perform_put_object_failed(logger, boto3_resource): |
| 337 | + s3_resource = Mock() |
| 338 | + s3_object = Mock() |
| 339 | + s3_resource.Object.return_value = s3_object |
| 340 | + boto3_resource.return_value = s3_resource |
| 341 | + object_key = "some" |
| 342 | + file_path = "some-file.json" |
| 343 | + file_data = 'some file data' |
| 344 | + |
| 345 | + s3_cls = S3( |
| 346 | + 'eu-west-1', |
| 347 | + 'some_bucket' |
| 348 | + ) |
| 349 | + correct_error_message = "Test exception" |
| 350 | + s3_object.put.side_effect = Exception(correct_error_message) |
| 351 | + with patch("builtins.open", mock_open(read_data=file_data)) as mock_file: |
| 352 | + with raises(Exception) as excinfo: |
| 353 | + s3_cls._perform_put_object( |
| 354 | + key=object_key, |
| 355 | + file_path=file_path, |
| 356 | + ) |
| 357 | + |
| 358 | + error_message = str(excinfo.value) |
| 359 | + assert error_message.find(correct_error_message) >= 0 |
| 360 | + |
| 361 | + mock_file.assert_called_with(file_path, 'rb') |
| 362 | + s3_resource.Object.assert_called_once_with(s3_cls.bucket, object_key) |
| 363 | + s3_object.put.assert_called_once_with(Body=mock_file.return_value) |
| 364 | + |
| 365 | + logger.info.assert_called_once_with( |
| 366 | + "Uploading %s as %s to S3 Bucket %s in %s", |
| 367 | + file_path, |
| 368 | + object_key, |
| 369 | + s3_cls.bucket, |
| 370 | + s3_cls.region, |
| 371 | + ) |
| 372 | + logger.debug.assert_not_called() |
| 373 | + logger.error.assert_called_once_with( |
| 374 | + "Failed to upload %s", |
| 375 | + object_key, |
| 376 | + exc_info=True, |
| 377 | + ) |
| 378 | + boto3_resource.assert_called_with('s3', region_name='eu-west-1') |
0 commit comments