# Copyright 2020 The Nomulus Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Unit tests of rolling_restart."""

import datetime
import unittest
from unittest import mock

import common
import rolling_restart
import steps

import appengine_test


class RollingRestartTestCase(unittest.TestCase):
    """Tests for rolling_restart."""
    def setUp(self) -> None:
        self._appengine_admin, self._appengine_request = (
            appengine_test.setup_appengine_admin())
        self._version = common.VersionKey('my_service', 'my_version')
        self.addCleanup(mock.patch.stopall)

    def _setup_execute_steps_tests(self):
        self._appengine_request.execute.side_effect = [
            # First list_instance response.
            {
                'instances': [{
                    'id': 'vm_to_delete',
                    'startTime': '2019-01-01T00:00:00Z'
                }, {
                    'id': 'vm_to_stay',
                    'startTime': '2019-01-01T00:00:00Z'
                }]
            },
            # Second list_instance response
            {
                'instances': [{
                    'id': 'vm_to_stay',
                    'startTime': '2019-01-01T00:00:00Z'
                }]
            },
            # Third list_instance response
            {
                'instances': [{
                    'id': 'vm_to_stay',
                    'startTime': '2019-01-01T00:00:00Z'
                }, {
                    'id': 'vm_new',
                    'startTime': '2019-01-01T00:00:00Z'
                }]
            }
        ]

    def _setup_generate_steps_tests(self):
        self._appengine_request.execute.side_effect = [
            # First page of list_instance response.
            {
                'instances': [{
                    'id': 'vm_2019',
                    'startTime': '2019-01-01T00:00:00Z'
                }],
                'nextPageToken':
                'token'
            },
            # Second and final page of list_instance response
            {
                'instances': [{
                    'id': 'vm_2020',
                    'startTime': '2020-01-01T00:00:00Z'
                }]
            }
        ]

    def test_kill_vm_command(self) -> None:
        cmd = steps.kill_nomulus_instance(
            'my_project', common.VersionKey('my_service', 'my_version'),
            'my_inst')
        self.assertEqual(cmd.instance_name, 'my_inst')
        self.assertIn(('gcloud app instances delete my_inst --quiet '
                       '--user-output-enabled=false --service my_service '
                       '--version my_version --project my_project'),
                      cmd.info())

    def _generate_kill_vm_command(self, version: common.VersionKey,
                                  instance_name: str):
        return steps.kill_nomulus_instance(self._appengine_admin.project,
                                           version, instance_name)

    def test_generate_commands(self):
        self._setup_generate_steps_tests()
        commands = rolling_restart.generate_steps(self._appengine_admin,
                                                  self._version,
                                                  datetime.datetime.utcnow())
        self.assertSequenceEqual(commands, [
            self._generate_kill_vm_command(self._version, 'vm_2019'),
            self._generate_kill_vm_command(self._version, 'vm_2020')
        ])

    def test_generate_commands_older_vm(self):
        self._setup_generate_steps_tests()
        version = common.VersionKey('my_service', 'my_version')
        # yapf: disable
        commands = rolling_restart.generate_steps(
            self._appengine_admin,
            version,
            common.parse_gcp_timestamp('2019-12-01T00:00:00Z'))
        # yapf: enable
        self.assertSequenceEqual(
            commands, [self._generate_kill_vm_command(version, 'vm_2019')])

    def test_execute_steps_variable_instances(self):
        self._setup_execute_steps_tests()
        cmd = mock.MagicMock()
        cmd.instance_name = 'vm_to_delete'
        cmds = tuple([cmd])  # yapf does not format (cmd,) correctly.
        rolling_restart.execute_steps(appengine_admin=self._appengine_admin,
                                      version=self._version,
                                      cmds=cmds,
                                      min_delay=0,
                                      configured_num_instances=None)
        self.assertEqual(self._appengine_request.execute.call_count, 2)

    def test_execute_steps_fixed_instances(self):
        self._setup_execute_steps_tests()
        cmd = mock.MagicMock()
        cmd.instance_name = 'vm_to_delete'
        cmds = tuple([cmd])  # yapf does not format (cmd,) correctly.
        rolling_restart.execute_steps(appengine_admin=self._appengine_admin,
                                      version=self._version,
                                      cmds=cmds,
                                      min_delay=0,
                                      configured_num_instances=2)
        self.assertEqual(self._appengine_request.execute.call_count, 3)


if __name__ == '__main__':
    unittest.main()
