Add API Test Structure

This patch adds the initial directory structure and helpers needed
for API tests.
This commit is contained in:
Malini Kamalambal 2014-07-14 11:56:26 -04:00
parent aaa8b274a0
commit 896eab13c8
15 changed files with 377 additions and 1 deletions

0
tests/api/__init__.py Normal file
View File

View 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

View File

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

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

View File

49
tests/api/utils/base.py Normal file
View 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
View 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
View 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")

View File

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

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

View File

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

View File

@ -4,7 +4,7 @@ hacking>=0.5.6,<0.8
# Packaging # Packaging
mock>=1.0 mock>=1.0
# Unit testing # Unit Tests
ddt>=0.4.0 ddt>=0.4.0
discover discover
fixtures>=0.3.14 fixtures>=0.3.14
@ -16,6 +16,10 @@ testtools>=0.9.32
# Functional Tests # Functional Tests
requests>=1.1 requests>=1.1
# API Tests
git+https://github.com/stackforge/opencafe.git#egg=opencafe
jsonschema
# Test runner # Test runner
nose nose
nose-exclude nose-exclude