heat API : Add more unit tests

Add more unit tests to improve API test coverage

Change-Id: I5f3a8933a9a09acb6b904ccc76ec20ad210a5974
Signed-off-by: Steven Hardy <shardy@redhat.com>
This commit is contained in:
Steven Hardy 2012-07-23 17:43:39 +01:00
parent 312a84ec70
commit 64f9b72c8e

View File

@ -13,13 +13,23 @@
# under the License.
import sys
import socket
import nose
import mox
import json
import unittest
from nose.plugins.attrib import attr
from heat.common import config
from heat.openstack.common import cfg
import heat.common.config
import httplib
import json
import urlparse
from heat.common import context
from heat.engine import auth
from heat.openstack.common import rpc
from heat.common.wsgi import Request
from heat.api.v1 import exception
import heat.api.v1.stacks as stacks
@ -30,6 +40,42 @@ class StackControllerTest(unittest.TestCase):
Tests the API class which acts as the WSGI controller,
the endpoint processing API requests after they are routed
'''
# Utility functions
def _create_context(self, user='api_test_user'):
ctx = context.get_admin_context()
self.m.StubOutWithMock(ctx, 'username')
ctx.username = user
self.m.StubOutWithMock(auth, 'authenticate')
return ctx
def _dummy_GET_request(self, params={}):
# Mangle the params dict into a query string
qs = "&".join(["=".join([k, str(params[k])]) for k in params])
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': qs}
req = Request(environ)
req.context = self._create_context()
return req
# The tests
def test_format_response(self):
response = self.controller._format_response("Foo", "Bar")
expected = {'FooResponse': {'FooResult': 'Bar'}}
self.assert_(response == expected)
def test_stackid_addprefix(self):
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
response = self.controller._stackid_addprefix({'StackName': 'Foo',
'StackId': str(123)})
expected = {'StackName': 'Foo',
'StackId': 'ahostname:8000:stack/Foo/123'}
self.assert_(response == expected)
def test_params_extract(self):
p = {'Parameters.member.Foo.ParameterKey': 'foo',
'Parameters.member.Foo.ParameterValue': 'bar',
@ -76,15 +122,497 @@ class StackControllerTest(unittest.TestCase):
params = self.controller._extract_user_params(p)
self.assertFalse(params)
# TODO : lots more StackController tests..
def test_reformat_dict_keys(self):
keymap = {"foo": "bar"}
data = {"foo": 123}
expected = {"bar": 123}
result = self.controller._reformat_dict_keys(keymap, data)
self.assertEqual(result, expected)
def test_list(self):
# Format a dummy GET request to pass into the WSGI handler
params = {'Action': 'ListStacks'}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = {u'stacks': [
{u'stack_id': u'1',
u'updated_time': u'2012-07-09T09:13:11Z',
u'template_description': u'blah',
u'stack_status_reason': u'Stack successfully created',
u'creation_time': u'2012-07-09T09:12:45Z',
u'stack_name': u'wordpress',
u'stack_status': u'CREATE_COMPLETE'}]}
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'show_stack',
'args': {'stack_name': None,
'params': dict(dummy_req.params)}}
).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
# Call the list controller function and compare the response
result = self.controller.list(dummy_req)
expected = {'ListStacksResponse': {'ListStacksResult':
{'StackSummaries': [
{u'StackId': u'ahostname:8000:stack/wordpress/1',
u'LastUpdatedTime': u'2012-07-09T09:13:11Z',
u'TemplateDescription': u'blah',
u'StackStatusReason': u'Stack successfully created',
u'CreationTime': u'2012-07-09T09:12:45Z',
u'StackName': u'wordpress', u'StackStatus': u'CREATE_COMPLETE'}]}}}
self.assertEqual(result, expected)
def test_describe(self):
# Format a dummy GET request to pass into the WSGI handler
stack_name = "wordpress"
params = {'Action': 'DescribeStacks', 'StackName': stack_name}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
# Note the engine returns a load of keys we don't actually use
# so this is a subset of the real response format
engine_resp = {u'stacks': [
{u'stack_id': u'6',
u'updated_time': u'2012-07-09T09:13:11Z',
u'parameters':{
u'DBUsername': {u'Default': u'admin'},
u'LinuxDistribution': {u'Default': u'F16'},
u'InstanceType': {u'Default': u'm1.large'},
u'DBRootPassword': {u'Default': u'admin'},
u'DBPassword': {u'Default': u'admin'},
u'DBName': {u'Default': u'wordpress'}},
u'outputs':
[{u'output_key': u'WebsiteURL',
u'description': u'URL for Wordpress wiki',
u'output_value': u'http://10.0.0.8/wordpress'}],
u'stack_status_reason': u'Stack successfully created',
u'creation_time': u'2012-07-09T09:12:45Z',
u'stack_name': u'wordpress',
u'notification_topics': [],
u'stack_status': u'CREATE_COMPLETE',
u'description': u'blah',
u'disable_rollback': True,
u'timeout_mins':60,
u'capabilities':[]}]}
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'show_stack', 'args':
{'stack_name': stack_name,
'params': dict(dummy_req.params)}}).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
# Call the list controller function and compare the response
response = self.controller.describe(dummy_req)
expected = {'DescribeStacksResponse': {'DescribeStacksResult':
{'Stacks':
[{'StackId': u'ahostname:8000:stack/wordpress/6',
'StackStatusReason': u'Stack successfully created',
'Description': u'blah',
'Parameters':
[{'ParameterValue': u'admin',
'ParameterKey': u'DBUsername'},
{'ParameterValue': u'F16',
'ParameterKey': u'LinuxDistribution'},
{'ParameterValue': u'm1.large',
'ParameterKey': u'InstanceType'},
{'ParameterValue': u'admin',
'ParameterKey': u'DBRootPassword'},
{'ParameterValue': u'admin',
'ParameterKey': u'DBPassword'},
{'ParameterValue': u'wordpress',
'ParameterKey': u'DBName'}],
'Outputs':
[{'OutputKey': u'WebsiteURL',
'OutputValue': u'http://10.0.0.8/wordpress',
'Description': u'URL for Wordpress wiki'}],
'TimeoutInMinutes': 60,
'CreationTime': u'2012-07-09T09:12:45Z',
'Capabilities': [],
'StackName': u'wordpress',
'NotificationARNs': [],
'StackStatus': u'CREATE_COMPLETE',
'DisableRollback': True,
'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}}
self.assert_(response == expected)
def test_get_template_int_body(self):
''' Test the internal _get_template function '''
params = {'TemplateBody': "abcdef"}
dummy_req = self._dummy_GET_request(params)
result = self.controller._get_template(dummy_req)
expected = "abcdef"
self.assert_(result == expected)
# TODO : test the _get_template TemplateUrl case
def test_create(self):
# Format a dummy request
stack_name = "wordpress"
template = {u'Foo': u'bar'}
json_template = json.dumps(template)
params = {'Action': 'CreateStack', 'StackName': stack_name,
'TemplateBody': '%s' % json_template,
'TimeoutInMinutes': 30,
'Parameters.member.1.ParameterKey': 'InstanceType',
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
engine_parms = {u'InstanceType': u'm1.xlarge'}
engine_args = {'timeout_mins': u'30'}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = {u'StackName': u'wordpress', u'StackId': 1}
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'create_stack',
'args':
{'stack_name': stack_name,
'template': template,
'params': engine_parms,
'args': engine_args}}).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
response = self.controller.create(dummy_req)
expected = {'CreateStackResponse': {'CreateStackResult':
{u'StackName': u'wordpress',
u'StackId': u'ahostname:8000:stack/wordpress/1'}}}
self.assert_(response == expected)
def test_update(self):
# Format a dummy request
stack_name = "wordpress"
template = {u'Foo': u'bar'}
json_template = json.dumps(template)
params = {'Action': 'UpdateStack', 'StackName': stack_name,
'TemplateBody': '%s' % json_template,
'Parameters.member.1.ParameterKey': 'InstanceType',
'Parameters.member.1.ParameterValue': 'm1.xlarge'}
engine_parms = {u'InstanceType': u'm1.xlarge'}
engine_args = {}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = {u'StackName': u'wordpress', u'StackId': 1}
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'update_stack',
'args':
{'stack_name': stack_name,
'template': template,
'params': engine_parms,
'args': engine_args}}).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
response = self.controller.update(dummy_req)
expected = {'UpdateStackResponse': {'UpdateStackResult':
{u'StackName': u'wordpress',
u'StackId': u'ahostname:8000:stack/wordpress/1'}}}
self.assert_(response == expected)
def test_get_template(self):
# Format a dummy request
stack_name = "wordpress"
template = {u'Foo': u'bar'}
params = {'Action': 'GetTemplate', 'StackName': stack_name}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = template
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'get_template',
'args':
{'stack_name': stack_name,
'params': dict(dummy_req.params)}}).AndReturn(engine_resp)
self.m.ReplayAll()
response = self.controller.get_template(dummy_req)
expected = {'GetTemplateResponse': {'GetTemplateResult':
{'TemplateBody': template}}}
self.assert_(response == expected)
def test_delete(self):
# Format a dummy request
stack_name = "wordpress"
params = {'Action': 'DeleteStack', 'StackName': stack_name}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
# Engine returns None when delete successful
engine_resp = None
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'delete_stack',
'args':
{'stack_name': stack_name,
'params': dict(dummy_req.params)}}).AndReturn(engine_resp)
self.m.ReplayAll()
response = self.controller.delete(dummy_req)
expected = {'DeleteStackResponse': {'DeleteStackResult': ''}}
self.assert_(response == expected)
def test_events_list(self):
# Format a dummy request
stack_name = "wordpress"
params = {'Action': 'DescribeStackEvents', 'StackName': stack_name}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = {u'events': [{u'stack_name': u'wordpress',
u'event_time': u'2012-07-23T13:05:39Z',
u'stack_id': 6,
u'logical_resource_id': u'WikiDatabase',
u'resource_status_reason': u'state changed',
u'event_id': 42,
u'resource_status': u'IN_PROGRESS',
u'physical_resource_id': None,
u'resource_properties':
{u'UserData': u'blah'},
u'resource_type': u'AWS::EC2::Instance'}]}
self.m.StubOutWithMock(rpc, 'call')
rpc.call(dummy_req.context, 'engine', {'method': 'list_events',
'args':
{'stack_name': stack_name,
'params': dict(dummy_req.params)}}).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
response = self.controller.events_list(dummy_req)
expected = {'DescribeStackEventsResponse':
{'DescribeStackEventsResult':
{'StackEvents':
[{'EventId': 42,
'StackId': u'ahostname:8000:stack/wordpress/6',
'ResourceStatus': u'IN_PROGRESS',
'ResourceType': u'AWS::EC2::Instance',
'Timestamp': u'2012-07-23T13:05:39Z',
'StackName': u'wordpress',
'ResourceProperties': {u'UserData': u'blah'},
'PhysicalResourceId': None,
'ResourceStatusData': u'state changed',
'LogicalResourceId': u'WikiDatabase'}]}}}
self.assert_(response == expected)
def test_describe_stack_resource(self):
# Format a dummy request
stack_name = "wordpress"
params = {'Action': 'DescribeStackResource',
'StackName': stack_name,
'LogicalResourceId': "WikiDatabase"}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = {u'description': u'',
u'stack_name': u'wordpress',
u'logical_resource_id': u'WikiDatabase',
u'resource_status_reason': None,
u'updated_time': u'2012-07-23T13:06:00Z',
u'stack_id': 6,
u'resource_status': u'CREATE_COMPLETE',
u'physical_resource_id':
u'a3455d8c-9f88-404d-a85b-5315293e67de',
u'resource_type': u'AWS::EC2::Instance',
u'metadata': {u'wordpress': []}}
self.m.StubOutWithMock(rpc, 'call')
args = {
'stack_name': dummy_req.params.get('StackName'),
'resource_name': dummy_req.params.get('LogicalResourceId'),
}
rpc.call(dummy_req.context, 'engine',
{'method': 'describe_stack_resource',
'args': args}).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
response = self.controller.describe_stack_resource(dummy_req)
expected = {'DescribeStackResourceResponse':
{'DescribeStackResourceResult':
{'StackResourceDetail':
{'StackId': u'ahostname:8000:stack/wordpress/6',
'ResourceStatus': u'CREATE_COMPLETE',
'Description': u'',
'ResourceType': u'AWS::EC2::Instance',
'ResourceStatusReason': None,
'LastUpdatedTimestamp': u'2012-07-23T13:06:00Z',
'StackName': u'wordpress',
'PhysicalResourceId':
u'a3455d8c-9f88-404d-a85b-5315293e67de',
'Metadata': {u'wordpress': []},
'LogicalResourceId': u'WikiDatabase'}}}}
self.assert_(response == expected)
def test_describe_stack_resources(self):
# Format a dummy request
stack_name = "wordpress"
params = {'Action': 'DescribeStackResources',
'StackName': stack_name,
'LogicalResourceId': "WikiDatabase"}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = [{u'description': u'',
u'stack_name': u'wordpress',
u'logical_resource_id': u'WikiDatabase',
u'resource_status_reason': None,
u'updated_time': u'2012-07-23T13:06:00Z',
u'stack_id': 6,
u'resource_status': u'CREATE_COMPLETE',
u'physical_resource_id':
u'a3455d8c-9f88-404d-a85b-5315293e67de',
u'resource_type': u'AWS::EC2::Instance',
u'metadata': {u'ensureRunning': u'true''true'}}]
self.m.StubOutWithMock(rpc, 'call')
args = {
'stack_name': dummy_req.params.get('StackName'),
'physical_resource_id': None,
'logical_resource_id': dummy_req.params.get('LogicalResourceId'),
}
rpc.call(dummy_req.context, 'engine',
{'method': 'describe_stack_resources',
'args': args}).AndReturn(engine_resp)
# Stub socket.gethostname so it returns "ahostname"
self.m.StubOutWithMock(socket, 'gethostname')
socket.gethostname().AndReturn("ahostname")
self.m.ReplayAll()
response = self.controller.describe_stack_resources(dummy_req)
expected = {'DescribeStackResourcesResponse':
{'DescribeStackResourcesResult':
{'StackResources':
[{'StackId': u'ahostname:8000:stack/wordpress/6',
'ResourceStatus': u'CREATE_COMPLETE',
'Description': u'',
'ResourceType': u'AWS::EC2::Instance',
'Timestamp': u'2012-07-23T13:06:00Z',
'ResourceStatusReason': None,
'StackName': u'wordpress',
'PhysicalResourceId':
u'a3455d8c-9f88-404d-a85b-5315293e67de',
'LogicalResourceId': u'WikiDatabase'}]}}}
self.assert_(response == expected)
def test_describe_stack_resources_err_inval(self):
# Format a dummy request containing both StackName and
# PhysicalResourceId, which is invalid and should throw a
# HeatInvalidParameterCombinationError
stack_name = "wordpress"
params = {'Action': 'DescribeStackResources',
'StackName': stack_name,
'PhysicalResourceId': "123456"}
dummy_req = self._dummy_GET_request(params)
ret = self.controller.describe_stack_resources(dummy_req)
self.assert_(type(ret) ==
exception.HeatInvalidParameterCombinationError)
def test_list_stack_resources(self):
# Format a dummy request
stack_name = "wordpress"
params = {'Action': 'ListStackResources',
'StackName': stack_name}
dummy_req = self._dummy_GET_request(params)
# Stub out the RPC call to the engine with a pre-canned response
engine_resp = [{u'description': u'',
u'stack_name': u'wordpress',
u'logical_resource_id': u'WikiDatabase',
u'resource_status_reason': None,
u'updated_time': u'2012-07-23T13:06:00Z',
u'stack_id': 6,
u'resource_status': u'CREATE_COMPLETE',
u'physical_resource_id':
u'a3455d8c-9f88-404d-a85b-5315293e67de',
u'resource_type': u'AWS::EC2::Instance',
u'metadata': {}}]
self.m.StubOutWithMock(rpc, 'call')
args = {
'stack_name': dummy_req.params.get('StackName'),
}
rpc.call(dummy_req.context, 'engine',
{'method': 'list_stack_resources',
'args': args}).AndReturn(engine_resp)
self.m.ReplayAll()
response = self.controller.list_stack_resources(dummy_req)
expected = {'ListStackResourcesResponse': {'ListStackResourcesResult':
{'StackResourceSummaries':
[{'ResourceStatus': u'CREATE_COMPLETE',
'ResourceType': u'AWS::EC2::Instance',
'ResourceStatusReason': None,
'LastUpdatedTimestamp': u'2012-07-23T13:06:00Z',
'PhysicalResourceId':
u'a3455d8c-9f88-404d-a85b-5315293e67de',
'LogicalResourceId': u'WikiDatabase'}]}}}
self.assert_(response == expected)
def setUp(self):
self.maxDiff = None
self.m = mox.Mox()
# Create WSGI controller instance
config.register_api_opts()
self.controller = stacks.StackController(cfg.CONF)
class DummyConfig():
bind_port = 8000
cfgopts = DummyConfig()
self.controller = stacks.StackController(options=cfgopts)
print "setup complete"
def tearDown(self):
self.m.UnsetStubs()
self.m.VerifyAll()
print "teardown complete"