import time from unittest.mock import MagicMock from unittest.mock import call from boto.dynamodb2.table import Table from boto.exception import JSONResponseError from garcon.contrib.aws import dynamodb import pytest def test__get_current_wcu_and_rcu(monkeypatch): """ Test _get_current_wcu_and_rcu.""" table_model_mock = MagicMock() describe_mock = MagicMock() monkeypatch.setattr(Table, 'describe', describe_mock) sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) dynamodb._get_current_wcu_and_rcu(table_model_mock) table_model_mock.assert_has_calls([ call.describe(), call.describe().get('Table'), call.describe().get().get('ProvisionedThroughput'), call.describe().get().get().get('ReadCapacityUnits'), call.describe().get().get().get('WriteCapacityUnits')]) def test__is_throughput_changes_required_not_matches(monkeypatch): """Test _is_throughput_changes_required if throughput not matches.""" table_model_mock = MagicMock() sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) result = dynamodb._is_throughput_changes_required( table_model_mock, 200, None) assert not result def test__is_throughput_changes_required_matches(monkeypatch): """Test _is_throughput_changes_required if throughput not matches.""" table_model_mock = MagicMock() sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) result = dynamodb._is_throughput_changes_required( table_model_mock, 500, 1) assert result def test__sleep_if_table_is_busy(monkeypatch): """Test _sleep_if_table_is_busy.""" activity_mock = MagicMock() table_model_mock = MagicMock() table_model_mock.table_name = 'some_table' values = [{'Table': {'TableStatus': 'UPDATING'}}, {'Table': {'TableStatus': 'READY'}}] describe_mock = MagicMock(side_effect=values) table_model_mock.describe = describe_mock sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) dynamodb._sleep_if_table_is_busy(table_model_mock, activity=activity_mock) activity_mock.assert_has_calls([ call.__bool__(), call.logger.info( 'Waiting for the DynamoDB table some_table to be ready ' '(current status: UPDATING)'), call.heartbeat(details='Table some_table is not ready yet') ]) assert sleep_mock.call_count == 3 def test__update_table(monkeypatch): """Test _update_table.""" activity_mock = MagicMock() table_model_mock = MagicMock() update_mock = MagicMock() table_model_mock.update = update_mock sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) dynamodb._update_table( table_model_mock, activity_mock, required_wcu=None, required_rcu=10) update_mock.assert_called_once_with( throughput={'write': 200, 'read': 10}) def test_update_table_params_success(monkeypatch): """Test update_table_params.""" activity_mock = MagicMock() sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) table_model_mock = MagicMock() _update_table_mock = MagicMock() table_model_mock.update = _update_table_mock _is_throughput_changes_required_mock = MagicMock(return_value=True) monkeypatch.setattr( dynamodb, '_is_throughput_changes_required', _is_throughput_changes_required_mock) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) dynamodb.update_table_params( table_model_mock, required_wcu=None, required_rcu=130) _update_table_mock.assert_called_once_with( throughput={'read': 130, 'write': 200}) dynamodb.update_table_params(table_model_mock, activity_mock, None, None) _update_table_mock.assert_any_call( throughput={'read': 300, 'write': 200}) dynamodb.update_table_params(table_model_mock, activity_mock, 23, None) _update_table_mock.assert_any_call( throughput={'write': 23, 'read': 300}) dynamodb.update_table_params(table_model_mock, activity_mock, 1241, 214) _update_table_mock.assert_any_call( throughput={'write': 1241, 'read': 214}) def test_update_table_params_with_one_exception(monkeypatch): """Test update_table_params.""" activity_mock = MagicMock() sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) table_model_mock = MagicMock() exception = JSONResponseError( 400, 'Bad Request', body={ '__type': 'com.amazon.coral.availability#ThrottlingException', 'message': 'The rate of control plane requests made by this ' 'account is too high'}) _update_table_mock = MagicMock( side_effect=[exception, True]) table_model_mock.update = _update_table_mock _is_throughput_changes_required_mock = MagicMock(return_value=True) monkeypatch.setattr( dynamodb, '_is_throughput_changes_required', _is_throughput_changes_required_mock) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) dynamodb.update_table_params(table_model_mock, activity_mock, None, 130) _update_table_mock.assert_has_calls([ call(throughput={'write': 200, 'read': 130}), call(throughput={'write': 200, 'read': 130})]) def test_update_table_params_with_six_exceptions(monkeypatch): """Test update_table_params when 6 ThrottlingException in row.""" activity_mock = MagicMock() sleep_mock = MagicMock() monkeypatch.setattr(time, 'sleep', sleep_mock) table_model_mock = MagicMock() exception = JSONResponseError( 400, 'Bad Request', body={ '__type': 'com.amazon.coral.availability#ThrottlingException', 'message': 'The rate of control plane requests made by this ' 'account is too high'}) _update_table_mock = MagicMock( side_effect=[ exception, exception, exception, exception, exception, exception, True]) table_model_mock.update = _update_table_mock _is_throughput_changes_required_mock = MagicMock(return_value=True) monkeypatch.setattr( dynamodb, '_is_throughput_changes_required', _is_throughput_changes_required_mock) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) with pytest.raises(JSONResponseError): dynamodb.update_table_params( table_model_mock, activity_mock, None, 130) def test_set_table_throughput_executed(monkeypatch): """Test set_table_throughput (changing params).""" update_table_params_mock = MagicMock(return_value=True) get_current_wcu_and_rcu_mock = MagicMock(return_value=(200, 300)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) activity_mock = MagicMock() logger_mock = MagicMock() activity_mock.logger.info = logger_mock monkeypatch.setattr( dynamodb, 'update_table_params', update_table_params_mock) dynamodb.set_table_throughput(activity_mock, 'some_table', 10, 20) logger_mock.assert_called_once_with( 'The throughtput of the DynamoDB table some_table was updated ' '(RCU: 20, WCU: 10)') def test_set_table_throughput_not_executed(monkeypatch): """Test set_table_throughput (not changing params).""" update_table_params_mock = MagicMock(return_value=None) get_current_wcu_and_rcu_mock = MagicMock(return_value=(10, 20)) monkeypatch.setattr( dynamodb, '_get_current_wcu_and_rcu', get_current_wcu_and_rcu_mock) activity_mock = MagicMock() logger_mock = MagicMock() activity_mock.logger.info = logger_mock monkeypatch.setattr( dynamodb, 'update_table_params', update_table_params_mock) dynamodb.set_table_throughput(activity_mock, 'some_table', 10, 20) logger_mock.assert_called_once_with( 'The throughtput of the DynamoDB table some_table already matches the ' 'required values (RCU: 20, WCU: 10)') def test_wait_for_table_task_completion(monkeypatch): """Test wait_for_table_task_completion.""" activity_mock = MagicMock() logger_mock = MagicMock() activity_mock.logger.info = logger_mock _sleep_if_table_is_busy_mock = MagicMock() table_obj_mock = MagicMock() table_mock = MagicMock(return_value=table_obj_mock) monkeypatch.setattr(dynamodb, 'Table', table_mock) monkeypatch.setattr( dynamodb, '_sleep_if_table_is_busy', _sleep_if_table_is_busy_mock) dynamodb.wait_for_table_task_completion(activity_mock, 'some_table') _sleep_if_table_is_busy_mock.assert_called_with( table_obj_mock, activity=activity_mock)