API Tests for Create Service endpoint

This patch updates API Tests for Create Service endpoint, to include
Fastly service validation.

Change-Id: I6c3370b5dd8937f4349e10ca3e81ae3c2f5eada3
This commit is contained in:
Malini Kamalambal 2014-09-24 07:38:08 -04:00
parent 7af26b4c95
commit ddf4174b22
11 changed files with 231 additions and 40 deletions

View File

@ -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()

View File

@ -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()

53
tests/api/providers.py Normal file
View File

@ -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()

View File

@ -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": []
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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')

View File

@ -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")
'''

View File

@ -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)

View File

@ -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

View File

@ -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}