Add basic read-only tests for heat cli

Adds some smoke-tests for the heat CLI interface, similar to what
exists for other projects - it's not an exhaustive test but it
provides coverage of the interfaces which do not require content
to be created or modified.

Change-Id: I9921815bfb17fbe4d010d41e2a79a8d9b292271d
blueprint: tempest-heat-integration
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index ec8b3a1..547d0d0 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -86,6 +86,12 @@
         return self.cmd_with_auth(
             'ceilometer', action, flags, params, admin, fail_ok)
 
+    def heat(self, action, flags='', params='', admin=True,
+             fail_ok=False):
+        """Executes heat command for the given action."""
+        return self.cmd_with_auth(
+            'heat', action, flags, params, admin, fail_ok)
+
     def cinder(self, action, flags='', params='', admin=True, fail_ok=False):
         """Executes cinder command for the given action."""
         return self.cmd_with_auth(
diff --git a/tempest/cli/simple_read_only/heat_templates/heat_minimal.yaml b/tempest/cli/simple_read_only/heat_templates/heat_minimal.yaml
new file mode 100644
index 0000000..7dcda39
--- /dev/null
+++ b/tempest/cli/simple_read_only/heat_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]
+    ConstraintDescription: must be a valid nova instance type.
+Resources:
+    InstanceResource:
+        Type: OS::Nova::Server
+        Properties:
+          flavor: {Ref: InstanceType}
+          image: {Ref: InstanceImage}
diff --git a/tempest/cli/simple_read_only/heat_templates/heat_minimal_hot.yaml b/tempest/cli/simple_read_only/heat_templates/heat_minimal_hot.yaml
new file mode 100644
index 0000000..6d89b7b
--- /dev/null
+++ b/tempest/cli/simple_read_only/heat_templates/heat_minimal_hot.yaml
@@ -0,0 +1,19 @@
+heat_template_version: 2013-05-23
+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.small, m1.medium, m1.large]
+          description: instance_type must be one of m1.small, m1.medium or m1.large
+resources:
+    instance:
+        type: OS::Nova::Server
+        properties:
+            image: { get_param: instance_image }
+            flavor: { get_param: instance_type }
diff --git a/tempest/cli/simple_read_only/test_heat.py b/tempest/cli/simple_read_only/test_heat.py
new file mode 100644
index 0000000..e2fefe8
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_heat.py
@@ -0,0 +1,98 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#    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
+import yaml
+
+from oslo.config import cfg
+
+import tempest.cli
+from tempest.openstack.common import log as logging
+
+CONF = cfg.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class SimpleReadOnlyHeatClientTest(tempest.cli.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.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        if (not CONF.service_available.heat):
+            msg = ("Skipping all Heat cli tests because it is "
+                   "not available")
+            raise cls.skipException(msg)
+        super(SimpleReadOnlyHeatClientTest, cls).setUpClass()
+
+    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 test_heat_template_validate_yaml(self):
+        filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                                'heat_templates/heat_minimal.yaml')
+        ret = self.heat('template-validate -f %s' % filepath)
+        # On success template-validate returns a json representation
+        # of the template parameters
+        self.assertIsInstance(json.loads(ret), dict)
+
+    def test_heat_template_validate_hot(self):
+        filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                                'heat_templates/heat_minimal_hot.yaml')
+        ret = self.heat('template-validate -f %s' % filepath)
+        self.assertIsInstance(json.loads(ret), dict)
+
+    def test_heat_help(self):
+        self.heat('help')
+
+    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')