"""Tests for oauth."""

import datetime
import json
import os
import platform
import unittest
from unittest.mock import MagicMock
from unittest.mock import patch

import google.oauth2.credentials

from gam.auth import oauth


class CredentialsTest(unittest.TestCase):

    def setUp(self):
        self.fake_token = 'fake_token'
        self.fake_refresh_token = 'fake_refresh_token'
        self.fake_id_token = 'fake_id_token'
        self.fake_token_uri = 'https://fake.token.uri'
        self.fake_client_id = 'fake_client_id'
        self.fake_client_secret = 'fake_client_secret'
        self.fake_scopes = [
            'fake_api.readonly',
            'fake_other_api.write',
        ]
        self.fake_quota_project_id = 'fake_quota_project_id'
        self.fake_token_expiry = datetime.datetime(2020, 1, 1, 10)
        self.fake_filename = 'fake_filename'
        self.fake_token_data = {
            'field': 'value',
            'another-field': 'another-value',
        }
        self.info_with_only_required_fields = {
            'refresh_token': self.fake_refresh_token,
            'client_id': self.fake_client_id,
            'client_secret': self.fake_client_secret,
        }
        super().setUp()

    def tearDown(self):
        # Remove any credential files that may have been created.
        if os.path.exists(self.fake_filename):
            os.remove(self.fake_filename)
        if os.path.exists(f'{self.fake_filename}.lock'):
            os.remove(f'{self.fake_filename}.lock')
        super().tearDown()

    def test_from_authorized_user_info_only_required_info(self):
        creds = oauth.Credentials.from_authorized_user_info(
            self.info_with_only_required_fields)
        self.assertEqual(self.fake_refresh_token, creds.refresh_token)
        self.assertEqual(self.fake_client_id, creds.client_id)
        self.assertEqual(self.fake_client_secret, creds.client_secret)
        self.assertIsNone(creds.id_token)
        self.assertIsNone(creds.expiry)
        self.assertIsNone(creds.filename)

    def test_from_authorized_user_info_all_info_provided(self):
        info = {
            'token':
                self.fake_token,
            'refresh_token':
                self.fake_refresh_token,
            'id_token':
                self.fake_id_token,
            'token_uri':
                self.fake_token_uri,
            'client_id':
                self.fake_client_id,
            'client_secret':
                self.fake_client_secret,
            'token_expiry':
                self.fake_token_expiry.strftime(
                    oauth.Credentials.DATETIME_FORMAT),
            'id_token_data':
                self.fake_token_data,
        }
        creds = oauth.Credentials.from_authorized_user_info(info)
        self.assertEqual(self.fake_refresh_token, creds.refresh_token)
        self.assertEqual(self.fake_client_id, creds.client_id)
        self.assertEqual(self.fake_client_secret, creds.client_secret)
        self.assertEqual(self.fake_id_token, creds.id_token)
        self.assertEqual(self.fake_token_uri, creds.token_uri)
        self.assertEqual(self.fake_token_expiry, creds.expiry)
        self.assertIsNone(creds.filename)

    def test_from_authorized_user_info_missing_required_info(self):
        info_with_missing_fields = {'token': self.fake_token}
        with self.assertRaises(ValueError):
            oauth.Credentials.from_authorized_user_info(
                info_with_missing_fields)

    def test_from_authorized_user_info_no_expiry_in_info(self):
        info_with_no_token_expiry = self.info_with_only_required_fields.copy()
        self.assertIsNone(info_with_no_token_expiry.get('expiry'))
        creds = oauth.Credentials.from_authorized_user_info(
            info_with_no_token_expiry)
        self.assertIsNone(creds.expiry)

    def test_init_saves_filename(self):
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)
        self.assertEqual(os.path.abspath(self.fake_filename), creds.filename)

    @patch.object(oauth.google.oauth2.id_token, 'verify_oauth2_token')
    def test_init_loads_decoded_id_token_data(self, mock_verify_token):
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  id_token=self.fake_id_token,
                                  id_token_data=self.fake_token_data)
        self.assertEqual(self.fake_token_data.get('field'),
                         creds.get_token_value('field'))
        # Verify the fetching method was not called, since the token
        # data was supposed to be loaded from the passed in info.
        self.assertEqual(mock_verify_token.call_count, 0)

    def test_credentials_uses_file_lock_when_filename_provided(self):
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)
        self.assertIsInstance(creds._lock, oauth.FileLock)
        self.assertEqual(creds._lock.lock_file, f'{creds.filename}.lock')

    def test_credentials_uses_thread_lock_when_filename_not_provided(self):
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=None)
        self.assertIsInstance(creds._lock, oauth._FileLikeThreadLock)
        self.assertIsNone(creds.filename)

    def test_from_oauth2credentials(self):
        google_creds = google.oauth2.credentials.Credentials(
            token=self.fake_token,
            refresh_token=self.fake_refresh_token,
            client_id=self.fake_client_id,
            client_secret=self.fake_client_secret,
            id_token=self.fake_id_token)
        creds = oauth.Credentials.from_google_oauth2_credentials(
            google_creds, filename=self.fake_filename)
        self.assertEqual(google_creds.token, creds.token)
        self.assertEqual(google_creds.refresh_token, creds.refresh_token)
        self.assertEqual(google_creds.client_id, creds.client_id)
        self.assertEqual(google_creds.client_secret, creds.client_secret)
        self.assertEqual(google_creds.id_token, creds.id_token)
        self.assertEqual(google_creds.expiry, creds.expiry)
        self.assertEqual(google_creds.quota_project_id, creds.quota_project_id)
        self.assertEqual(os.path.abspath(self.fake_filename), creds.filename)

    def test_from_credentials_file_corrupt_or_missing_file_raises_error(self):
        self.assertFalse(os.path.exists(self.fake_filename))
        with self.assertRaises(oauth.InvalidCredentialsFileError) as e:
            oauth.Credentials.from_credentials_file(self.fake_filename)
        self.assertIn('could not be opened', str(e.exception))

    @patch.object(oauth.fileutils, 'read_file')
    def test_from_credentials_file_no_serialized_data_in_file_raises_error(
        self, mock_read_file):
        mock_read_file.return_value = json.dumps({})
        with self.assertRaises(oauth.EmptyCredentialsFileError):
            oauth.Credentials.from_credentials_file(self.fake_filename)

    @patch.object(oauth.fileutils, 'read_file')
    def test_from_credentials_file_missing_any_token_raises_error(
        self, mock_read_file):
        mock_read_file.return_value = json.dumps({
            # This data is missing a token key/value pair
            'client_id': self.fake_client_id,
            'client_secret': self.fake_client_secret,
        })
        with self.assertRaises(oauth.InvalidCredentialsFileError):
            oauth.Credentials.from_credentials_file(self.fake_filename)

    @patch.object(oauth.fileutils, 'read_file')
    def test_from_credentials_file_missing_required_raises_error(
        self, mock_read_file):
        mock_read_file.return_value = json.dumps({
            # This data is missing a client_secret key/value pair
            'client_id': self.fake_client_id,
            'refresh_token': self.fake_refresh_token,
        })
        with self.assertRaises(oauth.InvalidCredentialsFileError):
            oauth.Credentials.from_credentials_file(self.fake_filename)

    @unittest.skip('disabled for oob fixes')
    @patch.object(oauth._ShortURLFlow, 'from_client_config')
    def test_from_client_secrets_console_flow(self, mock_flow):
        flow_creds = google.oauth2.credentials.Credentials(
            token=self.fake_token,
            refresh_token=self.fake_refresh_token,
            client_id=self.fake_client_id,
            client_secret=self.fake_client_secret,
            id_token=self.fake_id_token)
        mock_flow.return_value.credentials = flow_creds

        creds = oauth.Credentials.from_client_secrets(self.fake_client_id,
                                                      self.fake_client_secret,
                                                      self.fake_scopes,
                                                      use_console_flow=True)
        self.assertTrue(mock_flow.return_value.run_console.called)
        self.assertFalse(mock_flow.return_value.run_local_server.called)
        self.assertEqual(flow_creds.token, creds.token)
        self.assertEqual(flow_creds.refresh_token, creds.refresh_token)
        self.assertEqual(flow_creds.client_id, creds.client_id)
        self.assertEqual(flow_creds.client_secret, creds.client_secret)
        self.assertEqual(flow_creds.id_token, creds.id_token)

    @unittest.skip('disabled for oob fixes')
    @patch.object(oauth._ShortURLFlow, 'from_client_config')
    def test_from_client_secrets_local_server_flow(self, mock_flow):
        flow_creds = google.oauth2.credentials.Credentials(
            token=self.fake_token,
            refresh_token=self.fake_refresh_token,
            client_id=self.fake_client_id,
            client_secret=self.fake_client_secret,
            id_token=self.fake_id_token)
        mock_flow.return_value.credentials = flow_creds

        creds = oauth.Credentials.from_client_secrets(self.fake_client_id,
                                                      self.fake_client_secret,
                                                      self.fake_scopes,
                                                      use_console_flow=False)
        self.assertFalse(mock_flow.return_value.run_console.called)
        self.assertTrue(mock_flow.return_value.run_local_server.called)
        self.assertEqual(flow_creds.token, creds.token)
        self.assertEqual(flow_creds.refresh_token, creds.refresh_token)
        self.assertEqual(flow_creds.client_id, creds.client_id)
        self.assertEqual(flow_creds.client_secret, creds.client_secret)
        self.assertEqual(flow_creds.id_token, creds.id_token)

    @unittest.skip('disabled for oob fixes')
    @patch.object(oauth._ShortURLFlow, 'from_client_config')
    def test_from_client_secrets_uses_login_hint(self, mock_flow):
        flow_creds = google.oauth2.credentials.Credentials(
            token=self.fake_token,
            refresh_token=self.fake_refresh_token,
            client_id=self.fake_client_id,
            client_secret=self.fake_client_secret,
            id_token=self.fake_id_token)
        mock_flow.return_value.credentials = flow_creds

        oauth.Credentials.from_client_secrets(self.fake_client_id,
                                              self.fake_client_secret,
                                              self.fake_scopes,
                                              login_hint='someone@domain.com')

        run_flow_args = mock_flow.return_value.run_local_server.call_args[1]
        self.assertEqual('someone@domain.com', run_flow_args.get('login_hint'))

    def test_from_client_secrets_uses_shortened_url_flow(self):
        with patch.object(oauth._ShortURLFlow,
                          'from_client_config') as mock_flow:
            flow_creds = google.oauth2.credentials.Credentials(
                token=self.fake_token,
                refresh_token=self.fake_refresh_token,
                client_id=self.fake_client_id,
                client_secret=self.fake_client_secret,
                id_token=self.fake_id_token)
            mock_flow.return_value.credentials = flow_creds
            oauth.Credentials.from_client_secrets(self.fake_client_id,
                                                  self.fake_client_secret,
                                                  self.fake_scopes)
        self.assertTrue(mock_flow.called)

    @patch.object(oauth._ShortURLFlow, 'from_client_config')
    def test_from_client_secrets_passes_credentials_filename(self, mock_flow):
        flow_creds = google.oauth2.credentials.Credentials(
            token=self.fake_token,
            refresh_token=self.fake_refresh_token,
            client_id=self.fake_client_id,
            client_secret=self.fake_client_secret,
            id_token=self.fake_id_token)
        mock_flow.return_value.credentials = flow_creds

        creds = oauth.Credentials.from_client_secrets(
            self.fake_client_id,
            self.fake_client_secret,
            self.fake_scopes,
            filename=self.fake_filename)
        self.assertEqual(os.path.abspath(self.fake_filename), creds.filename)

    def test_from_client_secrets_file_corrupt_or_missing_file_raises_error(
        self):
        self.assertFalse(os.path.exists(self.fake_filename))
        with self.assertRaises(oauth.InvalidClientSecretsFileError):
            oauth.Credentials.from_client_secrets_file(self.fake_filename,
                                                       self.fake_scopes)

    @patch.object(oauth.fileutils, 'read_file')
    def test_from_client_secrets_file_missing_required_json_raises_error(
        self, mock_read_file):
        mock_read_file.return_value = json.dumps({})
        with self.assertRaises(oauth.InvalidClientSecretsFileFormatError) as e:
            oauth.Credentials.from_client_secrets_file(self.fake_filename,
                                                       self.fake_scopes)
        self.assertIn('Could not extract Client ID or Client Secret',
                      str(e.exception))

    @patch.object(oauth.Credentials, 'from_client_secrets')
    @patch.object(oauth.fileutils, 'read_file')
    def test_from_client_secrets_file_strips_domain_from_client_id(
        self, mock_read_file, mock_creds_from_client_secrets):
        mock_read_file.return_value = json.dumps({
            'installed': {
                'client_id':
                    self.fake_client_id + '.apps.googleusercontent.com',
                'client_secret':
                    self.fake_client_secret,
            }
        })

        oauth.Credentials.from_client_secrets_file(self.fake_filename,
                                                   self.fake_scopes)
        self.assertEqual(self.fake_client_id,
                         mock_creds_from_client_secrets.call_args[0][0])

    def test_get_token_value_known_token_field(self):
        token_data = {'known-field': 'known-value'}
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  id_token_data=token_data)
        self.assertEqual('known-value', creds.get_token_value('known-field'))

    def test_get_token_value_unknown_field_returns_unknown(self):
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  id_token_data=self.fake_token_data)
        self.assertEqual('Unknown', creds.get_token_value('unknown-field'))

    @patch.object(oauth.google.oauth2.id_token, 'verify_oauth2_token')
    def test_get_token_value_credentials_expired(self,
                                                 mock_verify_oauth2_token):
        mock_verify_oauth2_token.return_value = {
            'fetched-field': 'fetched-value'
        }
        time_earlier_than_now = datetime.datetime.now() - datetime.timedelta(
            minutes=5)
        creds = oauth.Credentials(token=self.fake_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  expiry=time_earlier_than_now,
                                  id_token=self.fake_id_token,
                                  id_token_data=None)
        self.assertTrue(creds.expired)
        creds.refresh = MagicMock()

        token_value = creds.get_token_value('fetched-field')

        self.assertEqual('fetched-value', token_value)
        self.assertTrue(creds.refresh.called)

    def test_to_json_contains_all_required_fields(self):
        creds = oauth.Credentials(token=self.fake_token,
                                  refresh_token=self.fake_refresh_token,
                                  id_token=self.fake_id_token,
                                  id_token_data=self.fake_token_data,
                                  token_uri=self.fake_token_uri,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  scopes=self.fake_scopes,
                                  quota_project_id=self.fake_quota_project_id,
                                  expiry=self.fake_token_expiry)
        json_string = creds.to_json()
        json_data = json.loads(json_string)
        keys = json_data.keys()
        self.assertIn('token', keys)
        self.assertEqual(self.fake_token, json_data['token'])
        self.assertIn('refresh_token', keys)
        self.assertEqual(self.fake_refresh_token, json_data['refresh_token'])
        self.assertIn('id_token', keys)
        self.assertEqual(self.fake_id_token, json_data['id_token'])
        self.assertIn('token_uri', keys)
        self.assertEqual(self.fake_token_uri, json_data['token_uri'])
        self.assertIn('client_id', keys)
        self.assertEqual(self.fake_client_id, json_data['client_id'])
        self.assertIn('client_secret', keys)
        self.assertEqual(self.fake_client_secret, json_data['client_secret'])
        self.assertNotIn('scopes', keys)  # Scopes are not currently saved
        self.assertIn('token_expiry', keys)
        self.assertEqual(
            self.fake_token_expiry.strftime(oauth.Credentials.DATETIME_FORMAT),
            json_data['token_expiry'])
        self.assertIn('decoded_id_token', keys)
        self.assertEqual(self.fake_token_data, json_data['decoded_id_token'])

    def test_credentials_to_json_and_back(self):
        original_creds = oauth.Credentials(
            token=self.fake_token,
            refresh_token=self.fake_refresh_token,
            id_token=self.fake_id_token,
            id_token_data=self.fake_token_data,
            token_uri=self.fake_token_uri,
            client_id=self.fake_client_id,
            client_secret=self.fake_client_secret,
            scopes=self.fake_scopes,
            quota_project_id=self.fake_quota_project_id,
            expiry=self.fake_token_expiry)
        pickled_creds = original_creds.to_json()
        serialized_json = json.loads(pickled_creds)
        unpickled_creds = oauth.Credentials.from_authorized_user_info(
            serialized_json)
        self.assertEqual(original_creds.token, unpickled_creds.token)
        self.assertEqual(original_creds.refresh_token,
                         unpickled_creds.refresh_token)
        self.assertEqual(original_creds.id_token, unpickled_creds.id_token)
        self.assertEqual(original_creds.token_uri, unpickled_creds.token_uri)
        self.assertEqual(original_creds.client_id, unpickled_creds.client_id)
        self.assertEqual(original_creds.client_secret,
                         unpickled_creds.client_secret)
        self.assertEqual(original_creds.expiry, unpickled_creds.expiry)

    @patch.object(oauth.google.oauth2.credentials.Credentials, 'refresh')
    def test_refresh_calls_super_refresh(self, mock_super_refresh):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret)
        request = MagicMock()

        creds.refresh(request)
        self.assertTrue(mock_super_refresh.called)
        self.assertEqual(request, mock_super_refresh.call_args[0][0])

    def test_refresh_locks_resource_during_refresh(self):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret)
        lock = creds._lock

        def check_lock_is_locked(*unused_args, **unused_kwargs):
            self.assertTrue(lock.is_locked)

        # We need to mock the superclass refresh so it doesn't actually try to
        # refresh our fake token.
        # At the same time, we'll make sure the lock is held during the refresh.
        with patch.object(oauth.google.oauth2.credentials.Credentials,
                          'refresh') as mock_refresh:
            mock_refresh.side_effect = check_lock_is_locked
            creds.refresh(request=MagicMock())

        # Make sure our side effect was actually performed.
        self.assertTrue(mock_refresh.called)
        # The lock should be released after refresh
        self.assertFalse(lock.is_locked)

    @patch.object(oauth.google.oauth2.credentials.Credentials, 'refresh')
    @patch.object(oauth.fileutils, 'write_file')
    def test_refresh_writes_new_credentials_to_disk_after_refresh(
        self, mock_write_file, mock_super_refresh):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)

        def update_access_token(unused_request):
            creds.token = 'refreshed_access_token'

        mock_super_refresh.side_effect = update_access_token

        self.assertIsNone(creds.token)
        creds.refresh(request=MagicMock())
        self.assertEqual('refreshed_access_token', creds.token,
                         'Access token was not refreshed')
        text_written_to_file = mock_write_file.call_args[0][1]
        self.assertIsNotNone(text_written_to_file,
                             'Nothing was written to file')
        saved_json = json.loads(text_written_to_file)
        self.assertEqual('refreshed_access_token', saved_json['token'],
                         'Refreshed access token was not saved to disk')

    def test_write_writes_credentials_to_disk(self):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)

        self.assertFalse(os.path.exists(self.fake_filename))
        creds.write()
        self.assertTrue(os.path.exists(self.fake_filename))

    def test_write_raises_error_when_no_credentials_file_is_set(self):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret)

        self.assertIsNone(creds.filename)
        with self.assertRaises(oauth.CredentialsError):
            creds.write()

    @patch.object(oauth.google.oauth2.credentials.Credentials, 'refresh')
    @patch.object(oauth.fileutils, 'write_file')
    def test_write_locks_resource_during_write(self, mock_write_file,
                                               unused_mock_super_refresh):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)
        lock = creds._lock

        def check_lock_is_locked(*unused_args, **unused_kwargs):
            self.assertTrue(creds._lock.is_locked)

        mock_write_file.side_effect = check_lock_is_locked

        self.assertFalse(lock.is_locked)
        creds.refresh(request=MagicMock())
        self.assertFalse(lock.is_locked)
        self.assertTrue(mock_write_file.called)

    def test_delete_removes_credentials_file(self):
        self.assertFalse(os.path.exists(self.fake_filename))
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)
        creds.write()
        self.assertTrue(os.path.exists(self.fake_filename))
        creds.delete()
        self.assertFalse(os.path.exists(self.fake_filename))

    @unittest.skipIf(
        platform.system() == 'Windows',
        reason=('On Windows, Filelock deletes the lock file each time the lock '
                'is released. Delete does not remove it.'))
    def test_delete_removes_lock_file(self):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret,
                                  filename=self.fake_filename)
        lock_file = f'{creds.filename}.lock'
        creds.write()
        self.assertTrue(os.path.exists(lock_file))
        creds.delete()
        self.assertFalse(os.path.exists(lock_file))

    def test_delete_is_noop_when_not_using_filelock(self):
        creds = oauth.Credentials(token=None,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret)
        self.assertIsNone(creds.filename)
        creds.delete()  # This should not raise an exception.

    def test_revoke_requests_credential_revoke(self):
        creds = oauth.Credentials(token=self.fake_token,
                                  refresh_token=self.fake_refresh_token,
                                  client_id=self.fake_client_id,
                                  client_secret=self.fake_client_secret)
        mock_http = MagicMock()

        creds.revoke(http=mock_http)

        uri = mock_http.request.call_args[0][0]
        self.assertRegex(uri, f'^{oauth.Credentials._REVOKE_TOKEN_BASE_URI}')
        params = uri[uri.index('?'):]
        self.assertIn(f'token={creds.refresh_token}', params)
        self.assertEqual('GET', mock_http.request.call_args[0][1])


