class TestLoad(unittest.TestCase):

    def setUp(self):
        self.patcher = patch('boto3.resource')
        self.resource_mock = self.patcher.start()

        self.cloudformation_mock = Mock()
        self.stack_mock = Mock()
        self.resource_mock.return_value = self.cloudformation_mock
        self.cloudformation_mock.Stack.return_value = self.stack_mock
        self.crassus = Crassus(None, None)
        self.crassus._stack_name = STACK_NAME
        self.crassus._output_topics = ANY_TOPIC

    def tearDown(self):
        self.patcher.stop()

    def test_deploy_stack_should_load_stack(self):
        self.crassus.load()
        self.stack_mock.load.assert_called_once_with()

    @patch('crassus.deployer.sqs_send_message')
    @patch('crassus.deployer.logger')
    def test_stack_load_throws_clienterror_exception(
            self, logger_mock, mock_sqs):
        self.stack_mock.load.side_effect = ClientError(
            {'Error': {'Code': 'ExpectedException', 'Message': ''}},
            'test_deploy_stack_should_notify_error_in_case_of_client_error')
        self.crassus.load()
        logger_mock.error.assert_called_once_with(ANY)
        self.assertEquals(mock_sqs.call_count, 1)

    """
class TestNotify(unittest.TestCase):
    STATUS = 'success'
    MESSAGE = 'ANY MESSAGE'

    @patch('crassus.deployer.sqs_send_message')
    @patch('boto3.resource')
    def test_should_notify_sns(self, resource_mock, mock_sqs):
        topic_mock = Mock()
        sns_mock = Mock()
        sns_mock.Topic.return_value = topic_mock
        resource_mock.return_value = sns_mock

        self.crassus = Crassus(None, None)
        self.crassus._stack_name = STACK_NAME
        self.crassus._output_topics = ANY_TOPIC

        self.crassus.notify(self.STATUS, self.MESSAGE)

        self.assertEquals(mock_sqs.call_count, 1)
        self.assertEquals(
            mock_sqs.call_args,
            call(['ANY_TOPIC'], {
                'status': 'success',
                'timestamp': ANY,
                'stackName': 'ANY_STACK',
                'version': '1.1',
                'message': 'ANY MESSAGE',
                'emitter': 'crassus'}))

    @patch('crassus.deployer.Crassus.output_topics', None)
    def test_should_do_gracefully_nothing(self):
        self.crassus = Crassus(None, None)
        self.crassus.notify('status', 'message')
    def setUp(self):
        self.patcher = patch('boto3.resource')
        self.resource_mock = self.patcher.start()

        self.cloudformation_mock = Mock()
        self.stack_mock = Mock()
        self.resource_mock.return_value = self.cloudformation_mock
        self.cloudformation_mock.Stack.return_value = self.stack_mock
        self.crassus = Crassus(None, None)
        self.crassus._stack_name = STACK_NAME
        self.crassus._output_topics = ANY_TOPIC
    def setUp(self):
        self.stack_mock = Mock()
        self.stack_mock.parameters = [
            {"ParameterKey": "KeyOne",
             "ParameterValue": "OriginalValueOne",
             },
            {"ParameterKey": "KeyTwo",
             "ParameterValue": "OriginalValueTwo",
             }
        ]

        self.update_parameters = {
            "version": 1,
            "stackName": "ANY_STACK",
            "region": "ANY_REGION",
            "parameters": {"KeyOne": "UpdateValueOne"},
        }

        self.expected_parameters = [
            {"ParameterKey": "KeyOne",
             "ParameterValue": "UpdateValueOne",
             },
            {"ParameterKey": "KeyTwo",
             "UsePreviousValue": True
             }
        ]
        self.context_mock = Mock(invoked_function_arn="any_arn",
                                 function_version="any_version")
        self.crassus = Crassus(None, self.context_mock)
        self.crassus._stack_update_parameters = \
            StackUpdateParameter(self.update_parameters)
        self.crassus.stack = self.stack_mock
        self.crassus._stack_name = STACK_NAME
        self.crassus._output_topics = ANY_TOPIC
        self.crassus.aws_lambda = Mock()
        self.crassus.aws_lambda.get_function_configuration.return_value = {
            'Description': dedent("""
                {"result_queue":[
                    "arn:aws:sns:eu-west-1:123456789012:crassus-output",
                    "arn:aws:sns:eu-west-1:123456789012:random-topic"
                    ],
                    "cfn_events": ["CFN-SNS-TOPIC-1"]}""")}
    def test_should_notify_sns(self, resource_mock, mock_sqs):
        topic_mock = Mock()
        sns_mock = Mock()
        sns_mock.Topic.return_value = topic_mock
        resource_mock.return_value = sns_mock

        self.crassus = Crassus(None, None)
        self.crassus._stack_name = STACK_NAME
        self.crassus._output_topics = ANY_TOPIC

        self.crassus.notify(self.STATUS, self.MESSAGE)

        self.assertEquals(mock_sqs.call_count, 1)
        self.assertEquals(
            mock_sqs.call_args,
            call(['ANY_TOPIC'], {
                'status': 'success',
                'timestamp': ANY,
                'stackName': 'ANY_STACK',
                'version': '1.1',
                'message': 'ANY MESSAGE',
                'emitter': 'crassus'}))
 def test_should_call_all_necessary_stuff(self, load_mock, update_mock):
     crassus = Crassus(None, None)
     crassus.deploy()
     load_mock.assert_called_once_with()
     update_mock.assert_called_once_with()
class TestUpdateStack(unittest.TestCase):

    def setUp(self):
        self.stack_mock = Mock()
        self.stack_mock.parameters = [
            {"ParameterKey": "KeyOne",
             "ParameterValue": "OriginalValueOne",
             },
            {"ParameterKey": "KeyTwo",
             "ParameterValue": "OriginalValueTwo",
             }
        ]

        self.update_parameters = {
            "version": 1,
            "stackName": "ANY_STACK",
            "region": "ANY_REGION",
            "parameters": {"KeyOne": "UpdateValueOne"},
        }

        self.expected_parameters = [
            {"ParameterKey": "KeyOne",
             "ParameterValue": "UpdateValueOne",
             },
            {"ParameterKey": "KeyTwo",
             "UsePreviousValue": True
             }
        ]
        self.context_mock = Mock(invoked_function_arn="any_arn",
                                 function_version="any_version")
        self.crassus = Crassus(None, self.context_mock)
        self.crassus._stack_update_parameters = \
            StackUpdateParameter(self.update_parameters)
        self.crassus.stack = self.stack_mock
        self.crassus._stack_name = STACK_NAME
        self.crassus._output_topics = ANY_TOPIC
        self.crassus.aws_lambda = Mock()
        self.crassus.aws_lambda.get_function_configuration.return_value = {
            'Description': dedent("""
                {"result_queue":[
                    "arn:aws:sns:eu-west-1:123456789012:crassus-output",
                    "arn:aws:sns:eu-west-1:123456789012:random-topic"
                    ],
                    "cfn_events": ["CFN-SNS-TOPIC-1"]}""")}

    @patch('crassus.deployer.Crassus.notify', Mock())
    @patch('crassus.deployer.get_lambda_config_property')
    def test_update_stack_should_call_update(self, mock_lambda):
        mock_lambda.return_value = ['CFN-SQS-QUEUE-1']
        self.crassus.update()
        self.stack_mock.update.assert_called_once_with(
            UsePreviousTemplate=True,
            Parameters=self.expected_parameters,
            Capabilities=['CAPABILITY_IAM'],
            NotificationARNs=['CFN-SQS-QUEUE-1'])
        self.assertEqual(self.crassus.cfn_output_topics, ['CFN-SQS-QUEUE-1'])

    @patch('crassus.deployer.get_lambda_config_property', Mock())
    @patch('crassus.deployer.Crassus.notify', Mock())
    @patch('crassus.deployer.logger')
    def test_update_stack_load_throws_clienterror_exception(self, logger_mock):
        self.stack_mock.update.side_effect = ClientError(
            {'Error': {'Code': 'ExpectedException', 'Message': ''}},
            'test_deploy_stack_should_notify_error_in_case_of_client_error')
        self.crassus.update()
        logger_mock.error.assert_called_once_with(ANY)

    """@patch('crassus.deployer.notify')
 def test_should_do_gracefully_nothing(self):
     self.crassus = Crassus(None, None)
     self.crassus.notify('status', 'message')