class TestProviderInteractiveMode(unittest.TestCase): """Tests for runway.cfngin.providers.aws.default interactive mode.""" def setUp(self) -> None: """Run before tests.""" region = "us-east-1" self.session = get_session(region=region) self.provider = Provider(self.session, interactive=True, recreate_failed=True) self.stubber = Stubber(self.provider.cloudformation) @patch("runway.cfngin.ui.get_raw_input") def test_interactive_destroy_stack(self, patched_input: MagicMock) -> None: """Test interactive_destroy_stack.""" stack_name = "fake-stack" stack = {"StackName": stack_name} patched_input.return_value = "y" self.stubber.add_response("delete_stack", {}, stack) with self.stubber: self.assertIsNone(self.provider.interactive_destroy_stack(stack_name)) self.stubber.assert_no_pending_responses() @patch("runway.cfngin.providers.aws.default.Provider.update_termination_protection") @patch("runway.cfngin.ui.get_raw_input") def test_interactive_destroy_stack_termination_protected( self, patched_input: MagicMock, patched_update_term: MagicMock ) -> None: """Test interactive_destroy_stack with termination protection.""" stack_name = "fake-stack" stack = {"StackName": stack_name} patched_input.return_value = "y" self.stubber.add_client_error( "delete_stack", service_message="TerminationProtection" ) self.stubber.add_response("delete_stack", {}, stack) with self.stubber: self.provider.interactive_destroy_stack(stack_name, approval="y") self.stubber.assert_no_pending_responses() patched_input.assert_called_once() patched_update_term.assert_called_once_with(stack_name, False) @patch("runway.cfngin.ui.get_raw_input") def test_destroy_stack_canceled(self, patched_input: MagicMock) -> None: """Test destroy stack canceled.""" patched_input.return_value = "n" with self.assertRaises(exceptions.CancelExecution): stack = {"StackName": "MockStack"} self.provider.destroy_stack(stack) # type: ignore def test_successful_init(self) -> None: """Test successful init.""" replacements = True provider = Provider( self.session, interactive=True, replacements_only=replacements ) self.assertEqual(provider.replacements_only, replacements) @patch("runway.cfngin.providers.aws.default.Provider.update_termination_protection") @patch("runway.cfngin.providers.aws.default.ask_for_approval") def test_update_stack_execute_success_no_stack_policy( self, patched_approval: MagicMock, patched_update_term: MagicMock ) -> None: """Test update stack execute success no stack policy.""" stack_name = "my-fake-stack" self.stubber.add_response( "create_change_set", {"Id": "CHANGESETID", "StackId": "STACKID"} ) changes = [generate_change()] self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes ), ) self.stubber.add_response("execute_change_set", {}) with self.stubber: self.provider.update_stack( fqn=stack_name, template=Template(url="http://fake.template.url.com/"), old_parameters=[], parameters=[], tags=[], ) patched_approval.assert_called_once_with( full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name ) patched_update_term.assert_called_once_with(stack_name, False) @patch("runway.cfngin.providers.aws.default.Provider.update_termination_protection") @patch("runway.cfngin.providers.aws.default.ask_for_approval") def test_update_stack_execute_success_with_stack_policy( self, patched_approval: MagicMock, patched_update_term: MagicMock ) -> None: """Test update stack execute success with stack policy.""" stack_name = "my-fake-stack" self.stubber.add_response( "create_change_set", {"Id": "CHANGESETID", "StackId": "STACKID"} ) changes = [generate_change()] self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes ), ) self.stubber.add_response("set_stack_policy", {}) self.stubber.add_response("execute_change_set", {}) with self.stubber: self.provider.update_stack( fqn=stack_name, template=Template(url="http://fake.template.url.com/"), old_parameters=[], parameters=[], tags=[], stack_policy=Template(body="{}"), ) patched_approval.assert_called_once_with( full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name ) patched_update_term.assert_called_once_with(stack_name, False) def test_select_destroy_method(self) -> None: """Test select destroy method.""" for i in [ [{"force_interactive": False}, self.provider.interactive_destroy_stack], [{"force_interactive": True}, self.provider.interactive_destroy_stack], ]: self.assertEqual( self.provider.select_destroy_method(**i[0]), i[1] # type: ignore ) def test_select_update_method(self) -> None: """Test select update method.""" for i in [ [ {"force_interactive": False, "force_change_set": False}, self.provider.interactive_update_stack, ], [ {"force_interactive": True, "force_change_set": False}, self.provider.interactive_update_stack, ], [ {"force_interactive": False, "force_change_set": True}, self.provider.interactive_update_stack, ], [ {"force_interactive": True, "force_change_set": True}, self.provider.interactive_update_stack, ], ]: self.assertEqual( self.provider.select_update_method(**i[0]), i[1] # type: ignore ) @patch("runway.cfngin.providers.aws.default.output_full_changeset") @patch("runway.cfngin.providers.aws.default.output_summary") def test_get_stack_changes_interactive( self, mock_output_summary: MagicMock, mock_output_full_cs: MagicMock ) -> None: """Test get stack changes interactive.""" stack_name = "MockStack" mock_stack = generate_stack_object(stack_name) self.stubber.add_response( "describe_stacks", {"Stacks": [generate_describe_stacks_stack(stack_name)]} ) self.stubber.add_response( "get_template", generate_get_template("cfn_template.yaml") ) self.stubber.add_response( "create_change_set", {"Id": "CHANGESETID", "StackId": stack_name} ) changes = [generate_change()] self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes ), ) self.stubber.add_response("delete_change_set", {}) self.stubber.add_response( "describe_stacks", {"Stacks": [generate_describe_stacks_stack(stack_name)]} ) with self.stubber: self.provider.get_stack_changes( stack=mock_stack, template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[], ) mock_output_summary.assert_called_with( stack_name, "changes", changes, [], replacements_only=False ) mock_output_full_cs.assert_called_with( full_changeset=changes, params_diff=[], fqn=stack_name )
class TestProviderInteractiveMode(unittest.TestCase): """Tests for runway.cfngin.providers.aws.default interactive mode.""" def setUp(self): """Run before tests.""" region = "us-east-1" self.session = get_session(region=region) self.provider = Provider(self.session, interactive=True, recreate_failed=True) self.stubber = Stubber(self.provider.cloudformation) @patch('runway.cfngin.ui.get_raw_input') def test_interactive_destroy_stack(self, patched_input): """Test interactive_destroy_stack.""" stack_name = 'fake-stack' stack = {'StackName': stack_name} patched_input.return_value = 'y' self.stubber.add_response('delete_stack', {}, stack) with self.stubber: self.assertIsNone( self.provider.interactive_destroy_stack(stack_name)) self.stubber.assert_no_pending_responses() @patch( 'runway.cfngin.providers.aws.default.Provider.update_termination_protection' ) @patch('runway.cfngin.ui.get_raw_input') def test_interactive_destroy_stack_termination_protected( self, patched_input, patched_update_term): """Test interactive_destroy_stack with termination protection.""" stack_name = 'fake-stack' stack = {'StackName': stack_name} patched_input.return_value = 'y' self.stubber.add_client_error('delete_stack', service_message='TerminationProtection') self.stubber.add_response('delete_stack', {}, stack) with self.stubber: self.provider.interactive_destroy_stack(stack_name, approval='y') self.stubber.assert_no_pending_responses() patched_input.assert_called_once() patched_update_term.assert_called_once_with(stack_name, False) @patch('runway.cfngin.ui.get_raw_input') def test_destroy_stack_canceled(self, patched_input): """Test destroy stack canceled.""" stack = {'StackName': 'MockStack'} patched_input.return_value = 'n' with self.assertRaises(exceptions.CancelExecution): self.provider.destroy_stack(stack) def test_successful_init(self): """Test successful init.""" replacements = True provider = Provider(self.session, interactive=True, replacements_only=replacements) self.assertEqual(provider.replacements_only, replacements) @patch( 'runway.cfngin.providers.aws.default.Provider.update_termination_protection' ) @patch("runway.cfngin.providers.aws.default.ask_for_approval") def test_update_stack_execute_success_no_stack_policy( self, patched_approval, patched_update_term): """Test update stack execute success no stack policy.""" stack_name = "my-fake-stack" self.stubber.add_response("create_change_set", { 'Id': 'CHANGESETID', 'StackId': 'STACKID' }) changes = [generate_change()] self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes, )) self.stubber.add_response("execute_change_set", {}) with self.stubber: self.provider.update_stack( fqn=stack_name, template=Template(url="http://fake.template.url.com/"), old_parameters=[], parameters=[], tags=[]) patched_approval.assert_called_once_with(full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name) patched_update_term.assert_called_once_with(stack_name, False) @patch( 'runway.cfngin.providers.aws.default.Provider.update_termination_protection' ) @patch("runway.cfngin.providers.aws.default.ask_for_approval") def test_update_stack_execute_success_with_stack_policy( self, patched_approval, patched_update_term): """Test update stack execute success with stack policy.""" stack_name = "my-fake-stack" self.stubber.add_response("create_change_set", { 'Id': 'CHANGESETID', 'StackId': 'STACKID' }) changes = [generate_change()] self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes, )) self.stubber.add_response("set_stack_policy", {}) self.stubber.add_response("execute_change_set", {}) with self.stubber: self.provider.update_stack( fqn=stack_name, template=Template(url="http://fake.template.url.com/"), old_parameters=[], parameters=[], tags=[], stack_policy=Template(body="{}"), ) patched_approval.assert_called_once_with(full_changeset=changes, params_diff=[], include_verbose=True, fqn=stack_name) patched_update_term.assert_called_once_with(stack_name, False) def test_select_destroy_method(self): """Test select destroy method.""" for i in [[{ 'force_interactive': False }, self.provider.interactive_destroy_stack], [{ 'force_interactive': True }, self.provider.interactive_destroy_stack]]: self.assertEqual(self.provider.select_destroy_method(**i[0]), i[1]) def test_select_update_method(self): """Test select update method.""" for i in [[{ 'force_interactive': False, 'force_change_set': False }, self.provider.interactive_update_stack], [{ 'force_interactive': True, 'force_change_set': False }, self.provider.interactive_update_stack], [{ 'force_interactive': False, 'force_change_set': True }, self.provider.interactive_update_stack], [{ 'force_interactive': True, 'force_change_set': True }, self.provider.interactive_update_stack]]: self.assertEqual(self.provider.select_update_method(**i[0]), i[1]) @patch('runway.cfngin.providers.aws.default.output_full_changeset') @patch('runway.cfngin.providers.aws.default.output_summary') def test_get_stack_changes_interactive(self, mock_output_summary, mock_output_full_cs): """Test get stack changes interactive.""" stack_name = "MockStack" mock_stack = generate_stack_object(stack_name) self.stubber.add_response( 'describe_stacks', {'Stacks': [generate_describe_stacks_stack(stack_name)]}) self.stubber.add_response('get_template', generate_get_template('cfn_template.yaml')) self.stubber.add_response("create_change_set", { 'Id': 'CHANGESETID', 'StackId': stack_name }) changes = [] changes.append(generate_change()) self.stubber.add_response( "describe_change_set", generate_change_set_response( status="CREATE_COMPLETE", execution_status="AVAILABLE", changes=changes, )) self.stubber.add_response("delete_change_set", {}) self.stubber.add_response( 'describe_stacks', {'Stacks': [generate_describe_stacks_stack(stack_name)]}) with self.stubber: self.provider.get_stack_changes( stack=mock_stack, template=Template(url="http://fake.template.url.com/"), parameters=[], tags=[]) mock_output_summary.assert_called_with(stack_name, 'changes', changes, [], replacements_only=False) mock_output_full_cs.assert_called_with(full_changeset=changes, params_diff=[], fqn=stack_name)