diff --git a/tests/api/base.py b/tests/api/base.py index f0456411..f7979416 100644 --- a/tests/api/base.py +++ b/tests/api/base.py @@ -19,7 +19,6 @@ from cafe.drivers.unittest import fixtures import jsonschema from oslo.config import cfg - from tests.api.utils import client from tests.api.utils import config from tests.api.utils import server @@ -36,8 +35,6 @@ class TestBase(fixtures.BaseTestFixture): @classmethod def setUpClass(cls): - cls.conf_file = 'poppy_mockdb.conf' - super(TestBase, cls).setUpClass() cls.auth_config = config.AuthConfig() @@ -57,11 +54,11 @@ class TestBase(fixtures.BaseTestFixture): serialize_format='json', deserialize_format='json') - cls.server_config = config.PoppyServerConfig() - - if cls.server_config.run_server: + cls.test_config = config.TestConfig() + if cls.test_config.run_server: + conf_file = 'poppy_mockdb.conf' conf_path = os.environ["POPPY_TESTS_CONFIGS_DIR"] - config_file = os.path.join(conf_path, cls.conf_file) + config_file = os.path.join(conf_path, conf_file) conf = cfg.ConfigOpts() conf(project='poppy', prog='poppy', args=[], @@ -79,6 +76,6 @@ class TestBase(fixtures.BaseTestFixture): @classmethod def tearDownClass(cls): """Deletes the added resources.""" - if cls.server_config.run_server: + if cls.test_config.run_server: cls.poppy_server.stop() super(TestBase, cls).tearDownClass() diff --git a/tests/api/health/test_health.py b/tests/api/health/test_health.py index 22fdcfa4..c5387d66 100644 --- a/tests/api/health/test_health.py +++ b/tests/api/health/test_health.py @@ -27,7 +27,6 @@ class TestHealth(base.TestBase): super(TestHealth, self).setUp() def test_health(self): - self.skipTest('Endpoint Not Implemented') resp = self.client.check_health() diff --git a/tests/api/providers.py b/tests/api/providers.py new file mode 100644 index 00000000..6a6fe97f --- /dev/null +++ b/tests/api/providers.py @@ -0,0 +1,53 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +from tests.api import base +from tests.api.utils import fastlyclient as fastly + + +class TestProviderBase(base.TestBase): + """Child class of base.TestBase for validating provider updates. + + Inherit from this and write your test methods (for tests that require + provider side validation. Operators might need to add validation for + providers not supported already.If the child class defines a prepare(self) + method, this method will be called before executing each test method. + """ + + @classmethod + def setUpClass(cls): + super(TestProviderBase, cls).setUpClass() + + def getServiceFromProvider(self, provider, service_name): + if provider == 'fastly': + fastly_config = self.config.FastlyConfig() + fastly_client = fastly.FastlyClient( + api_key=fastly_config.api_key, + email=fastly_config.email, + password=fastly_config.password) + service_details = fastly_client.get_service(service_name) + return service_details + + def getServiceFromFlavor(self, flavor, service_name): + """Verify response schema aligns with the expected schema.""" + provider_list = self.config.flavor[flavor] + service_details = dict( + (provider, self.getServiceFromProvider(provider, service_name)) + for provider in provider_list) + return service_details + + @classmethod + def tearDownClass(cls): + super(TestProviderBase, cls).tearDownClass() diff --git a/tests/api/services/data_create_service.json b/tests/api/services/data_create_service.json index 1c33bcaf..487db3e0 100644 --- a/tests/api/services/data_create_service.json +++ b/tests/api/services/data_create_service.json @@ -3,7 +3,7 @@ "name": "my_service_name", "domain_list": [{"domain": "mywebsite.com"}, {"domain": "blog.mywebsite.com"}], - "origin_list": [{"origins": "mywebsite.com", + "origin_list": [{"origin": "mywebsite1.com", "port": 443, "ssl": false}], "flavorRef": "standard", @@ -17,10 +17,10 @@ "name": "my_service_name_2", "domain_list": [{"domain": "mywebsite.com"}, {"domain": "blog.mywebsite.com"}], - "origin_list": [{"origins": "mywebsite.com", + "origin_list": [{"origin": "mywebsite1.com", "port": 443, "ssl": false}], "flavorRef": "standard", "caching_list": [] } -} \ No newline at end of file +} diff --git a/tests/api/services/test_services.py b/tests/api/services/test_services.py index 9f92832e..e214d478 100644 --- a/tests/api/services/test_services.py +++ b/tests/api/services/test_services.py @@ -17,45 +17,71 @@ import uuid import ddt -from tests.api import base -from tests.api.utils.schema import response +from tests.api import providers +# from tests.api.utils.schema import response - Uncomment after get_service API @ddt.ddt -class TestServices(base.TestBase): +class TestServices(providers.TestProviderBase): """Tests for Services.""" def setUp(self): super(TestServices, self).setUp() - self.service_name = uuid.uuid1() + self.service_name = str(uuid.uuid1()) @ddt.file_data('data_create_service.json') def test_create_service(self, test_data): - self.skipTest('Endpoint Not Implemented') - domain_list = test_data['domain_list'] origin_list = test_data['origin_list'] caching_list = test_data['caching_list'] + flavor = test_data['flavorRef'] resp = self.client.create_service(service_name=self.service_name, domain_list=domain_list, origin_list=origin_list, - caching_list=caching_list) - self.assertEqual(resp.status_code, 201) - - response_body = resp.json() - self.assertSchema(response_body, response.create_service) + caching_list=caching_list, + flavorRef=flavor) + self.assertEqual(resp.status_code, 202) + # TODO(malini): uncomment after get_service endpoint is complete. + ''' # Get on Created Service resp = self.client.get_service(service_name=self.service_name) self.assertEqual(resp.status_code, 200) body = resp.json() + self.assertSchema(body, response.create_service) + self.assertEqual(body['domains'], domain_list) self.assertEqual(body['origins'], origin_list) self.assertEqual(body['caching_list'], caching_list) + ''' + + # Verify the service is updated at all Providers for the flavor + if self.test_config.provider_validation: + service_details = ( + self.getServiceFromFlavor(flavor, self.service_name)) + provider_list = self.config.flavor[flavor] + # Verify that the service stored in each provider (that is part of + # the flavor) is what Poppy sent them. + for provider in provider_list: + self.assertEqual( + sorted(service_details[provider]['domain_list']), + sorted(domain_list), + msg='Domain Lists Not Correct for {0} service name {1}'. + format(provider, self.service_name)) + self.assertEqual( + sorted(service_details[provider]['origin_list']), + sorted(origin_list), + msg='Origin List Not Correct for {0} service name {1}'. + format(provider, self.service_name)) + self.assertEqual( + sorted(service_details[provider]['caching_list']), + sorted(caching_list), + msg='Caching List Not Correct for {0} service name {1}'. + format(provider, self.service_name)) def tearDown(self): self.client.delete_service(service_name=self.service_name) diff --git a/tests/api/utils/client.py b/tests/api/utils/client.py index b570cf91..55a2e073 100644 --- a/tests/api/utils/client.py +++ b/tests/api/utils/client.py @@ -70,7 +70,8 @@ class PoppyClient(client.AutoMarshallingHTTPClient): def create_service(self, service_name=None, domain_list=None, origin_list=None, - caching_list=None, requestslib_kwargs=None): + caching_list=None, requestslib_kwargs=None, + flavorRef=None): """Creates Service :return: Response Object containing response code 200 and body with @@ -82,8 +83,9 @@ class PoppyClient(client.AutoMarshallingHTTPClient): request_object = requests.CreateService(service_name=service_name, domain_list=domain_list, origin_list=origin_list, - caching_list=caching_list) - return self.request('PUT', url, + caching_list=caching_list, + flavorRef=flavorRef) + return self.request('POST', url, request_entity=request_object, requestslib_kwargs=requestslib_kwargs) diff --git a/tests/api/utils/config.py b/tests/api/utils/config.py index a7f343fd..c5938bb4 100644 --- a/tests/api/utils/config.py +++ b/tests/api/utils/config.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json + from cafe.engine.models import data_interfaces @@ -25,16 +27,26 @@ class PoppyConfig(data_interfaces.ConfigSectionInterface): """poppy endpoint.""" return self.get('base_url') + @property + def flavor(self): + """poppy flavor definitions.""" + return json.loads(self.get('flavor')) -class PoppyServerConfig(data_interfaces.ConfigSectionInterface): - """Defines the config values for starting (or not) a Poppy server.""" - SECTION_NAME = 'poppy_server' + +class TestConfig(data_interfaces.ConfigSectionInterface): + """Defines the config values specific to test execution.""" + SECTION_NAME = 'test_configuration' @property def run_server(self): """Boolean value indicating whether to start a Poppy server.""" return self.get_boolean('run_server') + @property + def provider_validation(self): + """Boolean value indicating if tests verify provider side details.""" + return self.get_boolean('provider_validation') + class AuthConfig(data_interfaces.ConfigSectionInterface): """Defines the auth config values.""" @@ -53,14 +65,34 @@ class AuthConfig(data_interfaces.ConfigSectionInterface): @property def user_name(self): """The name of the user, if applicable.""" - return self.get("user_name") + return self.get('user_name') @property def api_key(self): """The user's api key, if applicable.""" - return self.get_raw("api_key") + return self.get_raw('api_key') @property def tenant_id(self): """The user's tenant_id, if applicable.""" - return self.get("tenant_id") + return self.get('tenant_id') + + +class FastlyConfig(data_interfaces.ConfigSectionInterface): + """Defines the fastly config values.""" + SECTION_NAME = 'fastly' + + @property + def api_key(self): + """Fastly API Key.""" + return self.get('api_key') + + @property + def email(self): + """Email id associated with Fastly account.""" + return self.get('email') + + @property + def password(self): + """Fastly password.""" + return self.get('password') diff --git a/tests/api/utils/fastlyclient.py b/tests/api/utils/fastlyclient.py new file mode 100644 index 00000000..250bd79e --- /dev/null +++ b/tests/api/utils/fastlyclient.py @@ -0,0 +1,75 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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. + +from cafe.engine.http import client +import fastly + + +class FastlyClient(client.AutoMarshallingHTTPClient): + + """Client objects for Fastly api calls.""" + + def __init__(self, api_key, email, password, serialize_format="json", + deserialize_format="json"): + super(FastlyClient, self).__init__(serialize_format, + deserialize_format) + self.client = fastly.connect(api_key) + self.client.login(email, password) + + self.serialize = serialize_format + self.deserialize_format = deserialize_format + + def get_service(self, service_name): + # Get the service + service = self.client.get_service_by_name(service_name) + service_version = self.client.list_versions(service.id) + + # The create service api_call updates the domain, origin & cache + # settings in second version of the service. The version # will be the + # value of 'number' in the second element of the list returned by + # self.client.list_versions(service.id) call above + version = service_version[1].number + + # Get the Domain List + domains = self.client.list_domains(service.id, version) + domain_list = [{'domain': domain.name} for domain in domains] + + # Get the Cache List + cache_setting_list = self.client.list_cache_settings( + service.id, version) + + cache_list = [{'name': item['name'], 'ttl': int(item['ttl']), + 'rules': item['cache_condition']} + for item in cache_setting_list] + + # Get the Origin List + backends = self.client.list_backends(service.id, version) + origin = backends[0].address + port = backends[0].port + ssl = backends[0].use_ssl + + origin_list = [{'origin': origin, 'port': port, 'ssl': ssl}] + + return {'domain_list': domain_list, + 'origin_list': origin_list, + 'caching_list': cache_list} + + '''except fastly.FastlyError: + print('1', fastly.FastlyError) + return ("failed to GET service") + except Exception: + print('2', Exception) + return ("failed to GET service") + ''' diff --git a/tests/api/utils/models/requests.py b/tests/api/utils/models/requests.py index dd5cae61..b8775da0 100644 --- a/tests/api/utils/models/requests.py +++ b/tests/api/utils/models/requests.py @@ -21,20 +21,20 @@ from cafe.engine.models import base class CreateService(base.AutoMarshallingModel): """Marshalling for Create Service requests.""" - def __init__(self, name=None, domain_list=None, origin_list=None, - flavorRef=None, caching_list=None): + def __init__(self, service_name=None, domain_list=None, origin_list=None, + caching_list=None, flavorRef=None): super(CreateService, self).__init__() - self.service_name = name + self.service_name = service_name self.domain_list = domain_list or [] self.origin_list = origin_list or [] - self.flavorRef = flavorRef self.caching_list = caching_list or [] + self.flavorRef = flavorRef def _obj_to_json(self): create_service_request = {"name": self.service_name, "domains": self.domain_list, "origins": self.origin_list, - "flavorRef": self.flavorRef, - "caching": self.caching_list} + "caching": self.caching_list, + "flavorRef": self.flavorRef} return json.dumps(create_service_request) diff --git a/tests/etc/api.conf b/tests/etc/api.conf index 982a1e8b..6f08af81 100644 --- a/tests/etc/api.conf +++ b/tests/etc/api.conf @@ -10,6 +10,13 @@ base_url=https://identity.api.rackspacecloud.com/v2.0 [poppy] base_url=http://0.0.0.0:8888 +flavor = {"flavor1": ["provider_1], "flavor2": ["provider_2", "provider_3"]} -[poppy_server] +[test_configuration] run_server=True +provider_validation=False + +[provider_1] +api_key=INSERT_YOUR_API_KEY +email_id=account_email_id +password=password diff --git a/tox.ini b/tox.ini index 0aa71b9c..eb828a9a 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ setenv = VIRTUAL_ENV={envdir} NOSE_OPENSTACK_STDOUT=1 deps = -r{toxinidir}/requirements/requirements.txt -r{toxinidir}/tests/test-requirements.txt -commands = pip install git+https://github.com/malini-kamalambal/opencafe.git#egg=cafe +commands = pip install git+https://github.com/stackforge/opencafe.git#egg=cafe pip install git+https://github.com/tonytan4ever/python-maxcdn.git#egg=maxcdn nosetests {posargs}