class ShortUrlFlowTest(unittest.TestCase):

    def setUp(self):
        self.fake_client_id = 'fake_client_id'
        self.fake_client_secret = 'fake_client_secret'
        self.fake_scopes = [
            'fake_api.readonly',
            'fake_other_api.write',
        ]
        self.fake_client_config = {
            'installed': {
                'client_id': self.fake_client_id,
                'client_secret': self.fake_client_secret,
                'redirect_uris': [
                    'http://localhost', 'urn:ietf:wg:oauth:2.0:oob'
                ],
                'auth_uri': 'https://accounts.google.com/o/oauth2/v2/auth',
                'token_uri': 'https://oauth2.googleapis.com/token',
            }
        }
        self.long_url = 'http://example.com/some/long/url'
        self.short_url = 'http://ex.co/short'
        super().setUp()

    @patch.object(oauth.google_auth_oauthlib.flow.InstalledAppFlow,
                  'authorization_url')
    @unittest.skip('disable short url tests temporarily.')
    def test_shorturlflow_returns_shortened_url(self, mock_super_auth_url):
        url_flow = oauth._ShortURLFlow.from_client_config(
            self.fake_client_config, scopes=self.fake_scopes)
        mock_super_auth_url.return_value = (self.long_url, 'fake_state')

        mock_http = MagicMock()
        mock_response = MagicMock()
        mock_response.status = 200
        content = json.dumps({'short_url': self.short_url})
        mock_http.request.return_value = (mock_response, content)

        url, state = url_flow.authorization_url(http=mock_http)
        self.assertEqual(self.short_url, url)
        self.assertEqual('fake_state', state)

        # Verify request() was called with the expected arguments.
        self.assertEqual(oauth._ShortURLFlow.URL_SHORTENER_ENDPOINT,
                         mock_http.request.call_args[0][0])
        self.assertEqual('POST', mock_http.request.call_args[0][1])
        self.assertIn(self.long_url, mock_http.request.call_args[0][2])

    @patch.object(oauth.google_auth_oauthlib.flow.InstalledAppFlow,
                  'authorization_url')
    @unittest.skip('disable short url tests temporarily.')
    def test_shorturlflow_falls_back_to_long_url_on_request_error(
        self, mock_super_auth_url):
        url_flow = oauth._ShortURLFlow.from_client_config(
            self.fake_client_config, scopes=self.fake_scopes)
        mock_super_auth_url.return_value = (self.long_url, 'fake_state')

        mock_http = MagicMock()
        mock_http.request.side_effect = Exception()

        url, state = url_flow.authorization_url(http=mock_http)
        self.assertEqual(self.long_url, url)
        self.assertEqual('fake_state', state)

    @patch.object(oauth.google_auth_oauthlib.flow.InstalledAppFlow,
                  'authorization_url')
    @unittest.skip('disable short url tests temporarily.')
    def test_shorturlflow_falls_back_to_long_url_on_non_200_response_status(
        self, mock_super_auth_url):
        url_flow = oauth._ShortURLFlow.from_client_config(
            self.fake_client_config, scopes=self.fake_scopes)
        mock_super_auth_url.return_value = (self.long_url, 'fake_state')

        mock_http = MagicMock()
        mock_response = MagicMock()
        mock_response.status = 404  # Use a status that is not 200
        content = json.dumps({'short_url': self.short_url})
        mock_http.request.return_value = (mock_response, content)

        url, state = url_flow.authorization_url(http=mock_http)
        self.assertEqual(self.long_url, url)
        self.assertEqual('fake_state', state)

    @patch.object(oauth.google_auth_oauthlib.flow.InstalledAppFlow,
                  'authorization_url')
    @unittest.skip('disable short url tests temporarily.')
    def test_shorturlflow_falls_back_to_long_url_on_bad_json_response(
        self, mock_super_auth_url):
        url_flow = oauth._ShortURLFlow.from_client_config(
            self.fake_client_config, scopes=self.fake_scopes)
        mock_super_auth_url.return_value = (self.long_url, 'fake_state')

        mock_http = MagicMock()
        mock_response = MagicMock()
        mock_response.status = 200
        content = None
        mock_http.request.return_value = (mock_response, content)

        url, state = url_flow.authorization_url(http=mock_http)
        self.assertEqual(self.long_url, url)
        self.assertEqual('fake_state', state)

    @patch.object(oauth.google_auth_oauthlib.flow.InstalledAppFlow,
                  'authorization_url')
    @unittest.skip('disable short url tests temporarily.')
    def test_shorturlflow_falls_back_to_long_url_on_empty_short_url_field(
        self, mock_super_auth_url):
        url_flow = oauth._ShortURLFlow.from_client_config(
            self.fake_client_config, scopes=self.fake_scopes)
        mock_super_auth_url.return_value = (self.long_url, 'fake_state')

        mock_http = MagicMock()
        mock_response = MagicMock()
        mock_response.status = 200
        content = json.dumps(
            {})  # This json content contains no "short-url" key
        mock_http.request.return_value = (mock_response, content)

        url, state = url_flow.authorization_url(http=mock_http)
        self.assertEqual(self.long_url, url)
        self.assertEqual('fake_state', state)


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