diff --git a/heatclient/tests/functional/__init__.py b/heatclient/tests/functional/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/heatclient/tests/functional/base.py b/heatclient/tests/functional/base.py new file mode 100644 index 00000000..2f833415 --- /dev/null +++ b/heatclient/tests/functional/base.py @@ -0,0 +1,42 @@ +# 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 tempest_lib.cli import base + + +class ClientTestBase(base.ClientTestBase): + """This is a first pass at a simple read only python-heatclient test. + This only exercises client commands that are read only. + + This should test commands: + * as a regular user + * as a admin user + * with and without optional parameters + * initially just check return codes, and later test command outputs + """ + + def _get_clients(self): + cli_dir = os.environ.get( + 'OS_HEATCLIENT_EXEC_DIR', + os.path.join(os.path.abspath('.'), '.tox/functional/bin')) + + return base.CLIClient( + username=os.environ.get('OS_USERNAME'), + password=os.environ.get('OS_PASSWORD'), + tenant_name=os.environ.get('OS_TENANT_NAME'), + uri=os.environ.get('OS_AUTH_URL'), + cli_dir=cli_dir) + + def heat(self, *args, **kwargs): + return self.clients.heat(*args, **kwargs) diff --git a/heatclient/tests/functional/templates/heat_minimal.yaml b/heatclient/tests/functional/templates/heat_minimal.yaml new file mode 100644 index 00000000..d85e22ca --- /dev/null +++ b/heatclient/tests/functional/templates/heat_minimal.yaml @@ -0,0 +1,18 @@ +HeatTemplateFormatVersion: '2012-12-12' +Description: Minimal template to test validation +Parameters: + InstanceImage: + Description: Glance image name + Type: String + InstanceType: + Description: Nova instance type + Type: String + Default: m1.small + AllowedValues: [m1.tiny, m1.small, m1.medium, m1.large, m1.nano, m1.xlarge, m1.micro, m1.heat] + ConstraintDescription: must be a valid nova instance type. +Resources: + InstanceResource: + Type: OS::Nova::Server + Properties: + flavor: {Ref: InstanceType} + image: {Ref: InstanceImage} diff --git a/heatclient/tests/functional/templates/heat_minimal_hot.yaml b/heatclient/tests/functional/templates/heat_minimal_hot.yaml new file mode 100644 index 00000000..c4eb8b4d --- /dev/null +++ b/heatclient/tests/functional/templates/heat_minimal_hot.yaml @@ -0,0 +1,19 @@ +heat_template_version: 2015-04-30 +description: A minimal HOT test template +parameters: + instance_image: + description: Glance image name + type: string + instance_type: + description: Nova instance type + type: string + default: m1.small + constraints: + - allowed_values: [m1.tiny, m1.small, m1.medium, m1.large, m1.nano, m1.xlarge, m1.micro, m1.heat] + description: must be a valid nova instance type. +resources: + instance: + type: OS::Nova::Server + properties: + image: { get_param: instance_image } + flavor: { get_param: instance_type } diff --git a/heatclient/tests/functional/test_readonly_heat.py b/heatclient/tests/functional/test_readonly_heat.py new file mode 100644 index 00000000..ab04566c --- /dev/null +++ b/heatclient/tests/functional/test_readonly_heat.py @@ -0,0 +1,91 @@ +# 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 json +import os + +from tempest_lib import exceptions +import yaml + +from heatclient.tests.functional import base + + +class SimpleReadOnlyHeatClientTest(base.ClientTestBase): + """Basic, read-only tests for Heat CLI client. + Basic smoke test for the heat CLI commands which do not require + creating or modifying stacks. + """ + + def test_heat_fake_action(self): + self.assertRaises(exceptions.CommandFailed, + self.heat, + 'this-does-not-exist') + + def test_heat_stack_list(self): + self.heat('stack-list') + + def test_heat_stack_list_debug(self): + self.heat('stack-list', flags='--debug') + + def test_heat_resource_template_fmt_default(self): + ret = self.heat('resource-template OS::Nova::Server') + self.assertIn('Type: OS::Nova::Server', ret) + + def test_heat_resource_template_fmt_arg_short_yaml(self): + ret = self.heat('resource-template -F yaml OS::Nova::Server') + self.assertIn('Type: OS::Nova::Server', ret) + self.assertIsInstance(yaml.safe_load(ret), dict) + + def test_heat_resource_template_fmt_arg_long_json(self): + ret = self.heat('resource-template --format json OS::Nova::Server') + self.assertIn('"Type": "OS::Nova::Server"', ret) + self.assertIsInstance(json.loads(ret), dict) + + def test_heat_resource_type_list(self): + ret = self.heat('resource-type-list') + rsrc_types = self.parser.listing(ret) + self.assertTableStruct(rsrc_types, ['resource_type']) + + def test_heat_resource_type_show(self): + rsrc_schema = self.heat('resource-type-show OS::Nova::Server') + # resource-type-show returns a json resource schema + self.assertIsInstance(json.loads(rsrc_schema), dict) + + def _template_validate(self, templ_name): + heat_template_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + 'templates/%s' % templ_name) + ret = self.heat('template-validate -f %s' % heat_template_path) + # On success template-validate returns a json representation + # of the template parameters + self.assertIsInstance(json.loads(ret), dict) + + def test_heat_template_validate_yaml(self): + self._template_validate('heat_minimal.yaml') + + def test_heat_template_validate_hot(self): + self._template_validate('heat_minimal_hot.yaml') + + def test_heat_help(self): + self.heat('help') + + def test_heat_bash_completion(self): + self.heat('bash-completion') + + def test_heat_help_cmd(self): + # Check requesting help for a specific command works + help_text = self.heat('help resource-template') + lines = help_text.split('\n') + self.assertFirstLineStartsWith(lines, 'usage: heat resource-template') + + def test_heat_version(self): + self.heat('', flags='--version') diff --git a/test-requirements.txt b/test-requirements.txt index 598da17b..a4bba485 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -13,6 +13,7 @@ mox3>=0.7.0 oslosphinx>=2.5.0 # Apache-2.0 oslotest>=1.5.1 # Apache-2.0 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 +tempest-lib>=0.5.0 testrepository>=0.0.18 testscenarios>=0.4 testtools>=0.9.36,!=1.2.0 diff --git a/tox.ini b/tox.ini index dde3632e..6110c789 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,11 @@ whitelist_externals = bash [testenv:venv] commands = {posargs} +[testenv:functional] +setenv = + OS_TEST_PATH = ./heatclient/tests/functional +passenv = OS_* + [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}'