# coding: utf-8
#
# Copyright 2019 The Oppia 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 for scripts/release_scripts/update_configs.py."""

from __future__ import absolute_import  # pylint: disable=import-only-modules
from __future__ import unicode_literals  # pylint: disable=import-only-modules

import getpass
import os
import tempfile

from core.tests import test_utils
import python_utils
from scripts import common
from scripts.release_scripts import update_configs

import github  # isort:skip pylint: disable=wrong-import-position

INVALID_FECONF_CONFIG_PATH = os.path.join(
    os.getcwd(), 'core', 'tests', 'release_sources',
    'invalid_feconf_updates.config')
VALID_FECONF_CONFIG_PATH = os.path.join(
    os.getcwd(), 'core', 'tests', 'release_sources',
    'valid_feconf_updates.config')
FECONF_CONFIG_PATH_WITH_EXTRA_LINE = os.path.join(
    os.getcwd(), 'core', 'tests', 'release_sources',
    'feconf_updates_with_extra_line.config')
MOCK_LOCAL_FECONF_PATH = os.path.join(
    os.getcwd(), 'core', 'tests', 'release_sources',
    'feconf.txt')


class UpdateConfigsTests(test_utils.GenericTestBase):
    """Test the methods for updating configs."""

    def setUp(self):
        super(UpdateConfigsTests, self).setUp()
        self.mock_repo = github.Repository.Repository(
            requester='', headers='', attributes={}, completed='')
        def mock_get_organization(unused_self, unused_name):
            return github.Organization.Organization(
                requester='', headers='', attributes={}, completed='')
        def mock_get_repo(unused_self, unused_org):
            return self.mock_repo
        def mock_open_tab(unused_url):
            pass
        def mock_getpass(prompt):  # pylint: disable=unused-argument
            return 'test-token'
        def mock_url_open(unused_url):
            pass

        self.get_org_swap = self.swap(
            github.Github, 'get_organization', mock_get_organization)
        self.get_repo_swap = self.swap(
            github.Organization.Organization, 'get_repo', mock_get_repo)
        self.open_tab_swap = self.swap(
            common, 'open_new_tab_in_browser_if_possible', mock_open_tab)
        self.getpass_swap = self.swap(getpass, 'getpass', mock_getpass)
        self.url_open_swap = self.swap(python_utils, 'url_open', mock_url_open)

    def test_missing_terms_page(self):
        def mock_url_open(unused_url):
            raise Exception('Not found.')
        url_open_swap = self.swap(python_utils, 'url_open', mock_url_open)
        with url_open_swap, self.assertRaisesRegexp(
            Exception, 'Terms mainpage does not exist on Github.'):
            update_configs.main(
                'test-release-dir', 'test-deploy-dir', 'test-token', True)

    def test_invalid_user_input(self):
        print_msgs = []
        def mock_input():
            if print_msgs:
                return 'n'
            else:
                return 'invalid'
        def mock_print(msg):
            if 'Invalid Input' in msg:
                print_msgs.append(msg)
        input_swap = self.swap(python_utils, 'INPUT', mock_input)
        print_swap = self.swap(python_utils, 'PRINT', mock_print)
        with self.getpass_swap, self.get_org_swap, self.get_repo_swap:
            with self.open_tab_swap, input_swap, print_swap:
                update_configs.check_updates_to_terms_of_service(
                    MOCK_LOCAL_FECONF_PATH, 'test-token')
        self.assertEqual(
            print_msgs, ['Invalid Input: invalid. Please enter yes or no.'])

    def test_update_to_registration_updated_date(self):
        def mock_input():
            return 'y'
        def mock_get_commit(unused_self, unused_sha):
            return github.Commit.Commit(
                requester='', headers='',
                attributes={
                    'commit': {'committer': {'date': '2016-11-15T3:41:01Z'}}},
                completed='')
        input_swap = self.swap(python_utils, 'INPUT', mock_input)
        get_commit_swap = self.swap(
            github.Repository.Repository, 'get_commit', mock_get_commit)

        temp_feconf_path = tempfile.NamedTemporaryFile().name
        feconf_text = (
            '# When the site terms were last updated, in UTC.\n'
            'REGISTRATION_PAGE_LAST_UPDATED_UTC = '
            'datetime.datetime(2015, 10, 14, 2, 40, 0)\n'
            '# Format of string for dashboard statistics logs.\n'
            '# NOTE TO DEVELOPERS: This format should not be changed, '
            'since it is used in\n'
            '# the existing storage models for UserStatsModel.\n'
            'DASHBOARD_STATS_DATETIME_STRING_FORMAT = \'%Y-%m-%d\'\n')
        expected_feconf_text = feconf_text.replace(
            'datetime.datetime(2015, 10, 14, 2, 40, 0)',
            'datetime.datetime(2016, 11, 15, 3, 41, 1)')
        with python_utils.open_file(temp_feconf_path, 'w') as f:
            f.write(feconf_text)

        with self.getpass_swap, self.get_org_swap, self.get_repo_swap:
            with self.open_tab_swap, input_swap, get_commit_swap:
                update_configs.check_updates_to_terms_of_service(
                    temp_feconf_path, 'test-token')
        with python_utils.open_file(temp_feconf_path, 'r') as f:
            self.assertEqual(f.read(), expected_feconf_text)

    def test_missing_mailgun_api_key_line(self):
        mailgun_api_key = ('key-%s' % ('').join(['1'] * 32))
        def mock_getpass(prompt):  # pylint: disable=unused-argument
            return mailgun_api_key
        getpass_swap = self.swap(getpass, 'getpass', mock_getpass)

        temp_feconf_path = tempfile.NamedTemporaryFile().name
        feconf_text = (
            'REDISHOST = \'192.13.2.1\'\n'
            '# When the site terms were last updated, in UTC.\n'
            'REGISTRATION_PAGE_LAST_UPDATED_UTC = '
            'datetime.datetime(2015, 10, 14, 2, 40, 0)\n'
            '# Format of string for dashboard statistics logs.\n'
            '# NOTE TO DEVELOPERS: This format should not be changed, '
            'since it is used in\n'
            '# the existing storage models for UserStatsModel.\n'
            'DASHBOARD_STATS_DATETIME_STRING_FORMAT = \'%Y-%m-%d\'\n')
        with python_utils.open_file(temp_feconf_path, 'w') as f:
            f.write(feconf_text)

        with getpass_swap, self.assertRaisesRegexp(
            AssertionError, 'Missing mailgun API key'):
            update_configs.add_mailgun_api_key(temp_feconf_path)

    def test_invalid_mailgun_api_key(self):
        check_prompts = {
            'Enter mailgun api key from the release process doc.': False,
            'You have entered an invalid mailgun api key: invalid, '
            'please retry.': False
        }
        expected_check_prompts = {
            'Enter mailgun api key from the release process doc.': True,
            'You have entered an invalid mailgun api key: invalid, '
            'please retry.': True
        }
        mailgun_api_key = ('key-%s' % ('').join(['1'] * 32))
        def mock_getpass(prompt):
            check_prompts[prompt] = True
            if 'invalid' in prompt:
                return mailgun_api_key
            return 'invalid'
        getpass_swap = self.swap(getpass, 'getpass', mock_getpass)

        temp_feconf_path = tempfile.NamedTemporaryFile().name
        feconf_text = (
            'REDISHOST = \'192.13.2.1\'\n'
            'MAILGUN_API_KEY = None\n'
            '# When the site terms were last updated, in UTC.\n'
            'REGISTRATION_PAGE_LAST_UPDATED_UTC = '
            'datetime.datetime(2015, 10, 14, 2, 40, 0)\n'
            '# Format of string for dashboard statistics logs.\n'
            '# NOTE TO DEVELOPERS: This format should not be changed, '
            'since it is used in\n'