From 119a480fc1dcaf57f10a30b0f07870c6936628bc Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Tue, 4 Mar 2014 16:26:24 +1000 Subject: [PATCH] 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 --- novaclient/tests/fixture_data/__init__.py | 0 novaclient/tests/fixture_data/aggregates.py | 52 ++++++++++ novaclient/tests/fixture_data/base.py | 32 ++++++ novaclient/tests/fixture_data/client.py | 109 ++++++++++++++++++++ novaclient/tests/utils.py | 38 +++++++ novaclient/tests/v1_1/test_aggregates.py | 58 +++++------ novaclient/tests/v3/test_aggregates.py | 14 +-- test-requirements.txt | 1 + 8 files changed, 260 insertions(+), 44 deletions(-) create mode 100644 novaclient/tests/fixture_data/__init__.py create mode 100644 novaclient/tests/fixture_data/aggregates.py create mode 100644 novaclient/tests/fixture_data/base.py create mode 100644 novaclient/tests/fixture_data/client.py diff --git a/novaclient/tests/fixture_data/__init__.py b/novaclient/tests/fixture_data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/novaclient/tests/fixture_data/aggregates.py b/novaclient/tests/fixture_data/aggregates.py new file mode 100644 index 000000000..33e30905f --- /dev/null +++ b/novaclient/tests/fixture_data/aggregates.py @@ -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) diff --git a/novaclient/tests/fixture_data/base.py b/novaclient/tests/fixture_data/base.py new file mode 100644 index 000000000..3c96133b6 --- /dev/null +++ b/novaclient/tests/fixture_data/base.py @@ -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) diff --git a/novaclient/tests/fixture_data/client.py b/novaclient/tests/fixture_data/client.py new file mode 100644 index 000000000..a221b9bd1 --- /dev/null +++ b/novaclient/tests/fixture_data/client.py @@ -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) diff --git a/novaclient/tests/utils.py b/novaclient/tests/utils.py index cb8ded810..e544d7510 100644 --- a/novaclient/tests/utils.py +++ b/novaclient/tests/utils.py @@ -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 diff --git a/novaclient/tests/v1_1/test_aggregates.py b/novaclient/tests/v1_1/test_aggregates.py index ef0305166..8d41de30d 100644 --- a/novaclient/tests/v1_1/test_aggregates.py +++ b/novaclient/tests/v1_1/test_aggregates.py @@ -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') diff --git a/novaclient/tests/v3/test_aggregates.py b/novaclient/tests/v3/test_aggregates.py index f4ce8218a..7f64fb68f 100644 --- a/novaclient/tests/v3/test_aggregates.py +++ b/novaclient/tests/v3/test_aggregates.py @@ -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 diff --git a/test-requirements.txt b/test-requirements.txt index 388f0fd8b..717eaa2ba 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -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