Convert aggregates testing to use httpretty

This is the first in the series and so contains some setup classes. In
general the original data will not be able to be removed from fakes
until the very end because testing shell depends on a lot of it.

Change-Id: I499fe968923b9452f60c1b2ca50d5749e89827de
blueprint: httpretty-testing
This commit is contained in:
Jamie Lennox 2014-03-04 16:26:24 +10:00
parent c9f4e085e2
commit 119a480fc1
8 changed files with 260 additions and 44 deletions

View File

@ -0,0 +1,52 @@
# 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 httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
class Fixture(base.Fixture):
base_url = 'os-aggregates'
def setUp(self):
super(Fixture, self).setUp()
get_os_aggregates = {"aggregates": [
{'id': '1',
'name': 'test',
'availability_zone': 'nova1'},
{'id': '2',
'name': 'test2',
'availability_zone': 'nova1'},
]}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_aggregates),
content_type='application/json')
r = jsonutils.dumps({'aggregate': get_os_aggregates['aggregates'][0]})
httpretty.register_uri(httpretty.POST, self.url(), body=r,
content_type='application/json')
for agg_id in (1, 2):
for method in (httpretty.GET, httpretty.PUT):
httpretty.register_uri(method, self.url(agg_id), body=r,
content_type='application/json')
httpretty.register_uri(httpretty.POST, self.url(agg_id, 'action'),
body=r, content_type='application/json')
httpretty.register_uri(httpretty.DELETE, self.url(1), status=202)

View File

@ -0,0 +1,32 @@
# 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 fixtures
COMPUTE_URL = 'http://compute.host'
class Fixture(fixtures.Fixture):
base_url = None
def __init__(self, compute_url=COMPUTE_URL):
super(Fixture, self).__init__()
self.compute_url = compute_url
def url(self, *args):
url_args = [self.compute_url]
if self.base_url:
url_args.append(self.base_url)
return '/'.join(str(a).strip('/') for a in tuple(url_args) + args)

View File

@ -0,0 +1,109 @@
# 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 fixtures
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.v1_1 import client as v1_1client
from novaclient.v3 import client as v3client
IDENTITY_URL = 'http://identityserver:5000/v2.0'
COMPUTE_URL = 'http://compute.host'
class V1(fixtures.Fixture):
def __init__(self, compute_url=COMPUTE_URL, identity_url=IDENTITY_URL):
super(V1, self).__init__()
self.identity_url = identity_url
self.compute_url = compute_url
self.client = None
self.token = {
'access': {
"token": {
"id": "ab48a9efdfedb23ty3494",
"expires": "2010-11-01T03:32:15-05:00",
"tenant": {
"id": "345",
"name": "My Project"
}
},
"user": {
"id": "123",
"name": "jqsmith",
"roles": [
{
"id": "234",
"name": "compute:admin",
},
{
"id": "235",
"name": "object-store:admin",
"tenantId": "1",
}
],
"roles_links": [],
},
"serviceCatalog": [
{
"name": "Cloud Servers",
"type": "compute",
"endpoints": [
{
"publicURL": self.compute_url,
"internalURL": "https://compute1.host/v1/1",
},
],
"endpoints_links": [],
},
{
"name": "Cloud Servers",
"type": "computev3",
"endpoints": [
{
"publicURL": self.compute_url,
"internalURL": "https://compute1.host/v1/1",
},
],
"endpoints_links": [],
},
],
}
}
def setUp(self):
super(V1, self).setUp()
httpretty.enable()
self.addCleanup(httpretty.disable)
auth_url = '%s/tokens' % self.identity_url
httpretty.register_uri(httpretty.POST, auth_url,
body=jsonutils.dumps(self.token),
content_type='application/json')
self.client = self.new_client()
def new_client(self):
return v1_1client.Client(username='xx',
api_key='xx',
project_id='xx',
auth_url=self.identity_url)
class V3(V1):
def new_client(self):
return v3client.Client(username='xx',
password='xx',
project_id='xx',
auth_url=self.identity_url)

View File

