# 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 json
from unittest import mock

import keystoneauth1.exceptions.http as ks_exceptions
import osc_lib.exceptions as exceptions
import oslotest.base as base
import requests

from osc_placement import http
from osc_placement import version

from oslo_serialization import jsonutils


class FakeResponse(requests.Response):
    def __init__(self, status_code, content=None, headers=None):
        super(FakeResponse, self).__init__()
        self.status_code = status_code
        if content:
            self._content = content
        if headers:
            self.headers = headers


class TestSessionClient(base.BaseTestCase):
    def test_wrap_http_exceptions(self):
        def go():
            with http._wrap_http_exceptions():
                error = {
                    "errors": [
                        {"status": 404,
                         "detail": ("The resource could not be found.\n\n"
                                    "No resource provider with uuid 123 "
                                    "found for delete")}
                    ]
                }
                response = mock.Mock(content=json.dumps(error))
                raise ks_exceptions.NotFound(response=response)

        exc = self.assertRaises(exceptions.NotFound, go)
        self.assertEqual(404, exc.http_status)
        self.assertIn('No resource provider with uuid 123 found',
                      str(exc))

    def test_unexpected_response(self):
        def go():
            with http._wrap_http_exceptions():
                raise ks_exceptions.InternalServerError()

        exc = self.assertRaises(ks_exceptions.InternalServerError, go)
        self.assertEqual(500, exc.http_status)
        self.assertIn('Internal Server Error (HTTP 500)', str(exc))

    def test_session_client_version(self):
        session = mock.Mock()
        ks_filter = {'service_type': 'placement',
                     'region_name': 'mock_region',
                     'interface': 'mock_interface'}

        # 1. target to a specific version
        target_version = '1.23'
        client = http.SessionClient(
            session, ks_filter, api_version=target_version)
        self.assertEqual(client.api_version, target_version)

        # validate that the server side is not called
        session.request.assert_not_called()

        # 2. negotiation succeeds and have the client's highest version
        target_version = '1'
        session.request.return_value = FakeResponse(200)
        client = http.SessionClient(
            session, ks_filter, api_version=target_version)
        self.assertEqual(client.api_version, version.MAX_VERSION_NO_GAP)

        # validate that the server side is called
        expected_version = 'placement ' + version.MAX_VERSION_NO_GAP
        expected_headers = {'OpenStack-API-Version': expected_version,
                            'Accept': 'application/json'}
        session.request.assert_called_once_with(
            '/', 'GET', endpoint_filter=ks_filter,
            headers=expected_headers, raise_exc=False)
        session.reset_mock()

        # 3. negotiation fails and get the servers's highest version
        mock_server_version = '1.10'
        json_mock = {
            "errors": [{"status": 406,
                        "title": "Not Acceptable",
                        "min_version": "1.0",
                        "max_version": mock_server_version}]
        }
        session.request.return_value = FakeResponse(
            406, content=jsonutils.dump_as_bytes(json_mock))

        client = http.SessionClient(
            session, ks_filter, api_version=target_version)
        self.assertEqual(client.api_version, mock_server_version)

        # validate that the server side is called
        session.request.assert_called_once_with(
            '/', 'GET', endpoint_filter=ks_filter,
            headers=expected_headers, raise_exc=False)
