# Copyright 2016 Google LLC
#
# 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.


import unittest

import mock


def _make_credentials():
    import google.auth.credentials

    return mock.Mock(spec=google.auth.credentials.Credentials)


class TestClient(unittest.TestCase):

    PROJECT = 'PROJECT'
    SERVICE = 'SERVICE'
    VERSION = 'myversion'

    @staticmethod
    def _get_target_class():
        from google.cloud.error_reporting.client import Client

        return Client

    def _make_one(self, *args, **kw):
        return self._get_target_class()(*args, **kw)

    def _make_http(self, *args, **kw):
        from google.cloud.error_reporting.client import HTTPContext

        return HTTPContext(*args, **kw)

    def _get_report_payload(self, error_api):
        self.assertEqual(error_api.report_error_event.call_count, 1)
        call = error_api.report_error_event.mock_calls[0]
        _, positional, kwargs = call
        self.assertEqual(kwargs, {})
        self.assertEqual(len(positional), 1)
        return positional[0]

    @mock.patch('google.cloud.client._determine_default_project')
    def test_ctor_default(self, default_mock):
        credentials = _make_credentials()
        default_mock.return_value = 'foo'
        client = self._make_one(credentials=credentials)
        self.assertEqual(client.service, client.DEFAULT_SERVICE)
        self.assertEqual(client.version, None)
        default_mock.assert_called_once_with(None)

    def test_ctor_params(self):
        credentials = _make_credentials()
        client = self._make_one(project=self.PROJECT,
                                credentials=credentials,
                                service=self.SERVICE,
                                version=self.VERSION)
        self.assertEqual(client.service, self.SERVICE)
        self.assertEqual(client.version, self.VERSION)

    def test_report_exception_with_gax(self):
        credentials = _make_credentials()
        client = self._make_one(project=self.PROJECT,
                                credentials=credentials)

        patch = mock.patch(
            'google.cloud.error_reporting.client.make_report_error_api')
        with patch as make_api:
            try:
                raise NameError
            except NameError:
                client.report_exception()
            payload = make_api.return_value.report_error_event.call_args[0][0]
            make_api.assert_called_once_with(client)

        self.assertEqual(payload['serviceContext'], {
            'service': client.DEFAULT_SERVICE,
        })
        self.assertIn('test_report', payload['message'])
        self.assertIn('test_client.py', payload['message'])

    def test_report_exception_wo_gax(self):
        credentials = _make_credentials()
        client = self._make_one(project=self.PROJECT,
                                credentials=credentials,
                                _use_grpc=False)
        patch = mock.patch(
            'google.cloud.error_reporting.client._ErrorReportingLoggingAPI'
        )
        with patch as _error_api:
            try:
                raise NameError
            except NameError:
                client.report_exception()
            mock_report = _error_api.return_value.report_error_event
            payload = mock_report.call_args[0][0]

        self.assertEqual(payload['serviceContext'], {
            'service': client.DEFAULT_SERVICE,
        })
        self.assertIn('test_report', payload['message'])
        self.assertIn('test_client.py', payload['message'])
        self.assertIsNotNone(client.report_errors_api)

    @mock.patch('google.cloud.error_reporting.client.make_report_error_api')
    def test_report_exception_with_service_version_in_constructor(
            self, make_api):
        credentials = _make_credentials()
        service = 'notdefault'
        version = 'notdefaultversion'
        client = self._make_one(project=self.PROJECT,
                                credentials=credentials,
                                service=service,
                                version=version)

        http_context = self._make_http(
            method='GET', response_status_code=500)
        user = 'user@gmail.com'

        error_api = mock.Mock(spec=['report_error_event'])
        make_api.return_value = error_api

        try:
            raise NameError
        except NameError:
            client.report_exception(http_context=http_context, user=user)

        make_api.assert_called_once_with(client)

        payload = self._get_report_payload(error_api)
        self.assertEqual(payload['serviceContext'], {
            'service': service,
            'version': version
        })
        self.assertIn(
            'test_report_exception_with_service_version_in_constructor',
            payload['message'])
        self.assertIn('test_client.py', payload['message'])
        self.assertEqual(
            payload['context']['httpRequest']['responseStatusCode'], 500)
        self.assertEqual(
            payload['context']['httpRequest']['method'], 'GET')
        self.assertEqual(payload['context']['user'], user)

    @mock.patch('google.cloud.error_reporting.client.make_report_error_api')
    def test_report(self, make_api):
        credentials = _make_credentials()
        client = self._make_one(project=self.PROJECT,
                                credentials=credentials)

        error_api = mock.Mock(spec=['report_error_event'])
        make_api.return_value = error_api

        message = 'this is an error'
        client.report(message)

        payload = self._get_report_payload(error_api)

        self.assertEqual(payload['message'], message)
        report_location = payload['context']['reportLocation']
        self.assertIn('test_client.py', report_location['filePath'])
        self.assertEqual(report_location['functionName'], 'test_report')
        self.assertGreater(report_location['lineNumber'], 100)
        self.assertLess(report_location['lineNumber'], 250)