@ -14,9 +14,13 @@
import os
import fixtures
import httpretty
import requests
import six
import testtools
from novaclient.openstack.common import jsonutils
AUTH_URL = "http://localhost:5002/auth_url"
AUTH_URL_V1 = "http://localhost:5002/auth_url/v1.0"
AUTH_URL_V2 = "http://localhost:5002/auth_url/v2.0"
@ -39,6 +43,40 @@ class TestCase(testtools.TestCase):
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
class FixturedTestCase(TestCase):
client_fixture_class = None
data_fixture_class = None
def setUp(self):
super(FixturedTestCase, self).setUp()
httpretty.reset()
self.data_fixture = None
self.client_fixture = None
self.cs = None
if self.client_fixture_class:
self.client_fixture = self.useFixture(self.client_fixture_class())
self.cs = self.client_fixture.client
if self.data_fixture_class:
self.data_fixture = self.useFixture(self.data_fixture_class())
def assert_called(self, method, path, body=None):
self.assertEqual(httpretty.last_request().method, method)
self.assertEqual(httpretty.last_request().path, path)
if body:
req_data = httpretty.last_request().body
if isinstance(req_data, six.binary_type):
req_data = req_data.decode('utf-8')
if not isinstance(body, six.string_types):
# json load if the input body to match against is not a string
req_data = jsonutils.loads(req_data)
self.assertEqual(req_data, body)
class TestResponse(requests.Response):
"""
Class used to wrap requests.Response and provide some

View File

@ -13,51 +13,45 @@
# License for the specific language governing permissions and limitations
# under the License.
from novaclient.tests.fixture_data import aggregates as data
from novaclient.tests.fixture_data import client
from novaclient.tests import utils
from novaclient.tests.v1_1 import fakes
from novaclient.v1_1 import aggregates
class AggregatesTest(utils.TestCase):
def setUp(self):
super(AggregatesTest, self).setUp()
self.cs = self._get_fake_client()
self.aggregate_type = self._get_aggregate_type()
class AggregatesTest(utils.FixturedTestCase):
def _get_fake_client(self):
return fakes.FakeClient()
def _get_aggregate_type(self):
return aggregates.Aggregate
client_fixture_class = client.V1
data_fixture_class = data.Fixture
def test_list_aggregates(self):
result = self.cs.aggregates.list()
self.cs.assert_called('GET', '/os-aggregates')
self.assert_called('GET', '/os-aggregates')
for aggregate in result:
self.assertIsInstance(aggregate, aggregates.Aggregate)
def test_create_aggregate(self):
body = {"aggregate": {"name": "test", "availability_zone": "nova1"}}
aggregate = self.cs.aggregates.create("test", "nova1")
self.cs.assert_called('POST', '/os-aggregates', body)
self.assert_called('POST', '/os-aggregates', body)
self.assertIsInstance(aggregate, aggregates.Aggregate)
def test_get(self):
aggregate = self.cs.aggregates.get("1")
self.cs.assert_called('GET', '/os-aggregates/1')
self.assert_called('GET', '/os-aggregates/1')
self.assertIsInstance(aggregate, aggregates.Aggregate)
aggregate2 = self.cs.aggregates.get(aggregate)
self.cs.assert_called('GET', '/os-aggregates/1')
self.assert_called('GET', '/os-aggregates/1')
self.assertIsInstance(aggregate2, aggregates.Aggregate)
def test_get_details(self):
aggregate = self.cs.aggregates.get_details("1")
self.cs.assert_called('GET', '/os-aggregates/1')
self.assert_called('GET', '/os-aggregates/1')
self.assertIsInstance(aggregate, aggregates.Aggregate)
aggregate2 = self.cs.aggregates.get_details(aggregate)
self.cs.assert_called('GET', '/os-aggregates/1')
self.assert_called('GET', '/os-aggregates/1')
self.assertIsInstance(aggregate2, aggregates.Aggregate)
def test_update(self):
@ -66,11 +60,11 @@ class AggregatesTest(utils.TestCase):
body = {"aggregate": values}
result1 = aggregate.update(values)
self.cs.assert_called('PUT', '/os-aggregates/1', body)
self.assert_called('PUT', '/os-aggregates/1', body)
self.assertIsInstance(result1, aggregates.Aggregate)
result2 = self.cs.aggregates.update(2, values)
self.cs.assert_called('PUT', '/os-aggregates/2', body)
self.assert_called('PUT', '/os-aggregates/2', body)
self.assertIsInstance(result2, aggregates.Aggregate)
def test_update_with_availability_zone(self):
@ -79,7 +73,7 @@ class AggregatesTest(utils.TestCase):
body = {"aggregate": values}
result3 = self.cs.aggregates.update(aggregate, values)
self.cs.assert_called('PUT', '/os-aggregates/1', body)
self.assert_called('PUT', '/os-aggregates/1', body)
self.assertIsInstance(result3, aggregates.Aggregate)
def test_add_host(self):
@ -88,15 +82,15 @@ class AggregatesTest(utils.TestCase):
body = {"add_host": {"host": "host1"}}
result1 = aggregate.add_host(host)
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
self.assert_called('POST', '/os-aggregates/1/action', body)
self.assertIsInstance(result1, aggregates.Aggregate)
result2 = self.cs.aggregates.add_host("2", host)
self.cs.assert_called('POST', '/os-aggregates/2/action', body)
self.assert_called('POST', '/os-aggregates/2/action', body)
self.assertIsInstance(result2, aggregates.Aggregate)
result3 = self.cs.aggregates.add_host(aggregate, host)
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
self.assert_called('POST', '/os-aggregates/1/action', body)
self.assertIsInstance(result3, aggregates.Aggregate)
def test_remove_host(self):
@ -105,15 +99,15 @@ class AggregatesTest(utils.TestCase):
body = {"remove_host": {"host": "host1"}}
result1 = aggregate.remove_host(host)
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
self.assert_called('POST', '/os-aggregates/1/action', body)
self.assertIsInstance(result1, aggregates.Aggregate)
result2 = self.cs.aggregates.remove_host("2", host)
self.cs.assert_called('POST', '/os-aggregates/2/action', body)
self.assert_called('POST', '/os-aggregates/2/action', body)
self.assertIsInstance(result2, aggregates.Aggregate)
result3 = self.cs.aggregates.remove_host(aggregate, host)
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
self.assert_called('POST', '/os-aggregates/1/action', body)
self.assertIsInstance(result3, aggregates.Aggregate)
def test_set_metadata(self):
@ -122,24 +116,24 @@ class AggregatesTest(utils.TestCase):
body = {"set_metadata": {"metadata": metadata}}
result1 = aggregate.set_metadata(metadata)
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
self.assert_called('POST', '/os-aggregates/1/action', body)
self.assertIsInstance(result1, aggregates.Aggregate)
result2 = self.cs.aggregates.set_metadata(2, metadata)
self.cs.assert_called('POST', '/os-aggregates/2/action', body)
self.assert_called('POST', '/os-aggregates/2/action', body)
self.assertIsInstance(result2, aggregates.Aggregate)
result3 = self.cs.aggregates.set_metadata(aggregate, metadata)
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
self.assert_called('POST', '/os-aggregates/1/action', body)
self.assertIsInstance(result3, aggregates.Aggregate)
def test_delete_aggregate(self):
aggregate = self.cs.aggregates.list()[0]
aggregate.delete()
self.cs.assert_called('DELETE', '/os-aggregates/1')
self.assert_called('DELETE', '/os-aggregates/1')
self.cs.aggregates.delete('1')
self.cs.assert_called('DELETE', '/os-aggregates/1')
self.assert_called('DELETE', '/os-aggregates/1')
self.cs.aggregates.delete(aggregate)
self.cs.assert_called('DELETE', '/os-aggregates/1')
self.assert_called('DELETE', '/os-aggregates/1')

View File

@ -12,19 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from novaclient.tests.fixture_data import client
from novaclient.tests.v1_1 import test_aggregates
from novaclient.tests.v3 import fakes
from novaclient.v3 import aggregates
class AggregatesTest(test_aggregates.AggregatesTest):
def setUp(self):
super(AggregatesTest, self).setUp()
self.cs = self._get_fake_client()
self.aggregate_type = self._get_aggregate_type()
def _get_fake_client(self):
return fakes.FakeClient()
def _get_aggregate_type(self):
return aggregates.Aggregate
client_fixture = client.V3

View File

@ -3,6 +3,7 @@ hacking>=0.8.0,<0.9
coverage>=3.6
discover
fixtures>=0.3.14
httpretty>=0.8.0
keyring>=2.1
mock>=1.0
sphinx>=1.1.2,<1.2