Add basic stack-preview functional test
Change-Id: I401bc78488f62fe4d4674ae2d32678cce0242a56
Closes-bug: #1479594
diff --git a/functional/test_preview.py b/functional/test_preview.py
new file mode 100644
index 0000000..b4389ff
--- /dev/null
+++ b/functional/test_preview.py
@@ -0,0 +1,187 @@
+# 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 heat_integrationtests.common import test
+from heatclient import exc
+import six
+
+
+class StackPreviewTest(test.HeatIntegrationTest):
+ template = '''
+heat_template_version: 2015-04-30
+parameters:
+ incomming:
+ type: string
+resources:
+ one:
+ type: OS::Heat::TestResource
+ properties:
+ value: fred
+ two:
+ type: OS::Heat::TestResource
+ properties:
+ value: {get_param: incomming}
+ depends_on: one
+outputs:
+ main_out:
+ value: {get_attr: [two, output]}
+ '''
+ env = '''
+parameters:
+ incomming: abc
+ '''
+
+ def setUp(self):
+ super(StackPreviewTest, self).setUp()
+ self.client = self.orchestration_client
+ self.project_id = self.identity_client.auth_ref.project_id
+
+ def _assert_resource(self, res, stack_name):
+ self.assertEqual(stack_name, res['stack_name'])
+ self.assertEqual('INIT', res['resource_action'])
+ self.assertEqual('COMPLETE', res['resource_status'])
+ for field in ('resource_status_reason', 'physical_resource_id',
+ 'description'):
+ self.assertIn(field, res)
+ self.assertEqual('', res[field])
+ for field in ('creation_time', 'updated_time'):
+ self.assertIn(field, res)
+ self.assertIsNotNone(res[field])
+ self.assertIn('output', res['attributes'])
+
+ # resource_identity
+ self.assertEqual(stack_name,
+ res['resource_identity']['stack_name'])
+ self.assertEqual('None', res['resource_identity']['stack_id'])
+ self.assertEqual(self.project_id,
+ res['resource_identity']['tenant'])
+ self.assertEqual('/resources/%s' % res['resource_name'],
+ res['resource_identity']['path'])
+ # stack_identity
+ self.assertEqual(stack_name,
+ res['stack_identity']['stack_name'])
+ self.assertEqual('None', res['stack_identity']['stack_id'])
+ self.assertEqual(self.project_id,
+ res['stack_identity']['tenant'])
+ self.assertEqual('', res['stack_identity']['path'])
+
+ def _assert_results(self, result, stack_name):
+ # global stuff.
+ self.assertEqual(stack_name, result['stack_name'])
+ self.assertTrue(result['disable_rollback'])
+ self.assertEqual('None', result['id'])
+ self.assertIsNone(result['parent'])
+ self.assertEqual('No description', result['template_description'])
+
+ # parameters
+ self.assertEqual('None', result['parameters']['OS::stack_id'])
+ self.assertEqual(stack_name, result['parameters']['OS::stack_name'])
+ self.assertEqual('abc', result['parameters']['incomming'])
+
+ def test_basic_pass(self):
+ stack_name = self._stack_rand_name()
+ result = self.client.stacks.preview(
+ template=self.template,
+ stack_name=stack_name,
+ disable_rollback=True,
+ environment=self.env).to_dict()
+
+ self._assert_results(result, stack_name)
+ for res in result['resources']:
+ self._assert_resource(res, stack_name)
+ self.assertEqual('OS::Heat::TestResource',
+ res['resource_type'])
+
+ # common properties
+ self.assertEqual(False, res['properties']['fail'])
+ self.assertEqual(0, res['properties']['wait_secs'])
+ self.assertEqual(False, res['properties']['update_replace'])
+
+ if res['resource_name'] == 'one':
+ self.assertEqual('fred', res['properties']['value'])
+ self.assertEqual(['two'], res['required_by'])
+ if res['resource_name'] == 'two':
+ self.assertEqual('abc', res['properties']['value'])
+ self.assertEqual([], res['required_by'])
+
+ def test_basic_fail(self):
+ stack_name = self._stack_rand_name()
+
+ # break the template so it fails validation.
+ wont_work = self.template.replace('get_param: incomming',
+ 'get_param: missing')
+ excp = self.assertRaises(exc.HTTPBadRequest,
+ self.client.stacks.preview,
+ template=wont_work,
+ stack_name=stack_name,
+ disable_rollback=True,
+ environment=self.env)
+
+ self.assertIn('Property error: : resources.two.properties.value: '
+ ': The Parameter (missing) was not provided.',
+ six.text_type(excp))
+
+ def test_nested_pass(self):
+ """Nested stacks need to recurse down the stacks."""
+ main_template = '''
+heat_template_version: 2015-04-30
+parameters:
+ incomming:
+ type: string
+resources:
+ main:
+ type: nested.yaml
+ properties:
+ value: {get_param: incomming}
+outputs:
+ main_out:
+ value: {get_attr: [main, output]}
+ '''
+ nested_template = '''
+heat_template_version: 2015-04-30
+parameters:
+ value:
+ type: string
+resources:
+ nested:
+ type: OS::Heat::TestResource
+ properties:
+ value: {get_param: value}
+outputs:
+ output:
+ value: {get_attr: [nested, output]}
+'''
+ stack_name = self._stack_rand_name()
+ result = self.client.stacks.preview(
+ disable_rollback=True,
+ stack_name=stack_name,
+ template=main_template,
+ files={'nested.yaml': nested_template},
+ environment=self.env).to_dict()
+
+ self._assert_results(result, stack_name)
+
+ # nested resources return a list of their resources.
+ res = result['resources'][0][0]
+ nested_stack_name = '%s-%s' % (stack_name,
+ res['parent_resource'])
+
+ self._assert_resource(res, nested_stack_name)
+ self.assertEqual('OS::Heat::TestResource',
+ res['resource_type'])
+
+ self.assertEqual(False, res['properties']['fail'])
+ self.assertEqual(0, res['properties']['wait_secs'])
+ self.assertEqual(False, res['properties']['update_replace'])
+
+ self.assertEqual('abc', res['properties']['value'])
+ self.assertEqual([], res['required_by'])