Add a fixture for Keystone version discovery
As we push out the session and expect people to test with proper URL responses we should provide a means to create a correct Keystone version list to test with. Change-Id: I8309ab6cbc47ad159f3c6e018b60ff8c15c6d917
This commit is contained in:
@@ -11,8 +11,8 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
The generators in this directory produce keystone compliant tokens for use in
|
||||
testing.
|
||||
The generators in this directory produce keystone compliant structures for use
|
||||
in testing.
|
||||
|
||||
They should be considered part of the public API because they may be relied
|
||||
upon to generate test tokens for other clients. However they should never be
|
||||
@@ -21,8 +21,15 @@ may be dependencies from this module on libraries that are only available in
|
||||
testing.
|
||||
"""
|
||||
|
||||
from keystoneclient.fixture.discovery import * # noqa
|
||||
from keystoneclient.fixture.exception import FixtureValidationError # noqa
|
||||
from keystoneclient.fixture.v2 import Token as V2Token # noqa
|
||||
from keystoneclient.fixture.v3 import Token as V3Token # noqa
|
||||
|
||||
__all__ = ['V2Token', 'V3Token', 'FixtureValidationError']
|
||||
__all__ = ['DiscoveryList',
|
||||
'FixtureValidationError',
|
||||
'V2Discovery',
|
||||
'V3Discovery',
|
||||
'V2Token',
|
||||
'V3Token',
|
||||
]
|
||||
|
267
keystoneclient/fixture/discovery.py
Normal file
267
keystoneclient/fixture/discovery.py
Normal file
@@ -0,0 +1,267 @@
|
||||
# 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 datetime
|
||||
|
||||
from keystoneclient.openstack.common import timeutils
|
||||
from keystoneclient import utils
|
||||
|
||||
__all__ = ['DiscoveryList',
|
||||
'V2Discovery',
|
||||
'V3Discovery',
|
||||
]
|
||||
|
||||
_DEFAULT_DAYS_AGO = 30
|
||||
|
||||
|
||||
class DiscoveryBase(dict):
|
||||
"""The basic version discovery structure.
|
||||
|
||||
All version discovery elements should have access to these values.
|
||||
"""
|
||||
|
||||
@utils.positional()
|
||||
def __init__(self, id, status=None, updated=None):
|
||||
"""Create a new structure.
|
||||
|
||||
:param string id: The version id for this version entry.
|
||||
:param string status: The status of this entry.
|
||||
:param DateTime updated: When the API was last updated.
|
||||
"""
|
||||
super(DiscoveryBase, self).__init__()
|
||||
|
||||
self.id = id
|
||||
self.status = status or 'stable'
|
||||
self.updated = updated or (timeutils.utcnow() -
|
||||
datetime.timedelta(days=_DEFAULT_DAYS_AGO))
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.get('id')
|
||||
|
||||
@id.setter
|
||||
def id(self, value):
|
||||
self['id'] = value
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self.get('status')
|
||||
|
||||
@status.setter
|
||||
def status(self, value):
|
||||
self['status'] = value
|
||||
|
||||
@property
|
||||
def links(self):
|
||||
return self.setdefault('links', [])
|
||||
|
||||
@property
|
||||
def updated_str(self):
|
||||
return self.get('updated')
|
||||
|
||||
@updated_str.setter
|
||||
def updated_str(self, value):
|
||||
self['updated'] = value
|
||||
|
||||
@property
|
||||
def updated(self):
|
||||
return timeutils.parse_isotime(self.updated_str)
|
||||
|
||||
@updated.setter
|
||||
def updated(self, value):
|
||||
self.updated_str = timeutils.isotime(value)
|
||||
|
||||
@utils.positional()
|
||||
def add_link(self, href, rel='self', type=None):
|
||||
link = {'href': href, 'rel': rel}
|
||||
if type:
|
||||
link['type'] = type
|
||||
self.links.append(link)
|
||||
return link
|
||||
|
||||
@property
|
||||
def media_types(self):
|
||||
return self.setdefault('media-types', [])
|
||||
|
||||
@utils.positional(1)
|
||||
def add_media_type(self, base, type):
|
||||
mt = {'base': base, 'type': type}
|
||||
self.media_types.append(mt)
|
||||
return mt
|
||||
|
||||
|
||||
class V2Discovery(DiscoveryBase):
|
||||
"""A Version element for a V2 identity service endpoint.
|
||||
|
||||
Provides some default values and helper methods for creating a v2.0
|
||||
endpoint version structure. Clients should use this instead of creating
|
||||
their own structures.
|
||||
"""
|
||||
|
||||
_DESC_URL = 'http://docs.openstack.org/api/openstack-identity-service/2.0/'
|
||||
|
||||
@utils.positional()
|
||||
def __init__(self, href, id=None, html=True, pdf=True, **kwargs):
|
||||
"""Create a new structure.
|
||||
|
||||
:param string href: The url that this entry should point to.
|
||||
:param string id: The version id that should be reported. (optional)
|
||||
Defaults to 'v2.0'.
|
||||
:param bool html: Add HTML describedby links to the structure.
|
||||
:param bool pdf: Add PDF describedby links to the structure.
|
||||
"""
|
||||
super(V2Discovery, self).__init__(id or 'v2.0', **kwargs)
|
||||
|
||||
self.add_link(href)
|
||||
|
||||
if html:
|
||||
self.add_html_description()
|
||||
if pdf:
|
||||
self.add_pdf_description()
|
||||
|
||||
def add_html_description(self):
|
||||
"""Add the HTML described by links.
|
||||
|
||||
The standard structure includes a link to a HTML document with the
|
||||
API specification. Add it to this entry.
|
||||
"""
|
||||
self.add_link(href=self._DESC_URL + 'content',
|
||||
rel='describedby',
|
||||
type='text/html')
|
||||
|
||||
def add_pdf_description(self):
|
||||
"""Add the PDF described by links.
|
||||
|
||||
The standard structure includes a link to a PDF document with the
|
||||
API specification. Add it to this entry.
|
||||
"""
|
||||
self.add_link(href=self._DESC_URL + 'identity-dev-guide-2.0.pdf',
|
||||
rel='describedby',
|
||||
type='application/pdf')
|
||||
|
||||
|
||||
class V3Discovery(DiscoveryBase):
|
||||
"""A Version element for a V3 identity service endpoint.
|
||||
|
||||
Provides some default values and helper methods for creating a v3
|
||||
endpoint version structure. Clients should use this instead of creating
|
||||
there own structures.
|
||||
"""
|
||||
|
||||
@utils.positional()
|
||||
def __init__(self, href, id=None, json=True, xml=True, **kwargs):
|
||||
"""Create a new structure.
|
||||
|
||||
:param href: The url that this entry should point to.
|
||||
:param string id: The version id that should be reported. (optional)
|
||||
Defaults to 'v3.0'.
|
||||
:param bool json: Add JSON media-type elements to the structure.
|
||||
:param bool xml: Add XML media-type elements to the structure.
|
||||
"""
|
||||
super(V3Discovery, self).__init__(id or 'v3.0', **kwargs)
|
||||
|
||||
self.add_link(href)
|
||||
|
||||
if json:
|
||||
self.add_json_media_type()
|
||||
if xml:
|
||||
self.add_xml_media_type()
|
||||
|
||||
def add_json_media_type(self):
|
||||
"""Add the JSON media-type links.
|
||||
|
||||
The standard structure includes a list of media-types that the endpoint
|
||||
supports. Add JSON to the list.
|
||||
"""
|
||||
self.add_media_type(base='application/json',
|
||||
type='application/vnd.openstack.identity-v3+json')
|
||||
|
||||
def add_xml_media_type(self):
|
||||
"""Add the XML media-type links.
|
||||
|
||||
The standard structure includes a list of media-types that the endpoint
|
||||
supports. Add XML to the list.
|
||||
"""
|
||||
self.add_media_type(base='application/xml',
|
||||
type='application/vnd.openstack.identity-v3+xml')
|
||||
|
||||
|
||||
class DiscoveryList(dict):
|
||||
"""A List of version elements.
|
||||
|
||||
Creates a correctly structured list of identity service endpoints for
|
||||
use in testing with discovery.
|
||||
"""
|
||||
|
||||
TEST_URL = 'http://keystone.host:5000/'
|
||||
|
||||
@utils.positional(2)
|
||||
def __init__(self, href=None, v2=True, v3=True, v2_id=None, v3_id=None,
|
||||
v2_status=None, v2_updated=None, v2_html=True, v2_pdf=True,
|
||||
v3_status=None, v3_updated=None, v3_json=True, v3_xml=True):
|
||||
"""Create a new structure.
|
||||
|
||||
:param string href: The url that this should be based at.
|
||||
:param bool v2: Add a v2 element.
|
||||
:param bool v3: Add a v3 element.
|
||||
:param string v2_status: The status to use for the v2 element.
|
||||
:param DateTime v2_updated: The update time to use for the v2 element.
|
||||
:param bool v2_html: True to add a html link to the v2 element.
|
||||
:param bool v2_pdf: True to add a pdf link to the v2 element.
|
||||
:param string v3_status: The status to use for the v3 element.
|
||||
:param DateTime v3_updated: The update time to use for the v3 element.
|
||||
:param bool v3_json: True to add a html link to the v2 element.
|
||||
:param bool v3_xml: True to add a pdf link to the v2 element.
|
||||
"""
|
||||
|
||||
super(DiscoveryList, self).__init__(versions={'values': []})
|
||||
|
||||
href = href or self.TEST_URL
|
||||
|
||||
if v2:
|
||||
v2_href = href.rstrip('/') + '/v2.0'
|
||||
self.add_v2(v2_href, id=v2_id, status=v2_status,
|
||||
updated=v2_updated, html=v2_html, pdf=v2_pdf)
|
||||
|
||||
if v3:
|
||||
v3_href = href.rstrip('/') + '/v3'
|
||||
self.add_v3(v3_href, id=v3_id, status=v3_status,
|
||||
updated=v3_updated, json=v3_json, xml=v3_xml)
|
||||
|
||||
@property
|
||||
def versions(self):
|
||||
return self['versions']['values']
|
||||
|
||||
def add_version(self, version):
|
||||
"""Add a new version structure to the list.
|
||||
|
||||
:param dict version: A new version structure to add to the list.
|
||||
"""
|
||||
self.versions.append(version)
|
||||
|
||||
def add_v2(self, href, **kwargs):
|
||||
"""Add a v2 version to the list.
|
||||
|
||||
The parameters are the same as V2Discovery.
|
||||
"""
|
||||
obj = V2Discovery(href, **kwargs)
|
||||
self.add_version(obj)
|
||||
return obj
|
||||
|
||||
def add_v3(self, href, **kwargs):
|
||||
"""Add a v3 version to the list.
|
||||
|
||||
The parameters are the same as V3Discovery.
|
||||
"""
|
||||
obj = V3Discovery(href, **kwargs)
|
||||
self.add_version(obj)
|
||||
return obj
|
@@ -64,37 +64,9 @@ FAKE_ADMIN_TOKEN = jsonutils.dumps(
|
||||
'expires': '2022-10-03T16:58:01Z'}}})
|
||||
|
||||
|
||||
VERSION_LIST_v3 = jsonutils.dumps({
|
||||
"versions": {
|
||||
"values": [
|
||||
{
|
||||
"id": "v3.0",
|
||||
"status": "stable",
|
||||
"updated": "2013-03-06T00:00:00Z",
|
||||
"links": [{'href': '%s/v3' % BASE_URI, 'rel': 'self'}]
|
||||
},
|
||||
{
|
||||
"id": "v2.0",
|
||||
"status": "stable",
|
||||
"updated": "2011-11-19T00:00:00Z",
|
||||
"links": [{'href': '%s/v2.0' % BASE_URI, 'rel': 'self'}]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
VERSION_LIST_v2 = jsonutils.dumps({
|
||||
"versions": {
|
||||
"values": [
|
||||
{
|
||||
"id": "v2.0",
|
||||
"status": "stable",
|
||||
"updated": "2011-11-19T00:00:00Z",
|
||||
"links": []
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
VERSION_LIST_v2 = jsonutils.dumps(fixture.DiscoveryList(href=BASE_URI,
|
||||
v3=False))
|
||||
VERSION_LIST_v3 = jsonutils.dumps(fixture.DiscoveryList(href=BASE_URI))
|
||||
|
||||
ERROR_TOKEN = '7ae290c2a06244c4b41692eb4e9225f2'
|
||||
MEMCACHED_SERVERS = ['localhost:11211']
|
||||
|
@@ -18,6 +18,7 @@ from keystoneclient import _discover
|
||||
from keystoneclient import client
|
||||
from keystoneclient import discover
|
||||
from keystoneclient import exceptions
|
||||
from keystoneclient import fixture
|
||||
from keystoneclient.openstack.common import jsonutils
|
||||
from keystoneclient.tests import utils
|
||||
from keystoneclient.v2_0 import client as v2_client
|
||||
@@ -76,20 +77,8 @@ TEST_SERVICE_CATALOG = [{
|
||||
}]
|
||||
|
||||
V2_URL = "%sv2.0" % BASE_URL
|
||||
V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/'
|
||||
'openstack-identity-service/2.0/content/',
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html'}
|
||||
V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident'
|
||||
'ity-service/2.0/identity-dev-guide-2.0.pdf',
|
||||
'rel': 'describedby',
|
||||
'type': 'application/pdf'}
|
||||
|
||||
V2_VERSION = {'id': 'v2.0',
|
||||
'links': [{'href': V2_URL, 'rel': 'self'},
|
||||
V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF],
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
V2_VERSION = fixture.V2Discovery(V2_URL)
|
||||
V2_VERSION.updated_str = UPDATED
|
||||
|
||||
V2_AUTH_RESPONSE = jsonutils.dumps({
|
||||
"access": {
|
||||
@@ -108,16 +97,9 @@ V2_AUTH_RESPONSE = jsonutils.dumps({
|
||||
})
|
||||
|
||||
V3_URL = "%sv3" % BASE_URL
|
||||
V3_MEDIA_TYPES = [{'base': 'application/json',
|
||||
'type': 'application/vnd.openstack.identity-v3+json'},
|
||||
{'base': 'application/xml',
|
||||
'type': 'application/vnd.openstack.identity-v3+xml'}]
|
||||
|
||||
V3_VERSION = {'id': 'v3.0',
|
||||
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
V3_VERSION = fixture.V3Discovery(V3_URL)
|
||||
V3_MEDIA_TYPES = V3_VERSION.media_types
|
||||
V3_VERSION.updated_str = UPDATED
|
||||
|
||||
V3_TOKEN = six.u('3e2813b7ba0b4006840c3825860b86ed'),
|
||||
V3_AUTH_RESPONSE = jsonutils.dumps({
|
||||
@@ -434,12 +416,9 @@ class ClientDiscoveryTests(utils.TestCase):
|
||||
self.assertVersionNotAvailable(auth_url=V3_URL, version=2)
|
||||
|
||||
def test_discover_unstable_versions(self):
|
||||
v3_unstable_version = V3_VERSION.copy()
|
||||
v3_unstable_version['status'] = 'beta'
|
||||
version_list = _create_version_list([v3_unstable_version, V2_VERSION])
|
||||
|
||||
version_list = fixture.DiscoveryList(BASE_URL, v3_status='beta')
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=300,
|
||||
body=version_list)
|
||||
body=jsonutils.dumps(version_list))
|
||||
|
||||
self.assertCreatesV2(auth_url=BASE_URL)
|
||||
self.assertVersionNotAvailable(auth_url=BASE_URL, version=3)
|
||||
@@ -489,23 +468,15 @@ class ClientDiscoveryTests(utils.TestCase):
|
||||
self.assertCreatesV2(auth_url=BASE_URL)
|
||||
|
||||
def test_greater_version_than_required(self):
|
||||
resp = [{'id': 'v3.6',
|
||||
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}]
|
||||
versions = fixture.DiscoveryList(BASE_URL, v3_id='v3.6')
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=200,
|
||||
body=_create_version_list(resp))
|
||||
body=jsonutils.dumps(versions))
|
||||
self.assertCreatesV3(auth_url=BASE_URL, version=(3, 4))
|
||||
|
||||
def test_lesser_version_than_required(self):
|
||||
resp = [{'id': 'v3.4',
|
||||
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}]
|
||||
versions = fixture.DiscoveryList(BASE_URL, v3_id='v3.4')
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=200,
|
||||
body=_create_version_list(resp))
|
||||
body=jsonutils.dumps(versions))
|
||||
self.assertVersionNotAvailable(auth_url=BASE_URL, version=(3, 6))
|
||||
|
||||
def test_bad_response(self):
|
||||
@@ -557,13 +528,24 @@ class ClientDiscoveryTests(utils.TestCase):
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
body = _create_version_list([V4_VERSION, V3_VERSION, V2_VERSION])
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=300, body=body)
|
||||
versions = fixture.DiscoveryList()
|
||||
versions.add_version(V4_VERSION)
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=300,
|
||||
body=jsonutils.dumps(versions))
|
||||
|
||||
disc = discover.Discover(auth_url=BASE_URL)
|
||||
self.assertRaises(exceptions.DiscoveryFailure,
|
||||
disc.create_client, version=4)
|
||||
|
||||
def test_discovery_fail_for_missing_v3(self):
|
||||
versions = fixture.DiscoveryList(v2=True, v3=False)
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=300,
|
||||
body=jsonutils.dumps(versions))
|
||||
|
||||
disc = discover.Discover(auth_url=BASE_URL)
|
||||
self.assertRaises(exceptions.DiscoveryFailure,
|
||||
disc.create_client, version=(3, 0))
|
||||
|
||||
|
||||
@httpretty.activate
|
||||
class DiscoverQueryTests(utils.TestCase):
|
||||
@@ -715,13 +697,10 @@ class DiscoverQueryTests(utils.TestCase):
|
||||
|
||||
def test_allow_unknown(self):
|
||||
status = 'abcdef'
|
||||
version_list = [{'id': 'v3.0',
|
||||
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': status,
|
||||
'updated': UPDATED}]
|
||||
body = jsonutils.dumps({'versions': version_list})
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=200, body=body)
|
||||
version_list = fixture.DiscoveryList(BASE_URL, v2=False,
|
||||
v3_status=status)
|
||||
httpretty.register_uri(httpretty.GET, BASE_URL, status=200,
|
||||
body=jsonutils.dumps(version_list))
|
||||
|
||||
disc = discover.Discover(auth_url=BASE_URL)
|
||||
|
||||
|
Reference in New Issue
Block a user