Add API Test Structure
This patch adds the initial directory structure and helpers needed for API tests.
This commit is contained in:
parent
aaa8b274a0
commit
896eab13c8
0
tests/api/__init__.py
Normal file
0
tests/api/__init__.py
Normal file
11
tests/api/etc/tests.conf.sample
Normal file
11
tests/api/etc/tests.conf.sample
Normal file
@ -0,0 +1,11 @@
|
||||
#=============================================================================
|
||||
# Configuration file to execute API tests.
|
||||
#=============================================================================
|
||||
|
||||
[auth]
|
||||
username={user name of the cloud account}
|
||||
api_key={api key for this user name}
|
||||
base_url=https://identity.api.rackspacecloud.com/v2.0/tokens
|
||||
|
||||
[cdn]
|
||||
base_url=https://private-ea1ca-cloudcdn.apiary.io
|
0
tests/api/services/__init__.py
Normal file
0
tests/api/services/__init__.py
Normal file
22
tests/api/services/create_service.json
Normal file
22
tests/api/services/create_service.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"all_fields": {
|
||||
"domain_list": [{"domain": "mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"}],
|
||||
"origin_list": [{"origins": "mywebsite.com",
|
||||
"port": 443,
|
||||
"ssl": false}],
|
||||
"caching_list": [{"name": "default", "ttl": 3600},
|
||||
{"name": "home",
|
||||
"ttl": 1200,
|
||||
"rules": [{"name" : "index",
|
||||
"request_url" : "/index.htm"}]}]
|
||||
},
|
||||
"caching_empty": {
|
||||
"domain_list": [{"domain": "mywebsite.com"},
|
||||
{"domain": "blog.mywebsite.com"}],
|
||||
"origin_list": [{"origins": "mywebsite.com",
|
||||
"port": 443,
|
||||
"ssl": false}],
|
||||
"caching_list": []
|
||||
}
|
||||
}
|
46
tests/api/services/test_services.py
Normal file
46
tests/api/services/test_services.py
Normal file
@ -0,0 +1,46 @@
|
||||
import ddt
|
||||
import uuid
|
||||
|
||||
from tests.api.utils import base
|
||||
from tests.api.utils.schema import response
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestServices(base.TestBase):
|
||||
|
||||
"""Tests for Services."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestServices, self).setUp()
|
||||
self.service_name = uuid.uuid1()
|
||||
|
||||
@ddt.file_data('create_service.json')
|
||||
def test_create_service(self, test_data):
|
||||
|
||||
domain_list = test_data['domain_list']
|
||||
origin_list = test_data['origin_list']
|
||||
caching_list = test_data['caching_list']
|
||||
|
||||
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)
|
||||
|
||||
#Get on Created Service
|
||||
resp = self.client.get_service(service_name=self.service_name)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
body = resp.json()
|
||||
self.assertEqual(body['domains'], domain_list)
|
||||
self.assertEqual(body['origins'], origin_list)
|
||||
self.assertEqual(body['caching_list'], caching_list)
|
||||
|
||||
test_create_service.tags = ['smoke', 'positive']
|
||||
|
||||
def tearDown(self):
|
||||
self.client.delete_service(service_name=self.service_name)
|
||||
super(TestServices, self).tearDown()
|
0
tests/api/utils/__init__.py
Normal file
0
tests/api/utils/__init__.py
Normal file
49
tests/api/utils/base.py
Normal file
49
tests/api/utils/base.py
Normal file
@ -0,0 +1,49 @@
|
||||
import jsonschema
|
||||
|
||||
from cafe.drivers.unittest import fixtures
|
||||
|
||||
from tests.api.utils import client
|
||||
from tests.api.utils import config
|
||||
|
||||
|
||||
class TestBase(fixtures.BaseTestFixture):
|
||||
"""Child class of fixtures.BaseTestFixture for testing CDN.
|
||||
|
||||
Inherit from this and write your test methods. If the child class defines
|
||||
a prepare(self) method, this method will be called before executing each
|
||||
test method.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
super(TestBase, cls).setUpClass()
|
||||
|
||||
cls.auth_config = config.authConfig()
|
||||
cls.auth_client = client.AuthClient()
|
||||
auth_token = cls.auth_client.get_auth_token(cls.auth_config.base_url,
|
||||
cls.auth_config.user_name,
|
||||
cls.auth_config.api_key)
|
||||
|
||||
cls.config = config.cdnConfig()
|
||||
version = 'v1.0'
|
||||
cls.url = cls.config.base_url
|
||||
|
||||
cls.client = client.CDNClient(cls.url, auth_token,
|
||||
serialize_format='json',
|
||||
deserialize_format='json')
|
||||
|
||||
def assertSchema(self, response_json, expected_schema):
|
||||
"""Verify response schema aligns with the expected schema
|
||||
"""
|
||||
try:
|
||||
jsonschema.validate(response_json, expected_schema)
|
||||
except jsonschema.ValidationError as message:
|
||||
assert False, message
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""
|
||||
Deletes the added resources
|
||||
"""
|
||||
super(TestBase, cls).tearDownClass()
|
92
tests/api/utils/client.py
Normal file
92
tests/api/utils/client.py
Normal file
@ -0,0 +1,92 @@
|
||||
import json
|
||||
|
||||
from cafe.engine.http import client
|
||||
from models import requests
|
||||
|
||||
|
||||
class AuthClient(client.HTTPClient):
|
||||
|
||||
"""
|
||||
Client Objects for Auth call
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(AuthClient, self).__init__()
|
||||
|
||||
self.default_headers['Content-Type'] = 'application/json'
|
||||
self.default_headers['Accept'] = 'application/json'
|
||||
|
||||
def get_auth_token(self, url, user_name, api_key):
|
||||
"""
|
||||
Get Auth Token using api_key
|
||||
@todo: Support getting token with password (or) api key.
|
||||
"""
|
||||
request_body = {
|
||||
"auth": {
|
||||
"RAX-KSKEY:apiKeyCredentials": {
|
||||
"username": user_name,
|
||||
"apiKey": api_key
|
||||
},
|
||||
},
|
||||
}
|
||||
request_body = json.dumps(request_body)
|
||||
|
||||
response = self.request('POST', url, data=request_body)
|
||||
token = response.json()['access']['token']['id']
|
||||
return token
|
||||
|
||||
|
||||
class CDNClient(client.AutoMarshallingHTTPClient):
|
||||
|
||||
"""
|
||||
Client objects for all the CDN api calls
|
||||
"""
|
||||
|
||||
def __init__(self, url, auth_token, serialize_format="json",
|
||||
deserialize_format="json"):
|
||||
super(CDNClient, self).__init__(serialize_format,
|
||||
deserialize_format)
|
||||
self.url = url
|
||||
self.auth_token = auth_token
|
||||
self.default_headers['X-Auth-Token'] = auth_token
|
||||
self.default_headers['Content-Type'] = 'application/json'
|
||||
self.default_headers['Accept'] = 'application/json'
|
||||
|
||||
self.serialize = serialize_format
|
||||
self.deserialize_format = deserialize_format
|
||||
|
||||
def create_service(self, service_name=None,
|
||||
domain_list=None, origin_list=None,
|
||||
caching_list=None, requestslib_kwargs=None):
|
||||
"""
|
||||
Creates Service
|
||||
:return: Response Object containing response code 200 and body with
|
||||
details of service
|
||||
|
||||
PUT
|
||||
services/{service_name}
|
||||
"""
|
||||
url = '{0}/services/{1}'.format(self.url, service_name)
|
||||
request_object = requests.CreateService(domain_list=domain_list,
|
||||
origin_list=origin_list,
|
||||
caching_list=caching_list)
|
||||
return self.request('PUT', url,
|
||||
request_entity=request_object,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def get_service(self, service_name):
|
||||
"""Get Service
|
||||
:return: Response Object containing response code 200 and body with
|
||||
details of service
|
||||
"""
|
||||
|
||||
url = '{0}/services/{1}'.format(self.url, service_name)
|
||||
return self.request('GET', url)
|
||||
|
||||
def delete_service(self, service_name):
|
||||
"""Delete Service
|
||||
:return: Response Object containing response code 204
|
||||
"""
|
||||
|
||||
url = '{0}/services/{1}'.format(self.url, service_name)
|
||||
return self.request('DELETE', url)
|
44
tests/api/utils/config.py
Normal file
44
tests/api/utils/config.py
Normal file
@ -0,0 +1,44 @@
|
||||
from cafe.engine.models.data_interfaces import ConfigSectionInterface
|
||||
|
||||
|
||||
class cdnConfig(ConfigSectionInterface):
|
||||
"""
|
||||
Defines the config values for cdn
|
||||
"""
|
||||
SECTION_NAME = 'cdn'
|
||||
|
||||
@property
|
||||
def base_url(self):
|
||||
"""
|
||||
CDN endpoint
|
||||
"""
|
||||
return self.get('base_url')
|
||||
|
||||
|
||||
class authConfig(ConfigSectionInterface):
|
||||
"""
|
||||
Defines the auth config values
|
||||
"""
|
||||
SECTION_NAME = 'auth'
|
||||
|
||||
@property
|
||||
def base_url(self):
|
||||
"""
|
||||
Auth endpoint
|
||||
"""
|
||||
return self.get('base_url')
|
||||
|
||||
@property
|
||||
def user_name(self):
|
||||
"""The name of the user, if applicable"""
|
||||
return self.get("user_name")
|
||||
|
||||
@property
|
||||
def api_key(self):
|
||||
"""The user's api key, if applicable"""
|
||||
return self.get_raw("api_key")
|
||||
|
||||
@property
|
||||
def tenant_id(self):
|
||||
"""The user's tenant_id, if applicable"""
|
||||
return self.get("tenant_id")
|
0
tests/api/utils/models/__init__.py
Normal file
0
tests/api/utils/models/__init__.py
Normal file
21
tests/api/utils/models/requests.py
Normal file
21
tests/api/utils/models/requests.py
Normal file
@ -0,0 +1,21 @@
|
||||
import json
|
||||
from cafe.engine.models import base
|
||||
|
||||
|
||||
class CreateService(base.AutoMarshallingModel):
|
||||
"""
|
||||
Marshalling for Create Service requests
|
||||
"""
|
||||
|
||||
def __init__(self, domain_list=None, origin_list=None, caching_list=None):
|
||||
super(CreateService, self).__init__()
|
||||
|
||||
self.domain_list = domain_list or []
|
||||
self.origin_list = origin_list or []
|
||||
self.caching_list = caching_list or []
|
||||
|
||||
def _obj_to_json(self):
|
||||
create_service_request = {"domains": self.domain_list,
|
||||
"origins": self.origin_list,
|
||||
"caching": self.caching_list}
|
||||
return json.dumps(create_service_request)
|
21
tests/api/utils/models/response.py
Normal file
21
tests/api/utils/models/response.py
Normal file
@ -0,0 +1,21 @@
|
||||
import json
|
||||
from cafe.engine.models import base
|
||||
|
||||
|
||||
class CreateService(base.AutoMarshallingModel):
|
||||
"""
|
||||
Marshalling for Create Service requests
|
||||
"""
|
||||
|
||||
def __init__(self, domain_list=None, origin_list=None, caching_list=None):
|
||||
super(CreateService, self).__init__()
|
||||
|
||||
self.domain_list = domain_list or []
|
||||
self.origin_list = origin_list or []
|
||||
self.caching_list = caching_list or []
|
||||
|
||||
def _obj_to_json(self):
|
||||
create_service_request = {"domains": self.domain_list,
|
||||
"origins": self.origin_list,
|
||||
"caching": self.caching_list}
|
||||
return json.dumps(create_service_request)
|
0
tests/api/utils/schema/__init__.py
Normal file
0
tests/api/utils/schema/__init__.py
Normal file
66
tests/api/utils/schema/response.py
Normal file
66
tests/api/utils/schema/response.py
Normal file
@ -0,0 +1,66 @@
|
||||
domain = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'domain': {'type': 'string',
|
||||
'pattern': '^([a-zA-Z0-9-.]+(.com))$'}},
|
||||
'required': ['domain']
|
||||
}
|
||||
|
||||
origin = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'origin': {'type': 'string',
|
||||
'pattern': '^([a-zA-Z0-9-.]{5,1000})$'},
|
||||
'port': {'type': 'number',
|
||||
'minumum': 0,
|
||||
'maximum': 100000},
|
||||
'ssl': {'type': 'boolean'},
|
||||
'rules': {'type': 'array'}},
|
||||
'required': ['origin', 'port', 'ssl'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
cache = {'type': 'object',
|
||||
'properties': {
|
||||
'name': {'type': 'string', 'pattern': '^[a-zA-Z0-9_-]{1,64}$'},
|
||||
'ttl': {'type': 'number', 'minimum': 1, 'maximum': 36000},
|
||||
'rules': {'type': 'array'}},
|
||||
'required': ['name', 'ttl'],
|
||||
'additionalProperties': False}
|
||||
|
||||
links = {'type': 'object',
|
||||
'properties': {
|
||||
'href': {'type': 'string',
|
||||
'pattern': '^/v1.0/services/[a-zA-Z0-9_-]{1,64}$'},
|
||||
'rel': {'type': 'string'}}
|
||||
}
|
||||
|
||||
restrictions = {'type': 'array'}
|
||||
|
||||
#Response Schema Definition for Create Service API
|
||||
create_service = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'domains': {'type': 'array',
|
||||
'items': domain,
|
||||
'minItems': 1,
|
||||
'maxItems': 10
|
||||
},
|
||||
'origins': {'type': 'array',
|
||||
'items': origin,
|
||||
'minItems': 1,
|
||||
'maxItems': 10
|
||||
},
|
||||
'caching': {'type': 'array',
|
||||
'items': cache,
|
||||
'minItems': 1,
|
||||
'maxItems': 10
|
||||
},
|
||||
'links': {'type': 'array',
|
||||
'items': links,
|
||||
'minItems': 1,
|
||||
'maxItems': 1},
|
||||
'restrictions': restrictions,
|
||||
},
|
||||
'required': ['domains', 'origins', 'caching', 'links', 'restrictions'],
|
||||
'additionalProperties': False}
|
@ -4,7 +4,7 @@ hacking>=0.5.6,<0.8
|
||||
# Packaging
|
||||
mock>=1.0
|
||||
|
||||
# Unit testing
|
||||
# Unit Tests
|
||||
ddt>=0.4.0
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
@ -16,6 +16,10 @@ testtools>=0.9.32
|
||||
# Functional Tests
|
||||
requests>=1.1
|
||||
|
||||
# API Tests
|
||||
git+https://github.com/stackforge/opencafe.git#egg=opencafe
|
||||
jsonschema
|
||||
|
||||
# Test runner
|
||||
nose
|
||||
nose-exclude
|
||||
|
Loading…
x
Reference in New Issue
Block a user