Layout some functional tests for the V2 CLI
Change-Id: Ib2d1261bcb0362c586c0aae4b9c5a8a563f07c71
This commit is contained in:
parent
649e7d48c8
commit
6daf82777e
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ doc/build/*
|
||||
dist
|
||||
designateclient/versioninfo
|
||||
.testrepository
|
||||
*.log
|
||||
|
@ -1,4 +1,10 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ./designateclient/tests $LISTOPT $IDOPTION
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_LOG_CAPTURE=${OS_LOG_CAPTURE:-1} \
|
||||
OS_DEBUG=${OS_DEBUG:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-160} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./designateclient/tests} $LISTOPT $IDOPTION
|
||||
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
|
7
designateclient/functionaltests/__init__.py
Normal file
7
designateclient/functionaltests/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
filename='functional-tests.log',
|
||||
filemode='w',
|
||||
level=logging.DEBUG,
|
||||
)
|
26
designateclient/functionaltests/base.py
Normal file
26
designateclient/functionaltests/base.py
Normal file
@ -0,0 +1,26 @@
|
||||
"""
|
||||
Copyright 2015 Rackspace
|
||||
|
||||
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 tempest_lib.cli import base
|
||||
|
||||
from designateclient.functionaltests import client
|
||||
from designateclient.functionaltests import config
|
||||
|
||||
|
||||
class BaseDesignateTest(base.ClientTestBase):
|
||||
|
||||
def _get_clients(self):
|
||||
config.read_config()
|
||||
return client.DesignateCLI.as_user('default')
|
86
designateclient/functionaltests/client.py
Normal file
86
designateclient/functionaltests/client.py
Normal file
@ -0,0 +1,86 @@
|
||||
"""
|
||||
Copyright 2015 Rackspace
|
||||
|
||||
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 logging
|
||||
|
||||
from tempest_lib.cli import base
|
||||
|
||||
from designateclient.functionaltests.config import cfg
|
||||
from designateclient.functionaltests.models import FieldValueModel
|
||||
from designateclient.functionaltests.models import ListModel
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ZoneCommands(object):
|
||||
"""This is a mixin that provides zone commands to DesignateCLI"""
|
||||
|
||||
def zone_list(self, *args, **kwargs):
|
||||
return self.parsed_cmd('zone list', ListModel, *args, **kwargs)
|
||||
|
||||
def zone_show(self, id, *args, **kwargs):
|
||||
return self.parsed_cmd('zone show %s' % id, FieldValueModel, *args,
|
||||
**kwargs)
|
||||
|
||||
def zone_delete(self, id, *args, **kwargs):
|
||||
return self.parsed_cmd('zone delete %s' % id, *args, **kwargs)
|
||||
|
||||
def zone_create(self, name, email, *args, **kwargs):
|
||||
cmd = 'zone create %s --email %s' % (name, email)
|
||||
return self.parsed_cmd(cmd, FieldValueModel, *args, **kwargs)
|
||||
|
||||
|
||||
class DesignateCLI(base.CLIClient, ZoneCommands):
|
||||
|
||||
@classmethod
|
||||
def get_clients(cls):
|
||||
return {
|
||||
'default': DesignateCLI(
|
||||
cli_dir=cfg.CONF.designateclient.directory,
|
||||
username=cfg.CONF.identity.username,
|
||||
password=cfg.CONF.identity.password,
|
||||
tenant_name=cfg.CONF.identity.tenant_name,
|
||||
uri=cfg.CONF.identity.uri,
|
||||
),
|
||||
'alt': DesignateCLI(
|
||||
cli_dir=cfg.CONF.designateclient.directory,
|
||||
username=cfg.CONF.identity.alt_username,
|
||||
password=cfg.CONF.identity.alt_password,
|
||||
tenant_name=cfg.CONF.identity.alt_tenant_name,
|
||||
uri=cfg.CONF.identity.uri,
|
||||
),
|
||||
'admin': DesignateCLI(
|
||||
cli_dir=cfg.CONF.designateclient.directory,
|
||||
username=cfg.CONF.identity.admin_username,
|
||||
password=cfg.CONF.identity.admin_password,
|
||||
tenant_name=cfg.CONF.identity.admin_tenant_name,
|
||||
uri=cfg.CONF.identity.uri,
|
||||
)
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def as_user(self, user):
|
||||
clients = self.get_clients()
|
||||
if user in clients:
|
||||
return clients[user]
|
||||
raise Exception("User '{0}' does not exist".format(user))
|
||||
|
||||
def parsed_cmd(self, cmd, model=None, *args, **kwargs):
|
||||
out = self.openstack(cmd, *args, **kwargs)
|
||||
LOG.debug(out)
|
||||
if model is not None:
|
||||
return model(out)
|
||||
return out
|
64
designateclient/functionaltests/config.py
Normal file
64
designateclient/functionaltests/config.py
Normal file
@ -0,0 +1,64 @@
|
||||
"""
|
||||
Copyright 2015 Rackspace
|
||||
|
||||
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 os
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
cfg.CONF.register_group(cfg.OptGroup(
|
||||
name='identity', title="Configuration for Keystone auth"
|
||||
))
|
||||
|
||||
cfg.CONF.register_group(cfg.OptGroup(
|
||||
name='designateclient', title="Configuration for the Designate client"
|
||||
))
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('uri', help="The Keystone v2 endpoint"),
|
||||
cfg.StrOpt('uri_v3', help="The Keystone v3 endpoint"),
|
||||
cfg.StrOpt('auth_version', default='v2'),
|
||||
cfg.StrOpt('region', default='RegionOne'),
|
||||
|
||||
cfg.StrOpt('username'),
|
||||
cfg.StrOpt('tenant_name'),
|
||||
cfg.StrOpt('password', secret=True),
|
||||
cfg.StrOpt('domain_name'),
|
||||
|
||||
cfg.StrOpt('alt_username'),
|
||||
cfg.StrOpt('alt_tenant_name'),
|
||||
cfg.StrOpt('alt_password', secret=True),
|
||||
cfg.StrOpt('alt_domain_name'),
|
||||
|
||||
cfg.StrOpt('admin_username'),
|
||||
cfg.StrOpt('admin_tenant_name'),
|
||||
cfg.StrOpt('admin_password', secret=True),
|
||||
cfg.StrOpt('admin_domain_name'),
|
||||
], group='identity')
|
||||
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('directory',
|
||||
help='the directory containing the client executable'),
|
||||
], group='designateclient')
|
||||
|
||||
|
||||
def find_config_file():
|
||||
return os.environ.get(
|
||||
'TEMPEST_CONFIG', '/opt/stack/tempest/etc/tempest.conf')
|
||||
|
||||
|
||||
def read_config():
|
||||
cfg.CONF(args=[], default_config_files=[find_config_file()])
|
22
designateclient/functionaltests/datagen.py
Normal file
22
designateclient/functionaltests/datagen.py
Normal file
@ -0,0 +1,22 @@
|
||||
"""
|
||||
Copyright 2015 Rackspace
|
||||
|
||||
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 random
|
||||
import string
|
||||
|
||||
|
||||
def random_zone_name(name='testdomain'):
|
||||
digits = "".join([random.choice(string.digits) for _ in range(8)])
|
||||
return "{0}{1}.com.".format(name, digits)
|
80
designateclient/functionaltests/models.py
Normal file
80
designateclient/functionaltests/models.py
Normal file
@ -0,0 +1,80 @@
|
||||
"""
|
||||
Copyright 2015 Rackspace
|
||||
|
||||
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 tempest_lib.cli import output_parser
|
||||
|
||||
|
||||
class Model(object):
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__dict__)
|
||||
|
||||
|
||||
class FieldValueModel(Model):
|
||||
"""This converts cli output from messy lists/dicts to neat attributes."""
|
||||
|
||||
def __init__(self, out):
|
||||
"""This parses output with fields and values like:
|
||||
|
||||
+----------------+------------------------------+
|
||||
| Field | Value |
|
||||
+----------------+------------------------------+
|
||||
| action | CREATE |
|
||||
| created_at | 2015-08-20T17:22:17.000000 |
|
||||
| description | None |
|
||||
+----------------+------------------------------+
|
||||
|
||||
These are then accessible as:
|
||||
|
||||
model.action
|
||||
model.created_at
|
||||
model.description
|
||||
|
||||
"""
|
||||
table = output_parser.table(out)
|
||||
for field, value in table['values']:
|
||||
setattr(self, field, value)
|
||||
|
||||
|
||||
class ListEntryModel(Model):
|
||||
|
||||
def __init__(self, fields, values):
|
||||
for k, v in zip(fields, values):
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class ListModel(Model, list):
|
||||
|
||||
def __init__(self, out):
|
||||
"""This parses an output table with any number of headers, and any
|
||||
number of entries:
|
||||
|
||||
+--------------------------------------+----------+---------+
|
||||
| id | name | type |
|
||||
+--------------------------------------+----------+---------+
|
||||
| e658a875-1024-4f88-a347-e5b244ec5a10 | aaa.com. | PRIMARY |
|
||||
+--------------------------------------+----------+---------+
|
||||
| 98d1fb5f-2954-448e-988e-6f1df0f24c52 | bbb.com. | PRIMARY |
|
||||
+--------------------------------------+----------+---------+
|
||||
|
||||
These are then accessible as:
|
||||
|
||||
model[0].name == 'aaa.com.'
|
||||
model[1].name == 'bbb.com.'
|
||||
|
||||
"""
|
||||
table = output_parser.table(out)
|
||||
for entry in table['values']:
|
||||
self.append(ListEntryModel(table['headers'], entry))
|
0
designateclient/functionaltests/v2/__init__.py
Normal file
0
designateclient/functionaltests/v2/__init__.py
Normal file
47
designateclient/functionaltests/v2/test_zone.py
Normal file
47
designateclient/functionaltests/v2/test_zone.py
Normal file
@ -0,0 +1,47 @@
|
||||
"""
|
||||
Copyright 2015 Rackspace
|
||||
|
||||
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 tempest_lib.exceptions import CommandFailed
|
||||
|
||||
from designateclient.functionaltests.base import BaseDesignateTest
|
||||
from designateclient.functionaltests.datagen import random_zone_name
|
||||
|
||||
|
||||
class TestZone(BaseDesignateTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestZone, self).setUp()
|
||||
self.zone_name = random_zone_name()
|
||||
zone = self.clients.zone_create(name=self.zone_name,
|
||||
email='test@example.com')
|
||||
self.zone_id = zone.id
|
||||
|
||||
def test_zone_list(self):
|
||||
zones = self.clients.zone_list()
|
||||
self.assertGreater(len(zones), 0)
|
||||
|
||||
def test_zone_create_and_show(self):
|
||||
zone = self.clients.zone_show(self.zone_id)
|
||||
self.assertEqual(zone.name, self.zone_name)
|
||||
self.assertEqual(zone.id, self.zone_id)
|
||||
|
||||
def test_zone_delete(self):
|
||||
self.clients.zone_delete(self.zone_id)
|
||||
self.assertRaises(CommandFailed, self.clients.zone_show, self.zone_id)
|
||||
|
||||
def tearDown(self):
|
||||
if hasattr(self, 'zone_id'):
|
||||
self.clients.zone_delete(self.zone_id, fail_ok=True)
|
||||
super(TestZone, self).tearDown()
|
67
doc/source/functional-tests.rst
Normal file
67
doc/source/functional-tests.rst
Normal file
@ -0,0 +1,67 @@
|
||||
================
|
||||
Functional Tests
|
||||
================
|
||||
|
||||
The functional tests invoke the client executable to see that it actually works
|
||||
with a running Designate. WARNING: these tests will create and delete zones,
|
||||
recordsets, and other resources in Designate.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
cd python-designateclient
|
||||
pip install python-openstackclient
|
||||
pip install -r requirements.txt -r test-requirements.txt
|
||||
pip install -e .
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The functional tests look for a variable ``TEMPEST_CONFIG`` which specifies a
|
||||
config file for the test.
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
export TEMPEST_CONFIG=tempest.conf
|
||||
|
||||
The tests will use Keystone to grab the Designate endpoint to test against.
|
||||
They need at least three users (two regular users, and one admin) for all the
|
||||
tests to run.
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
[identity]
|
||||
uri = http://localhost:5000/v2.0
|
||||
uri_v3 = http://localhost:5000/v3
|
||||
auth_version = v2
|
||||
region = RegionOne
|
||||
|
||||
username = demo
|
||||
tenant_name = demo
|
||||
password = password
|
||||
domain_name = Default
|
||||
|
||||
alt_username = alt_demo
|
||||
alt_tenant_name = alt_demo
|
||||
alt_password = password
|
||||
alt_domain_name = Default
|
||||
|
||||
admin_username = admin
|
||||
admin_tenant_name = admin
|
||||
admin_password = password
|
||||
admin_domain_name = Default
|
||||
|
||||
[designateclient]
|
||||
# the directory containing the openstack executable
|
||||
directory=/root/python-designateclient/.venv/bin
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
|
||||
The functional tests are run with tox (installed with ``pip install tox``):
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
tox -e functional
|
@ -21,6 +21,7 @@ Contents
|
||||
shell-examples
|
||||
shell-v2
|
||||
contributing
|
||||
functional-tests
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
@ -12,3 +12,4 @@ requests-mock>=0.6.0 # Apache-2.0
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
|
||||
testrepository>=0.0.18
|
||||
oslosphinx>=2.5.0 # Apache-2.0
|
||||
tempest-lib>=0.8.0
|
||||
|
10
tox.ini
10
tox.ini
@ -33,6 +33,16 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:functional]
|
||||
usedevelop = False
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
OS_TEST_PATH=designateclient/functionaltests/
|
||||
passenv = OS_STDOUT_CAPTURE
|
||||
OS_STDERR_CAPTURE
|
||||
OS_LOG_CAPTURE
|
||||
OS_DEBUG
|
||||
TEMPEST_CONFIG
|
||||
|
||||
[flake8]
|
||||
# ignored flake8 codes:
|
||||
# H302 import only modules
|
||||
|
Loading…
Reference in New Issue
Block a user