diff options
Diffstat (limited to 'heatclient/tests')
-rw-r--r-- | heatclient/tests/functional/osc/v1/base.py | 4 | ||||
-rw-r--r-- | heatclient/tests/unit/osc/v1/test_event.py | 12 | ||||
-rw-r--r-- | heatclient/tests/unit/osc/v1/test_snapshot.py | 28 | ||||
-rw-r--r-- | heatclient/tests/unit/osc/v1/test_stack.py | 76 | ||||
-rw-r--r-- | heatclient/tests/unit/osc/v1/test_template.py | 29 | ||||
-rw-r--r-- | heatclient/tests/unit/test_common_http.py | 679 | ||||
-rw-r--r-- | heatclient/tests/unit/test_event_utils.py | 112 | ||||
-rw-r--r-- | heatclient/tests/unit/test_events.py | 124 | ||||
-rw-r--r-- | heatclient/tests/unit/test_resources.py | 12 | ||||
-rw-r--r-- | heatclient/tests/unit/test_shell.py | 159 | ||||
-rw-r--r-- | heatclient/tests/unit/test_template_utils.py | 508 |
11 files changed, 940 insertions, 803 deletions
diff --git a/heatclient/tests/functional/osc/v1/base.py b/heatclient/tests/functional/osc/v1/base.py index 31a9cd7..b17fb4c 100644 --- a/heatclient/tests/functional/osc/v1/base.py +++ b/heatclient/tests/functional/osc/v1/base.py @@ -48,7 +48,7 @@ class OpenStackClientTestBase(base.ClientTestBase): items = self.parser.listing(output) for item in items: obj[item['Field']] = six.text_type(item['Value']) - return dict((self._key_name(k), v) for k, v in six.iteritems(obj)) + return dict((self._key_name(k), v) for k, v in obj.items()) def _key_name(self, key): return key.lower().replace(' ', '_') @@ -60,7 +60,7 @@ class OpenStackClientTestBase(base.ClientTestBase): if item['ID'] == id: obj = item break - return dict((self._key_name(k), v) for k, v in six.iteritems(obj)) + return dict((self._key_name(k), v) for k, v in obj.items()) def _stack_create(self, name, template, parameters=[], wait=True): cmd = 'stack create ' + name diff --git a/heatclient/tests/unit/osc/v1/test_event.py b/heatclient/tests/unit/osc/v1/test_event.py index 3c64b92..d7ecb60 100644 --- a/heatclient/tests/unit/osc/v1/test_event.py +++ b/heatclient/tests/unit/osc/v1/test_event.py @@ -194,7 +194,9 @@ class TestEventList(TestEvent): mock_sort_items.assert_called_with(mock.ANY, "resource_name:desc") - self.event_client.list.assert_called_with(**self.defaults) + self.event_client.list.assert_called_with( + filters={}, resource_name=None, sort_dir='desc', + sort_keys=['resource_name'], stack_id='my_stack') self.assertEqual(self.fields, columns) @mock.patch('osc_lib.utils.sort_items') @@ -209,7 +211,9 @@ class TestEventList(TestEvent): mock_sort_items.assert_called_with(mock.ANY, "resource_name:desc,id:asc") - self.event_client.list.assert_called_with(**self.defaults) + self.event_client.list.assert_called_with( + filters={}, resource_name=None, sort_dir='desc', + sort_keys=['resource_name', 'id'], stack_id='my_stack') self.assertEqual(self.fields, columns) @mock.patch('osc_lib.utils.sort_items') @@ -223,7 +227,9 @@ class TestEventList(TestEvent): columns, data = self.cmd.take_action(parsed_args) mock_sort_items.assert_called_with(mock.ANY, "event_time:desc") - self.event_client.list.assert_called_with(**self.defaults) + self.event_client.list.assert_called_with( + filters={}, resource_name=None, sort_dir='desc', sort_keys=[], + stack_id='my_stack') self.assertEqual(self.fields, columns) @mock.patch('time.sleep') diff --git a/heatclient/tests/unit/osc/v1/test_snapshot.py b/heatclient/tests/unit/osc/v1/test_snapshot.py index f51a6ed..7f69c61 100644 --- a/heatclient/tests/unit/osc/v1/test_snapshot.py +++ b/heatclient/tests/unit/osc/v1/test_snapshot.py @@ -10,8 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. # - +import mock from osc_lib import exceptions as exc +import six from heatclient import exc as heat_exc from heatclient.osc.v1 import snapshot @@ -157,3 +158,28 @@ class TestSnapshotDelete(TestStack): exc.CommandError, self.cmd.take_action, parsed_args) + + @mock.patch('sys.stdin', spec=six.StringIO) + def test_snapshot_delete_prompt(self, mock_stdin): + arglist = ['my_stack', 'snapshot_id'] + mock_stdin.isatty.return_value = True + mock_stdin.readline.return_value = 'y' + parsed_args = self.check_parser(self.cmd, arglist, []) + + self.cmd.take_action(parsed_args) + + mock_stdin.readline.assert_called_with() + self.stack_client.snapshot_delete.assert_called_with('my_stack', + 'snapshot_id') + + @mock.patch('sys.stdin', spec=six.StringIO) + def test_snapshot_delete_prompt_no(self, mock_stdin): + arglist = ['my_stack', 'snapshot_id'] + mock_stdin.isatty.return_value = True + mock_stdin.readline.return_value = 'n' + parsed_args = self.check_parser(self.cmd, arglist, []) + + self.cmd.take_action(parsed_args) + + mock_stdin.readline.assert_called_with() + self.stack_client.snapshot_delete.assert_not_called() diff --git a/heatclient/tests/unit/osc/v1/test_stack.py b/heatclient/tests/unit/osc/v1/test_stack.py index c40bdf1..54fe9be 100644 --- a/heatclient/tests/unit/osc/v1/test_stack.py +++ b/heatclient/tests/unit/osc/v1/test_stack.py @@ -149,7 +149,8 @@ class TestStackCreate(TestStack): self.cmd.take_action(parsed_args) self.stack_client.create.assert_called_with(**self.defaults) - self.stack_client.get.assert_called_with(**{'stack_id': '1234'}) + self.stack_client.get.assert_called_with(**{'stack_id': '1234', + 'resolve_outputs': False}) @mock.patch('heatclient.common.event_utils.poll_for_events', return_value=('CREATE_FAILED', @@ -347,7 +348,8 @@ class TestStackUpdate(TestStack): self.cmd.take_action(parsed_args) self.stack_client.update.assert_called_with(**self.defaults) - self.stack_client.get.assert_called_with(**{'stack_id': 'my_stack'}) + self.stack_client.get.assert_called_with(**{'stack_id': 'my_stack', + 'resolve_outputs': False}) @mock.patch('heatclient.common.event_utils.poll_for_events', return_value=('UPDATE_FAILED', @@ -414,6 +416,16 @@ class TestStackShow(TestStack): self.cmd.take_action(parsed_args) self.stack_client.get.assert_called_with(**{ 'stack_id': 'my_stack', + 'resolve_outputs': True, + }) + + def test_stack_show_explicit_no_resolve(self): + arglist = ['--no-resolve-outputs', '--format', self.format, 'my_stack'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + self.stack_client.get.assert_called_with(**{ + 'stack_id': 'my_stack', + 'resolve_outputs': False, }) def test_stack_show_short(self): @@ -453,6 +465,9 @@ class TestStackList(TestStack): 'deletion_time': '2015-10-21T07:50:00Z', } + data_with_project = copy.deepcopy(data) + data_with_project['project'] = 'test_project' + def setUp(self): super(TestStackList, self).setUp() self.cmd = stack.ListStack(self.app, None) @@ -495,6 +510,8 @@ class TestStackList(TestStack): self.assertEqual(cols, columns) def test_stack_list_all_projects(self): + self.stack_client.list.return_value = [ + stacks.Stack(None, self.data_with_project)] kwargs = copy.deepcopy(self.defaults) kwargs['global_tenant'] = True cols = copy.deepcopy(self.columns) @@ -507,7 +524,23 @@ class TestStackList(TestStack): self.stack_client.list.assert_called_with(**kwargs) self.assertEqual(cols, columns) + def test_stack_list_with_project(self): + self.stack_client.list.return_value = [ + stacks.Stack(None, self.data_with_project)] + kwargs = copy.deepcopy(self.defaults) + cols = copy.deepcopy(self.columns) + cols.insert(2, 'Project') + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + + columns, data = self.cmd.take_action(parsed_args) + + self.stack_client.list.assert_called_with(**kwargs) + self.assertEqual(cols, columns) + def test_stack_list_long(self): + self.stack_client.list.return_value = [ + stacks.Stack(None, self.data_with_project)] kwargs = copy.deepcopy(self.defaults) kwargs['global_tenant'] = True cols = copy.deepcopy(self.columns) @@ -729,7 +762,8 @@ class TestStackAdopt(TestStack): self.cmd.take_action(parsed_args) self.stack_client.create.assert_called_with(**self.defaults) - self.stack_client.get.assert_called_with(**{'stack_id': '1234'}) + self.stack_client.get.assert_called_with(**{'stack_id': '1234', + 'resolve_outputs': False}) @mock.patch('heatclient.common.event_utils.poll_for_events', return_value=('ADOPT_FAILED', @@ -743,6 +777,42 @@ class TestStackAdopt(TestStack): self.assertRaises(exc.CommandError, self.cmd.take_action, parsed_args) +class TestStackExport(TestStack): + + columns = ['stack_name', 'stack_status', 'id'] + data = ['my_stack', 'ABANDONED', '1234'] + + response = dict(zip(columns, data)) + + def setUp(self): + super(TestStackExport, self).setUp() + self.cmd = stack.ExportStack(self.app, None) + self.stack_client.export.return_value = self.response + + def test_stack_export(self): + arglist = ['my_stack'] + parsed_args = self.check_parser(self.cmd, arglist, []) + + columns, data = self.cmd.take_action(parsed_args) + + for column in self.columns: + self.assertIn(column, columns) + for datum in self.data: + self.assertIn(datum, data) + + @mock.patch('heatclient.osc.v1.stack.open', create=True) + def test_stack_export_output_file(self, mock_open): + arglist = ['my_stack', '--output-file', 'file.json'] + mock_open.return_value = mock.MagicMock(spec=io.IOBase) + parsed_args = self.check_parser(self.cmd, arglist, []) + + columns, data = self.cmd.take_action(parsed_args) + + mock_open.assert_called_once_with('file.json', 'w') + self.assertEqual([], columns) + self.assertIsNone(data) + + class TestStackAbandon(TestStack): columns = ['stack_name', 'stack_status', 'id'] diff --git a/heatclient/tests/unit/osc/v1/test_template.py b/heatclient/tests/unit/osc/v1/test_template.py index c9b67ca..3a85c99 100644 --- a/heatclient/tests/unit/osc/v1/test_template.py +++ b/heatclient/tests/unit/osc/v1/test_template.py @@ -29,19 +29,17 @@ class TestTemplate(fakes.TestOrchestrationv1): class TestTemplateVersionList(TestTemplate): - defaults = [ - {'version': 'HOT123', 'type': 'hot'}, - {'version': 'CFN456', 'type': 'cfn'} - ] - - def setUp(self): - super(TestTemplateVersionList, self).setUp() - tv1 = template_versions.TemplateVersion(None, self.defaults[0]) - tv2 = template_versions.TemplateVersion(None, self.defaults[1]) + def _stub_versions_list(self, ret_data): + tv1 = template_versions.TemplateVersion(None, ret_data[0]) + tv2 = template_versions.TemplateVersion(None, ret_data[1]) self.template_versions.list.return_value = [tv1, tv2] self.cmd = template.VersionList(self.app, None) def test_version_list(self): + ret_data = [ + {'version': 'HOT123', 'type': 'hot'}, + {'version': 'CFN456', 'type': 'cfn'}] + self._stub_versions_list(ret_data) parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) @@ -49,6 +47,19 @@ class TestTemplateVersionList(TestTemplate): self.assertEqual(['Version', 'Type'], columns) self.assertEqual([('HOT123', 'hot'), ('CFN456', 'cfn')], list(data)) + def test_version_list_with_aliases(self): + ret_data = [ + {'version': 'HOT123', 'type': 'hot', 'aliases': ['releasex']}, + {'version': 'CFN456', 'type': 'cfn', 'aliases': ['releasey']}] + self._stub_versions_list(ret_data) + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(['Version', 'Type', 'Aliases'], columns) + self.assertEqual([('HOT123', 'hot', 'releasex'), + ('CFN456', 'cfn', 'releasey')], list(data)) + class TestTemplateFunctionList(TestTemplate): diff --git a/heatclient/tests/unit/test_common_http.py b/heatclient/tests/unit/test_common_http.py index 9ccc24a..8ce6f18 100644 --- a/heatclient/tests/unit/test_common_http.py +++ b/heatclient/tests/unit/test_common_http.py @@ -12,15 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging -import os import socket from keystoneauth1 import adapter import mock -import mox from oslo_serialization import jsonutils -import requests import six import testtools @@ -30,138 +26,108 @@ from heatclient import exc from heatclient.tests.unit import fakes +@mock.patch('heatclient.common.http.requests.request') class HttpClientTest(testtools.TestCase): - # Patch os.environ to avoid required auth info. - def setUp(self): - super(HttpClientTest, self).setUp() - self.m = mox.Mox() - self.m.StubOutWithMock(requests, 'request') - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_http_raw_request(self): + def test_http_raw_request(self, mock_request): headers = {'Content-Type': 'application/octet-stream', 'User-Agent': 'python-heatclient'} # Record a 200 - mock_conn = http.requests.request('GET', 'http://example.com:8004', - allow_redirects=False, - headers=headers) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/octet-stream'}, - '')) + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/octet-stream'}, + '') # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) self.assertEqual('', ''.join([x for x in resp.content])) + mock_request.assert_called_with('GET', 'http://example.com:8004', + allow_redirects=False, + headers=headers) - def test_token_or_credentials(self): + def test_token_or_credentials(self, mock_request): # Record a 200 fake200 = fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/octet-stream'}, '') + mock_request.return_value = fake200 # no token or credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake200) - - # credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Key': 'pass', - 'X-Auth-User': 'user'}) - mock_conn.AndReturn(fake200) - - # token suppresses credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Token': 'abcd1234'}) - mock_conn.AndReturn(fake200) - - # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - + # credentials client.username = 'user' client.password = 'pass' resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - + # token suppresses credentials client.auth_token = 'abcd1234' resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - - def test_include_pass(self): + mock_request.assert_has_calls([ + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient'}), + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient', + 'X-Auth-Key': 'pass', + 'X-Auth-User': 'user'}), + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient', + 'X-Auth-Token': 'abcd1234'}) + ]) + + def test_include_pass(self, mock_request): # Record a 200 fake200 = fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/octet-stream'}, '') - + mock_request.return_value = fake200 # no token or credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake200) - - # credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Key': 'pass', - 'X-Auth-User': 'user'}) - mock_conn.AndReturn(fake200) - - # token suppresses credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient', - 'X-Auth-Token': 'abcd1234', - 'X-Auth-Key': 'pass', - 'X-Auth-User': 'user'}) - mock_conn.AndReturn(fake200) - - # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - + # credentials client.username = 'user' client.password = 'pass' client.include_pass = True resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - + # token suppresses credentials client.auth_token = 'abcd1234' resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - - def test_not_include_pass(self): + mock_request.assert_has_calls([ + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient'}), + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient', + 'X-Auth-Key': 'pass', + 'X-Auth-User': 'user'}), + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient', + 'X-Auth-Token': 'abcd1234', + 'X-Auth-Key': 'pass', + 'X-Auth-User': 'user'}) + ]) + + def test_not_include_pass(self, mock_request): # Record a 200 fake500 = fakes.FakeHTTPResponse( 500, 'ERROR', @@ -169,21 +135,20 @@ class HttpClientTest(testtools.TestCase): '(HTTP 401)') # no token or credentials - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake500) + mock_request.return_value = fake500 # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') e = self.assertRaises(exc.HTTPUnauthorized, client.raw_request, 'GET', '') self.assertIn('Authentication failed', str(e)) + mock_request.assert_called_with( + 'GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/octet-stream', + 'User-Agent': 'python-heatclient'}) - def test_region_name(self): + def test_region_name(self, mock_request): # Record a 200 fake200 = fakes.FakeHTTPResponse( 200, 'OK', @@ -191,61 +156,46 @@ class HttpClientTest(testtools.TestCase): '') # Specify region name - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/octet-stream', - 'X-Region-Name': 'RegionOne', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn(fake200) + mock_request.return_value = fake200 # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') client.region_name = 'RegionOne' resp = client.raw_request('GET', '') self.assertEqual(200, resp.status_code) - - def test_http_json_request(self): - # Record a 200 - mock_conn = http.requests.request( + mock_request.assert_called_with( 'GET', 'http://example.com:8004', allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', + headers={'Content-Type': 'application/octet-stream', + 'X-Region-Name': 'RegionOne', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) + + def test_http_json_request(self, mock_request): + # Record a 200 + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/json'}, + '{}') # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp, body = client.json_request('GET', '') self.assertEqual(200, resp.status_code) self.assertEqual({}, body) - - def test_http_json_request_argument_passed_to_requests(self): - """Check that we have sent the proper arguments to requests.""" - # Record a 200 - mock_conn = http.requests.request( + mock_request.assert_called_with( 'GET', 'http://example.com:8004', allow_redirects=False, - cert=('RANDOM_CERT_FILE', 'RANDOM_KEY_FILE'), - verify=True, - data='"text"', headers={'Content-Type': 'application/json', 'Accept': 'application/json', - 'X-Auth-Url': 'http://AUTH_URL', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() + + def test_http_json_request_argument_passed_to_requests(self, mock_request): + """Check that we have sent the proper arguments to requests.""" + # Record a 200 + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/json'}, + '{}') + client = http.HTTPClient('http://example.com:8004') client.verify_cert = True client.cert_file = 'RANDOM_CERT_FILE' @@ -254,395 +204,360 @@ class HttpClientTest(testtools.TestCase): resp, body = client.json_request('GET', '', data='text') self.assertEqual(200, resp.status_code) self.assertEqual({}, body) - - def test_http_json_request_w_req_body(self): - # Record a 200 - mock_conn = http.requests.request( + mock_request.assert_called_with( 'GET', 'http://example.com:8004', - body='test-body', allow_redirects=False, + cert=('RANDOM_CERT_FILE', 'RANDOM_KEY_FILE'), + verify=True, + data='"text"', headers={'Content-Type': 'application/json', 'Accept': 'application/json', + 'X-Auth-Url': 'http://AUTH_URL', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) + + def test_http_json_request_w_req_body(self, mock_request): + # Record a 200 + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/json'}, + '{}') # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp, body = client.json_request('GET', '', body='test-body') self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - - def test_http_json_request_non_json_resp_cont_type(self): - # Record a 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', body='test-body', + mock_request.assert_called_with( + 'GET', 'http://example.com:8004', + body='test-body', allow_redirects=False, headers={'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'not/json'}, - {})) + + def test_http_json_request_non_json_resp_cont_type(self, mock_request): + # Record a 200i + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'not/json'}, + '{}') # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp, body = client.json_request('GET', '', body='test-body') self.assertEqual(200, resp.status_code) self.assertIsNone(body) - - def test_http_json_request_invalid_json(self): - # Record a 200 - mock_conn = http.requests.request( + mock_request.assert_called_with( 'GET', 'http://example.com:8004', + body='test-body', allow_redirects=False, headers={'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - 'invalid-json')) + + def test_http_json_request_invalid_json(self, mock_request): + # Record a 200 + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/json'}, + 'invalid-json') + # Replay, create client, assert - self.m.ReplayAll() client = http.HTTPClient('http://example.com:8004') resp, body = client.json_request('GET', '') self.assertEqual(200, resp.status_code) self.assertEqual('invalid-json', body) - - def test_http_manual_redirect_delete(self): - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo', + mock_request.assert_called_with( + 'GET', 'http://example.com:8004', allow_redirects=False, headers={'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + + def test_http_manual_redirect_delete(self, mock_request): + mock_request.side_effect = [ fakes.FakeHTTPResponse( 302, 'Found', {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + ''), fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() + 'invalid-json') + ] client = http.HTTPClient('http://example.com:8004/foo') resp, body = client.json_request('DELETE', '') - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_post(self): - mock_conn = http.requests.request( - 'POST', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + mock_request.assert_has_calls([ + mock.call('DELETE', 'http://example.com:8004/foo', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}), + mock.call('DELETE', 'http://example.com:8004/foo/bar', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}) + ]) + + def test_http_manual_redirect_post(self, mock_request): + mock_request.side_effect = [ fakes.FakeHTTPResponse( 302, 'Found', {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'POST', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + ''), fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() + 'invalid-json') + ] client = http.HTTPClient('http://example.com:8004/foo') resp, body = client.json_request('POST', '') - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_put(self): - mock_conn = http.requests.request( - 'PUT', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + mock_request.assert_has_calls([ + mock.call('POST', 'http://example.com:8004/foo', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}), + mock.call('POST', 'http://example.com:8004/foo/bar', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}) + ]) + + def test_http_manual_redirect_put(self, mock_request): + mock_request.side_effect = [ fakes.FakeHTTPResponse( 302, 'Found', {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'PUT', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + ''), fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() + 'invalid-json') + ] client = http.HTTPClient('http://example.com:8004/foo') resp, body = client.json_request('PUT', '') - self.assertEqual(200, resp.status_code) - - def test_http_manual_redirect_put_uppercase(self): - mock_conn = http.requests.request( - 'PUT', 'http://EXAMPLE.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + mock_request.assert_has_calls([ + mock.call('PUT', 'http://example.com:8004/foo', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}), + mock.call('PUT', 'http://example.com:8004/foo/bar', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}) + ]) + + def test_http_manual_redirect_put_uppercase(self, mock_request): + mock_request.side_effect = [ fakes.FakeHTTPResponse( 302, 'Found', {'location': 'http://example.com:8004/foo/bar'}, - '')) - mock_conn = http.requests.request( - 'PUT', 'http://example.com:8004/foo/bar', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + ''), fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/json'}, - '{}')) - - self.m.ReplayAll() - + 'invalid-json') + ] client = http.HTTPClient('http://EXAMPLE.com:8004/foo') resp, body = client.json_request('PUT', '') - self.assertEqual(200, resp.status_code) - def test_http_manual_redirect_error_without_location(self): - mock_conn = http.requests.request( - 'DELETE', 'http://example.com:8004/foo', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 302, 'Found', - {}, - '')) - self.m.ReplayAll() + mock_request.assert_has_calls([ + mock.call('PUT', 'http://EXAMPLE.com:8004/foo', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}), + mock.call('PUT', 'http://example.com:8004/foo/bar', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}) + ]) + + def test_http_manual_redirect_error_without_location(self, mock_request): + mock_request.return_value = fakes.FakeHTTPResponse( + 302, 'Found', + {}, + '') client = http.HTTPClient('http://example.com:8004/foo') self.assertRaises(exc.InvalidEndpoint, client.json_request, 'DELETE', '') - - def test_http_json_request_redirect(self): - # Record the 302 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', + mock_request.assert_called_once_with( + 'DELETE', 'http://example.com:8004/foo', allow_redirects=False, headers={'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + + def test_http_json_request_redirect(self, mock_request): + # Record the 302 + mock_request.side_effect = [ fakes.FakeHTTPResponse( 302, 'Found', {'location': 'http://example.com:8004'}, - '')) - # Record the following 200 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( + ''), fakes.FakeHTTPResponse( 200, 'OK', {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() + '{}') + ] client = http.HTTPClient('http://example.com:8004') resp, body = client.json_request('GET', '') self.assertEqual(200, resp.status_code) self.assertEqual({}, body) - - def test_http_404_json_request(self): + mock_request.assert_has_calls([ + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}), + mock.call('GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}) + ]) + + def test_http_404_json_request(self, mock_request): # Record a 404 - mock_conn = http.requests.request( - 'GET', 'http://example.com:8004', - allow_redirects=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 404, 'OK', {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() + mock_request.return_value = fakes.FakeHTTPResponse( + 404, 'OK', {'content-type': 'application/json'}, + '{}') client = http.HTTPClient('http://example.com:8004') e = self.assertRaises(exc.HTTPNotFound, client.json_request, 'GET', '') # Assert that the raised exception can be converted to string self.assertIsNotNone(str(e)) - - def test_http_300_json_request(self): - # Record a 300 - mock_conn = http.requests.request( + mock_request.assert_called_with( 'GET', 'http://example.com:8004', allow_redirects=False, headers={'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-heatclient'}) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 300, 'OK', {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() + + def test_http_300_json_request(self, mock_request): + # Record a 300 + mock_request.return_value = fakes.FakeHTTPResponse( + 300, 'OK', {'content-type': 'application/json'}, + '{}') + # Assert that the raised exception can be converted to string client = http.HTTPClient('http://example.com:8004') e = self.assertRaises( exc.HTTPMultipleChoices, client.json_request, 'GET', '') - # Assert that the raised exception can be converted to string self.assertIsNotNone(str(e)) + mock_request.assert_called_with( + 'GET', 'http://example.com:8004', + allow_redirects=False, + headers={'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'python-heatclient'}) - def test_fake_json_request(self): + def test_fake_json_request(self, mock_request): headers = {'User-Agent': 'python-heatclient'} - mock_conn = http.requests.request('GET', 'fake://example.com:8004/', - allow_redirects=False, - headers=headers) - mock_conn.AndRaise(socket.gaierror) - self.m.ReplayAll() - + mock_request.side_effect = [socket.gaierror] client = http.HTTPClient('fake://example.com:8004') self.assertRaises(exc.InvalidEndpoint, client._http_request, "/", "GET") - def test_debug_curl_command(self): - self.m.StubOutWithMock(logging.Logger, 'debug') - - ssl_connection_params = {'ca_file': 'TEST_CA', - 'cert_file': 'TEST_CERT', - 'key_file': 'TEST_KEY', - 'insecure': 'TEST_NSA'} - - headers = {'key': 'value'} - - mock_logging_debug = logging.Logger.debug( - "curl -g -i -X GET -H 'key: value' --key TEST_KEY " - "--cert TEST_CERT --cacert TEST_CA " - "-k -d 'text' http://foo/bar" - ) - mock_logging_debug.AndReturn(None) - self.m.ReplayAll() - - client = http.HTTPClient('http://foo') - client.ssl_connection_params = ssl_connection_params - client.log_curl_request('GET', '/bar', {'headers': headers, - 'data': 'text'}) - - def test_http_request_socket_error(self): + mock_request.assert_called_with('GET', 'fake://example.com:8004/', + allow_redirects=False, + headers=headers) + + def test_debug_curl_command(self, mock_request): + with mock.patch('logging.Logger.debug') as mock_logging_debug: + + ssl_connection_params = {'ca_file': 'TEST_CA', + 'cert_file': 'TEST_CERT', + 'key_file': 'TEST_KEY', + 'insecure': 'TEST_NSA'} + + headers = {'key': 'value'} + mock_logging_debug.return_value = None + client = http.HTTPClient('http://foo') + client.ssl_connection_params = ssl_connection_params + client.log_curl_request('GET', '/bar', {'headers': headers, + 'data': 'text'}) + mock_logging_debug.assert_called_with( + "curl -g -i -X GET -H 'key: value' --key TEST_KEY " + "--cert TEST_CERT --cacert TEST_CA " + "-k -d 'text' http://foo/bar" + ) + + def test_http_request_socket_error(self, mock_request): headers = {'User-Agent': 'python-heatclient'} - mock_conn = http.requests.request('GET', 'http://example.com:8004/', - allow_redirects=False, - headers=headers) - mock_conn.AndRaise(socket.error) - self.m.ReplayAll() + mock_request.side_effect = [socket.error] client = http.HTTPClient('http://example.com:8004') self.assertRaises(exc.CommunicationError, client._http_request, "/", "GET") + mock_request.assert_called_with('GET', 'http://example.com:8004/', + allow_redirects=False, + headers=headers) - def test_http_request_socket_timeout(self): + def test_http_request_socket_timeout(self, mock_request): headers = {'User-Agent': 'python-heatclient'} - mock_conn = http.requests.request('GET', 'http://example.com:8004/', - allow_redirects=False, - headers=headers) - mock_conn.AndRaise(socket.timeout) - self.m.ReplayAll() + mock_request.side_effect = [socket.timeout] client = http.HTTPClient('http://example.com:8004') self.assertRaises(exc.CommunicationError, client._http_request, "/", "GET") + mock_request.assert_called_with('GET', 'http://example.com:8004/', + allow_redirects=False, + headers=headers) - def test_http_request_specify_timeout(self): - mock_conn = http.requests.request( + def test_http_request_specify_timeout(self, mock_request): + mock_request.return_value = fakes.FakeHTTPResponse( + 200, 'OK', + {'content-type': 'application/json'}, + '{}') + client = http.HTTPClient('http://example.com:8004', timeout='123') + resp, body = client.json_request('GET', '') + self.assertEqual(200, resp.status_code) + mock_request.assert_called_with( 'GET', 'http://example.com:8004', allow_redirects=False, headers={'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-heatclient'}, timeout=float(123)) - mock_conn.AndReturn( - fakes.FakeHTTPResponse( - 200, 'OK', - {'content-type': 'application/json'}, - '{}')) - # Replay, create client, assert - self.m.ReplayAll() - client = http.HTTPClient('http://example.com:8004', timeout='123') - resp, body = client.json_request('GET', '') - self.assertEqual(200, resp.status_code) - self.assertEqual({}, body) - def test_get_system_ca_file(self): + def test_get_system_ca_file(self, mock_request): chosen = '/etc/ssl/certs/ca-certificates.crt' - self.m.StubOutWithMock(os.path, 'exists') - os.path.exists(chosen).AndReturn(chosen) - self.m.ReplayAll() - - ca = http.get_system_ca_file() - self.assertEqual(chosen, ca) + with mock.patch('os.path.exists') as mock_os: + mock_os.return_value = chosen + ca = http.get_system_ca_file() + self.assertEqual(chosen, ca) + mock_os.assert_called_once_with(chosen) - def test_insecure_verify_cert_None(self): + def test_insecure_verify_cert_None(self, mock_request): client = http.HTTPClient('https://foo', insecure=True) self.assertFalse(client.verify_cert) - def test_passed_cert_to_verify_cert(self): + def test_passed_cert_to_verify_cert(self, mock_request): client = http.HTTPClient('https://foo', ca_file="NOWHERE") self.assertEqual("NOWHERE", client.verify_cert) - self.m.StubOutWithMock(http, 'get_system_ca_file') - http.get_system_ca_file().AndReturn("SOMEWHERE") - self.m.ReplayAll() - client = http.HTTPClient('https://foo') - self.assertEqual("SOMEWHERE", client.verify_cert) + with mock.patch('heatclient.common.http.get_system_ca_file') as gsf: + gsf.return_value = "SOMEWHERE" + client = http.HTTPClient('https://foo') + self.assertEqual("SOMEWHERE", client.verify_cert) - def test_curl_log_i18n_headers(self): - self.m.StubOutWithMock(logging.Logger, 'debug') + @mock.patch('logging.Logger.debug', return_value=None) + def test_curl_log_i18n_headers(self, mock_log, mock_request): kwargs = {'headers': {'Key': b'foo\xe3\x8a\x8e'}} - mock_logging_debug = logging.Logger.debug( - u"curl -g -i -X GET -H 'Key: foo㊎' http://somewhere" - ) - mock_logging_debug.AndReturn(None) - - self.m.ReplayAll() - client = http.HTTPClient('http://somewhere') client.log_curl_request("GET", '', kwargs=kwargs) + mock_log.assert_called_once_with( + u"curl -g -i -X GET -H 'Key: foo㊎' http://somewhere") class SessionClientTest(testtools.TestCase): diff --git a/heatclient/tests/unit/test_event_utils.py b/heatclient/tests/unit/test_event_utils.py index 32e4473..b080e00 100644 --- a/heatclient/tests/unit/test_event_utils.py +++ b/heatclient/tests/unit/test_event_utils.py @@ -18,6 +18,15 @@ from heatclient.v1 import events as hc_ev from heatclient.v1 import resources as hc_res +class FakeWebSocket(object): + + def __init__(self, events): + self.events = events + + def recv(self): + return self.events.pop(0) + + class ShellTestEventUtils(testtools.TestCase): @staticmethod def _mock_resource(resource_id, nested_id=None): @@ -38,7 +47,9 @@ class ShellTestEventUtils(testtools.TestCase): @staticmethod def _mock_event(event_id, resource_id, resource_status='CREATE_COMPLETE'): - ev_info = {"links": [{"href": "http://heat/foo", "rel": "self"}], + ev_info = {"links": [ + {"href": "http://heat/foo", "rel": "self"}, + {"href": "http://heat/stacks/astack", "rel": "stack"}], "logical_resource_id": resource_id, "physical_resource_id": resource_id, "resource_name": resource_id, @@ -48,6 +59,23 @@ class ShellTestEventUtils(testtools.TestCase): "id": event_id} return hc_ev.Event(manager=None, info=ev_info) + @staticmethod + def _mock_stack_event(event_id, stack_name, + stack_status='CREATE_COMPLETE'): + stack_id = 'abcdef' + ev_info = {"links": [{"href": "http://heat/foo", "rel": "self"}, + {"href": "http://heat/stacks/%s/%s" % (stack_name, + stack_id), + "rel": "stack"}], + "logical_resource_id": stack_name, + "physical_resource_id": stack_id, + "resource_name": stack_name, + "resource_status": stack_status, + "resource_status_reason": "state changed", + "event_time": "2014-12-05T14:14:30Z", + "id": event_id} + return hc_ev.Event(manager=None, info=ev_info) + def test_get_nested_ids(self): def list_stub(stack_id): return [self._mock_resource('aresource', 'foo3/3id')] @@ -134,7 +162,7 @@ class ShellTestEventUtils(testtools.TestCase): @mock.patch('heatclient.common.event_utils.get_events') def test_poll_for_events(self, ge): ge.side_effect = [[ - self._mock_event('1', 'astack', 'CREATE_IN_PROGRESS'), + self._mock_stack_event('1', 'astack', 'CREATE_IN_PROGRESS'), self._mock_event('2', 'res_child1', 'CREATE_IN_PROGRESS'), self._mock_event('3', 'res_child2', 'CREATE_IN_PROGRESS'), self._mock_event('4', 'res_child3', 'CREATE_IN_PROGRESS') @@ -142,7 +170,7 @@ class ShellTestEventUtils(testtools.TestCase): self._mock_event('5', 'res_child1', 'CREATE_COMPLETE'), self._mock_event('6', 'res_child2', 'CREATE_COMPLETE'), self._mock_event('7', 'res_child3', 'CREATE_COMPLETE'), - self._mock_event('8', 'astack', 'CREATE_COMPLETE') + self._mock_stack_event('8', 'astack', 'CREATE_COMPLETE') ]] stack_status, msg = event_utils.poll_for_events( @@ -159,12 +187,41 @@ class ShellTestEventUtils(testtools.TestCase): ]) @mock.patch('heatclient.common.event_utils.get_events') + def test_poll_for_events_same_name(self, ge): + ge.side_effect = [[ + self._mock_stack_event('1', 'mything', 'CREATE_IN_PROGRESS'), + self._mock_event('2', 'res_child1', 'CREATE_IN_PROGRESS'), + self._mock_event('3', 'mything', 'CREATE_IN_PROGRESS'), + ], [ + self._mock_event('4', 'mything', 'CREATE_COMPLETE'), + ], [ + self._mock_event('5', 'res_child1', 'CREATE_COMPLETE'), + self._mock_stack_event('6', 'mything', 'CREATE_COMPLETE'), + ]] + + stack_status, msg = event_utils.poll_for_events( + None, 'mything', action='CREATE', poll_period=0) + self.assertEqual('CREATE_COMPLETE', stack_status) + self.assertEqual('\n Stack mything CREATE_COMPLETE \n', msg) + ge.assert_has_calls([ + mock.call(None, stack_id='mything', nested_depth=0, event_args={ + 'sort_dir': 'asc', 'marker': None + }), + mock.call(None, stack_id='mything', nested_depth=0, event_args={ + 'sort_dir': 'asc', 'marker': '3' + }), + mock.call(None, stack_id='mything', nested_depth=0, event_args={ + 'sort_dir': 'asc', 'marker': '4' + }) + ]) + + @mock.patch('heatclient.common.event_utils.get_events') def test_poll_for_events_with_marker(self, ge): ge.side_effect = [[ self._mock_event('5', 'res_child1', 'CREATE_COMPLETE'), self._mock_event('6', 'res_child2', 'CREATE_COMPLETE'), self._mock_event('7', 'res_child3', 'CREATE_COMPLETE'), - self._mock_event('8', 'astack', 'CREATE_COMPLETE') + self._mock_stack_event('8', 'astack', 'CREATE_COMPLETE') ]] stack_status, msg = event_utils.poll_for_events( @@ -181,9 +238,9 @@ class ShellTestEventUtils(testtools.TestCase): @mock.patch('heatclient.common.event_utils.get_events') def test_poll_for_events_in_progress_resource(self, ge): ge.side_effect = [[ - self._mock_event('1', 'astack', 'CREATE_IN_PROGRESS'), + self._mock_stack_event('1', 'astack', 'CREATE_IN_PROGRESS'), self._mock_event('2', 'res_child1', 'CREATE_IN_PROGRESS'), - self._mock_event('3', 'astack', 'CREATE_COMPLETE') + self._mock_stack_event('3', 'astack', 'CREATE_COMPLETE') ]] stack_status, msg = event_utils.poll_for_events( @@ -194,7 +251,7 @@ class ShellTestEventUtils(testtools.TestCase): @mock.patch('heatclient.common.event_utils.get_events') def test_poll_for_events_failed(self, ge): ge.side_effect = [[ - self._mock_event('1', 'astack', 'CREATE_IN_PROGRESS'), + self._mock_stack_event('1', 'astack', 'CREATE_IN_PROGRESS'), self._mock_event('2', 'res_child1', 'CREATE_IN_PROGRESS'), self._mock_event('3', 'res_child2', 'CREATE_IN_PROGRESS'), self._mock_event('4', 'res_child3', 'CREATE_IN_PROGRESS') @@ -202,7 +259,7 @@ class ShellTestEventUtils(testtools.TestCase): self._mock_event('5', 'res_child1', 'CREATE_COMPLETE'), self._mock_event('6', 'res_child2', 'CREATE_FAILED'), self._mock_event('7', 'res_child3', 'CREATE_COMPLETE'), - self._mock_event('8', 'astack', 'CREATE_FAILED') + self._mock_stack_event('8', 'astack', 'CREATE_FAILED') ]] stack_status, msg = event_utils.poll_for_events( @@ -213,7 +270,7 @@ class ShellTestEventUtils(testtools.TestCase): @mock.patch('heatclient.common.event_utils.get_events') def test_poll_for_events_no_action(self, ge): ge.side_effect = [[ - self._mock_event('1', 'astack', 'CREATE_IN_PROGRESS'), + self._mock_stack_event('1', 'astack', 'CREATE_IN_PROGRESS'), self._mock_event('2', 'res_child1', 'CREATE_IN_PROGRESS'), self._mock_event('3', 'res_child2', 'CREATE_IN_PROGRESS'), self._mock_event('4', 'res_child3', 'CREATE_IN_PROGRESS') @@ -221,7 +278,7 @@ class ShellTestEventUtils(testtools.TestCase): self._mock_event('5', 'res_child1', 'CREATE_COMPLETE'), self._mock_event('6', 'res_child2', 'CREATE_FAILED'), self._mock_event('7', 'res_child3', 'CREATE_COMPLETE'), - self._mock_event('8', 'astack', 'FOO_FAILED') + self._mock_stack_event('8', 'astack', 'FOO_FAILED') ]] stack_status, msg = event_utils.poll_for_events( @@ -235,7 +292,7 @@ class ShellTestEventUtils(testtools.TestCase): mock_client.stacks.get.return_value.stack_status = 'CREATE_FAILED' ge.side_effect = [[ - self._mock_event('1', 'astack', 'CREATE_IN_PROGRESS'), + self._mock_stack_event('1', 'astack', 'CREATE_IN_PROGRESS'), self._mock_event('2', 'res_child1', 'CREATE_IN_PROGRESS'), self._mock_event('3', 'res_child2', 'CREATE_IN_PROGRESS'), self._mock_event('4', 'res_child3', 'CREATE_IN_PROGRESS') @@ -245,3 +302,36 @@ class ShellTestEventUtils(testtools.TestCase): mock_client, 'astack', action='CREATE', poll_period=0) self.assertEqual('CREATE_FAILED', stack_status) self.assertEqual('\n Stack astack CREATE_FAILED \n', msg) + + def test_wait_for_events(self): + ws = FakeWebSocket([ + {'body': { + 'timestamp': '2014-01-06T16:14:26Z', + 'payload': {'resource_action': 'CREATE', + 'resource_status': 'COMPLETE', + 'resource_name': 'mystack', + 'physical_resource_id': 'stackid1', + 'stack_id': 'stackid1'}}}]) + stack_status, msg = event_utils.wait_for_events(ws, 'mystack') + self.assertEqual('CREATE_COMPLETE', stack_status) + self.assertEqual('\n Stack mystack CREATE_COMPLETE \n', msg) + + def test_wait_for_events_failed(self): + ws = FakeWebSocket([ + {'body': { + 'timestamp': '2014-01-06T16:14:23Z', + 'payload': {'resource_action': 'CREATE', + 'resource_status': 'IN_PROGRESS', + 'resource_name': 'mystack', + 'physical_resource_id': 'stackid1', + 'stack_id': 'stackid1'}}}, + {'body': { + 'timestamp': '2014-01-06T16:14:26Z', + 'payload': {'resource_action': 'CREATE', + 'resource_status': 'FAILED', + 'resource_name': 'mystack', + 'physical_resource_id': 'stackid1', + 'stack_id': 'stackid1'}}}]) + stack_status, msg = event_utils.wait_for_events(ws, 'mystack') + self.assertEqual('CREATE_FAILED', stack_status) + self.assertEqual('\n Stack mystack CREATE_FAILED \n', msg) diff --git a/heatclient/tests/unit/test_events.py b/heatclient/tests/unit/test_events.py index f6071a5..b63aebe 100644 --- a/heatclient/tests/unit/test_events.py +++ b/heatclient/tests/unit/test_events.py @@ -13,7 +13,6 @@ # under the License. import mock -import mox import testtools from heatclient.common import utils @@ -22,39 +21,39 @@ from heatclient.v1 import events class EventManagerTest(testtools.TestCase): - def setUp(self): - super(EventManagerTest, self).setUp() - self.m = mox.Mox() - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - def test_list_event(self): stack_id = 'teststack', resource_name = 'testresource' manager = events.EventManager(None) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - manager._resolve_stack_id(stack_id).AndReturn('teststack/abcd1234') - self.m.ReplayAll() - manager._list = mock.MagicMock() - manager.list(stack_id, resource_name) - # Make sure url is correct. - manager._list.assert_called_once_with('/stacks/teststack%2Fabcd1234/' - 'resources/testresource/events', - "events") + with mock.patch('heatclient.v1.events.EventManager._resolve_stack_id')\ + as mock_re: + mock_re.return_value = 'teststack/abcd1234' + + manager._list = mock.MagicMock() + manager.list(stack_id, resource_name) + # Make sure url is correct. + manager._list.assert_called_once_with( + '/stacks/teststack/abcd1234/' + 'resources/testresource/events', + "events") + mock_re.assert_called_once_with(stack_id) def test_list_event_with_unicode_resource_name(self): stack_id = 'teststack', resource_name = u'\u5de5\u4f5c' manager = events.EventManager(None) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - manager._resolve_stack_id(stack_id).AndReturn('teststack/abcd1234') - self.m.ReplayAll() - manager._list = mock.MagicMock() - manager.list(stack_id, resource_name) - # Make sure url is correct. - manager._list.assert_called_once_with('/stacks/teststack%2Fabcd1234/' - 'resources/%E5%B7%A5%E4%BD%9C/' - 'events', "events") + with mock.patch('heatclient.v1.events.EventManager._resolve_stack_id')\ + as mock_re: + mock_re.return_value = 'teststack/abcd1234' + + manager._list = mock.MagicMock() + manager.list(stack_id, resource_name) + # Make sure url is correct. + manager._list.assert_called_once_with( + '/stacks/teststack/abcd1234/' + 'resources/%E5%B7%A5%E4%BD%9C/' + 'events', "events") + mock_re.assert_called_once_with(stack_id) def test_list_event_with_none_resource_name(self): stack_id = 'teststack', @@ -75,28 +74,33 @@ class EventManagerTest(testtools.TestCase): 'resource_status': 'COMPLETE' }} manager = events.EventManager(None) - self.m.StubOutWithMock(manager, '_resolve_stack_id') - manager._resolve_stack_id(stack_id).AndReturn('teststack/abcd1234') - self.m.ReplayAll() - manager._list = mock.MagicMock() - manager.list(stack_id, resource_name, **kwargs) - # Make sure url is correct. - self.assertEqual(1, manager._list.call_count) - args = manager._list.call_args - self.assertEqual(2, len(args[0])) - url, param = args[0] - self.assertEqual("events", param) - base_url, query_params = utils.parse_query_url(url) - expected_base_url = ('/stacks/teststack%2Fabcd1234/' - 'resources/testresource/events') - self.assertEqual(expected_base_url, base_url) - expected_query_dict = {'marker': ['6d6935f4-0ae5'], - 'limit': ['2'], - 'resource_action': ['CREATE'], - 'resource_status': ['COMPLETE']} - self.assertEqual(expected_query_dict, query_params) - - def test_get_event(self): + manager = events.EventManager(None) + with mock.patch('heatclient.v1.events.EventManager._resolve_stack_id')\ + as mock_re: + mock_re.return_value = 'teststack/abcd1234' + + manager._list = mock.MagicMock() + manager.list(stack_id, resource_name, **kwargs) + # Make sure url is correct. + self.assertEqual(1, manager._list.call_count) + args = manager._list.call_args + self.assertEqual(2, len(args[0])) + url, param = args[0] + self.assertEqual("events", param) + base_url, query_params = utils.parse_query_url(url) + expected_base_url = ('/stacks/teststack/abcd1234/' + 'resources/testresource/events') + self.assertEqual(expected_base_url, base_url) + expected_query_dict = {'marker': ['6d6935f4-0ae5'], + 'limit': ['2'], + 'resource_action': ['CREATE'], + 'resource_status': ['COMPLETE']} + self.assertEqual(expected_query_dict, query_params) + mock_re.assert_called_once_with(stack_id) + + @mock.patch('heatclient.v1.events.EventManager._resolve_stack_id') + @mock.patch('heatclient.common.utils.get_response_body') + def test_get_event(self, mock_utils, mock_re): fields = {'stack_id': 'teststack', 'resource_name': 'testresource', 'event_id': '1'} @@ -106,7 +110,7 @@ class EventManagerTest(testtools.TestCase): def json_request(self, *args, **kwargs): expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/testresource/events/1') assert args == expect return {}, {'event': []} @@ -116,15 +120,14 @@ class EventManagerTest(testtools.TestCase): manager = events.EventManager(FakeAPI()) with mock.patch('heatclient.v1.events.Event'): - self.m.StubOutWithMock(manager, '_resolve_stack_id') - self.m.StubOutWithMock(utils, 'get_response_body') - utils.get_response_body(mox.IgnoreArg()).AndReturn({'event': []}) - manager._resolve_stack_id('teststack').AndReturn( - 'teststack/abcd1234') - self.m.ReplayAll() + mock_utils.return_value = {'event': []} + mock_re.return_value = 'teststack/abcd1234' manager.get(**fields) + mock_re.assert_called_once_with('teststack') - def test_get_event_with_unicode_resource_name(self): + @mock.patch('heatclient.v1.events.EventManager._resolve_stack_id') + @mock.patch('heatclient.common.utils.get_response_body') + def test_get_event_with_unicode_resource_name(self, mock_utils, mock_re): fields = {'stack_id': 'teststack', 'resource_name': u'\u5de5\u4f5c', 'event_id': '1'} @@ -134,7 +137,7 @@ class EventManagerTest(testtools.TestCase): def json_request(self, *args, **kwargs): expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/%E5%B7%A5%E4%BD%9C/events/1') assert args == expect return {}, {'event': []} @@ -144,10 +147,7 @@ class EventManagerTest(testtools.TestCase): manager = events.EventManager(FakeAPI()) with mock.patch('heatclient.v1.events.Event'): - self.m.StubOutWithMock(manager, '_resolve_stack_id') - self.m.StubOutWithMock(utils, 'get_response_body') - utils.get_response_body(mox.IgnoreArg()).AndReturn({'event': []}) - manager._resolve_stack_id('teststack').AndReturn( - 'teststack/abcd1234') - self.m.ReplayAll() + mock_utils.return_value = {'event': []} + mock_re.return_value = 'teststack/abcd1234' manager.get(**fields) + mock_re.assert_called_once_with('teststack') diff --git a/heatclient/tests/unit/test_resources.py b/heatclient/tests/unit/test_resources.py index b3c5c30..96e8203 100644 --- a/heatclient/tests/unit/test_resources.py +++ b/heatclient/tests/unit/test_resources.py @@ -73,7 +73,7 @@ class ResourceManagerTest(testtools.TestCase): fields = {'stack_id': 'teststack', 'resource_name': 'testresource'} expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/testresource') key = 'resource' @@ -86,7 +86,7 @@ class ResourceManagerTest(testtools.TestCase): 'resource_name': 'testresource', 'with_attr': ['attr_a', 'attr_b']} expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/testresource?with_attr=attr_a&with_attr=attr_b') key = 'resource' @@ -98,7 +98,7 @@ class ResourceManagerTest(testtools.TestCase): fields = {'stack_id': 'teststack', 'resource_name': u'\u5de5\u4f5c'} expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/%E5%B7%A5%E4%BD%9C') key = 'resource' @@ -154,7 +154,7 @@ class ResourceManagerTest(testtools.TestCase): fields = {'stack_id': 'teststack', 'resource_name': 'testresource'} expect = ('GET', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/testresource/metadata') key = 'metadata' @@ -192,7 +192,7 @@ class ResourceManagerTest(testtools.TestCase): 'resource_name': 'testresource', 'data': 'Some content'} expect = ('POST', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/testresource/signal') key = 'signal' @@ -206,7 +206,7 @@ class ResourceManagerTest(testtools.TestCase): 'mark_unhealthy': 'True', 'resource_status_reason': 'Anything'} expect = ('PATCH', - '/stacks/teststack%2Fabcd1234/resources' + '/stacks/teststack/abcd1234/resources' '/testresource') key = 'mark_unhealthy' diff --git a/heatclient/tests/unit/test_shell.py b/heatclient/tests/unit/test_shell.py index 383ee2c..b9ec014 100644 --- a/heatclient/tests/unit/test_shell.py +++ b/heatclient/tests/unit/test_shell.py @@ -32,6 +32,7 @@ import testscenarios import testtools import yaml +from heatclient._i18n import _ from heatclient.common import http from heatclient.common import utils from heatclient import exc @@ -126,25 +127,25 @@ class TestCase(testtools.TestCase): mockfixture = self.useFixture(mockpatch.Patch(target, **kwargs)) return mockfixture.mock - def stack_list_resp_dict(self, show_nested=False): - resp_dict = {"stacks": [ - { - "id": "1", - "stack_name": "teststack", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'CREATE_COMPLETE', - "creation_time": "2012-10-25T01:58:47Z" - }, - { - "id": "2", - "stack_name": "teststack2", - "stack_owner": "testowner", - "project": "testproject", - "stack_status": 'IN_PROGRESS', - "creation_time": "2012-10-25T01:58:47Z" - }] - } + def stack_list_resp_dict(self, show_nested=False, include_project=False): + stack1 = { + "id": "1", + "stack_name": "teststack", + "stack_owner": "testowner", + "stack_status": 'CREATE_COMPLETE', + "creation_time": "2012-10-25T01:58:47Z"} + stack2 = { + "id": "2", + "stack_name": "teststack2", + "stack_owner": "testowner", + "stack_status": 'IN_PROGRESS', + "creation_time": "2012-10-25T01:58:47Z" + } + if include_project: + stack1['project'] = 'testproject' + stack1['project'] = 'testproject' + + resp_dict = {"stacks": [stack1, stack2]} if show_nested: nested = { "id": "3", @@ -153,6 +154,8 @@ class TestCase(testtools.TestCase): "creation_time": "2012-10-25T01:58:47Z", "parent": "theparentof3" } + if include_project: + nested['project'] = 'testproject' resp_dict["stacks"].append(nested) return resp_dict @@ -211,7 +214,7 @@ class TestCase(testtools.TestCase): {"href": "http://heat.example.com:8004/foo3", "rel": "stack"}], "logical_resource_id": "aResource", - "physical_resource_id": None, + "physical_resource_id": 'foo3', "resource_name": stack_name, "resource_status": "%s_IN_PROGRESS" % action, "resource_status_reason": "state changed"}) @@ -225,7 +228,7 @@ class TestCase(testtools.TestCase): {"href": "http://heat.example.com:8004/foo3", "rel": "stack"}], "logical_resource_id": "aResource", - "physical_resource_id": None, + "physical_resource_id": 'foo3', "resource_name": stack_name, "resource_status": "%s_%s" % (action, final_state), "resource_status_reason": "state changed"}) @@ -561,7 +564,7 @@ class ShellTestNoMox(TestCase): rsrc_eventid2=eventid2 ) - self.requests.get('http://heat.example.com/stacks/myStack%2F60f83b5e/' + self.requests.get('http://heat.example.com/stacks/myStack/60f83b5e/' 'resources/myDeployment/events', headers={'Content-Type': 'application/json'}, json=resp_dict) @@ -618,7 +621,8 @@ class ShellTestEndpointType(TestCase): 'region_name': '', 'username': 'username', 'password': 'password', - 'include_pass': False + 'include_pass': False, + 'endpoint_override': mox.IgnoreArg(), } http._construct_http_client(**kwargs) heatclient.v1.shell.do_stack_list(mox.IgnoreArg(), mox.IgnoreArg()) @@ -637,7 +641,8 @@ class ShellTestEndpointType(TestCase): 'region_name': '', 'username': 'username', 'password': 'password', - 'include_pass': False + 'include_pass': False, + 'endpoint_override': mox.IgnoreArg(), } http._construct_http_client(**kwargs) heatclient.v1.shell.do_stack_list(mox.IgnoreArg(), mox.IgnoreArg()) @@ -658,7 +663,8 @@ class ShellTestEndpointType(TestCase): 'region_name': '', 'username': 'username', 'password': 'password', - 'include_pass': False + 'include_pass': False, + 'endpoint_override': mox.IgnoreArg(), } http._construct_http_client(**kwargs) heatclient.v1.shell.do_stack_list(mox.IgnoreArg(), mox.IgnoreArg()) @@ -2220,6 +2226,65 @@ class ShellTestUserPass(ShellBase): resp = self.shell('snapshot-show teststack/1 2') self.assertEqual(resp_dict, jsonutils.loads(resp)) + # the main thing this @mock.patch is doing here is keeping + # sys.stdin untouched for later tests + @mock.patch('sys.stdin', new_callable=six.StringIO) + def test_snapshot_delete_prompt_with_tty(self, ms): + self.register_keystone_auth_fixture() + resp_dict = {"snapshot": { + "id": "2", + "creation_time": "2012-10-25T01:58:47Z" + }} + + mock_stdin = mock.Mock() + mock_stdin.isatty = mock.Mock() + mock_stdin.isatty.return_value = True + mock_stdin.readline = mock.Mock() + mock_stdin.readline.return_value = 'n' + sys.stdin = mock_stdin + + self.mock_request_delete('/stacks/teststack/1/snapshots/2', resp_dict) + + self.m.ReplayAll() + + resp = self.shell('snapshot-delete teststack/1 2') + resp_text = ('Are you sure you want to delete the snapshot of ' + 'this stack [Y/N]?') + self.assertEqual(resp_text, resp) + self.m.ReplayAll() + + mock_stdin.readline.return_value = 'Y' + resp = self.shell('snapshot-delete teststack/1 2') + msg = _("Request to delete the snapshot 2 of the stack " + "teststack/1 has been accepted.") + self.assertRegex(resp, msg) + + # the main thing this @mock.patch is doing here is keeping + # sys.stdin untouched for later tests + @mock.patch('sys.stdin', new_callable=six.StringIO) + def test_snapshot_delete_prompt_with_tty_y(self, ms): + self.register_keystone_auth_fixture() + resp_dict = {"snapshot": { + "id": "2", + "creation_time": "2012-10-25T01:58:47Z" + }} + + mock_stdin = mock.Mock() + mock_stdin.isatty = mock.Mock() + mock_stdin.isatty.return_value = True + mock_stdin.readline = mock.Mock() + mock_stdin.readline.return_value = '' + sys.stdin = mock_stdin + + self.mock_request_delete('/stacks/teststack/1/snapshots/2', resp_dict) + + self.m.ReplayAll() + # -y from the shell should skip the n/y prompt + resp = self.shell('snapshot-delete -y teststack/1 2') + msg = _("Request to delete the snapshot 2 of the stack " + "teststack/1 has been accepted.") + self.assertRegex(resp, msg) + def test_snapshot_delete(self): self.register_keystone_auth_fixture() @@ -2231,7 +2296,9 @@ class ShellTestUserPass(ShellBase): self.m.ReplayAll() resp = self.shell('snapshot-delete teststack/1 2') - self.assertEqual("", resp) + msg = _("Request to delete the snapshot 2 of the stack " + "teststack/1 has been accepted.") + self.assertRegex(resp, msg) def test_stack_restore(self): self.register_keystone_auth_fixture() @@ -2617,9 +2684,9 @@ class ShellTestEvents(ShellBase): resource_name = 'testresource/1' self.mock_request_get( '/stacks/%s/resources/%s/events?sort_dir=asc' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '')), + resource_name))), resp_dict) self.m.ReplayAll() @@ -2693,10 +2760,10 @@ class ShellTestEvents(ShellBase): self.mock_request_get( '/stacks/%s/resources/%s/events/%s' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), ''), - parse.quote(self.event_id_one, '') + resource_name)), + parse.quote(self.event_id_one) ), resp_dict) @@ -3276,9 +3343,9 @@ class ShellTestResources(ShellBase): self.mock_request_get( '/stacks/%s/resources/%s?with_attr=attr_a&with_attr=attr_b' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), resp_dict) self.m.ReplayAll() @@ -3318,9 +3385,9 @@ class ShellTestResources(ShellBase): self.mock_request_post( '/stacks/%s/resources/%s/signal' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), '', data={'message': 'Content'} @@ -3340,9 +3407,9 @@ class ShellTestResources(ShellBase): self.mock_request_post( '/stacks/%s/resources/%s/signal' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), '', data=None @@ -3400,9 +3467,9 @@ class ShellTestResources(ShellBase): self.mock_request_post( '/stacks/%s/resources/%s/signal' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), '', data={'message': 'Content'} @@ -3425,9 +3492,9 @@ class ShellTestResources(ShellBase): self.mock_request_patch( '/stacks/%s/resources/%s' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), '', req_headers=False, @@ -3448,9 +3515,9 @@ class ShellTestResources(ShellBase): self.mock_request_patch( '/stacks/%s/resources/%s' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), '', req_headers=False, @@ -3471,9 +3538,9 @@ class ShellTestResources(ShellBase): self.mock_request_patch( '/stacks/%s/resources/%s' % ( - parse.quote(stack_id, ''), + parse.quote(stack_id), parse.quote(encodeutils.safe_encode( - resource_name), '') + resource_name)) ), '', req_headers=False, @@ -4206,7 +4273,7 @@ class MockShellTestUserPass(MockShellBase): def test_stack_list_with_args(self): self.register_keystone_auth_fixture() - resp_dict = self.stack_list_resp_dict() + resp_dict = self.stack_list_resp_dict(include_project=True) resp = fakes.FakeHTTPResponse( 200, 'success, you', diff --git a/heatclient/tests/unit/test_template_utils.py b/heatclient/tests/unit/test_template_utils.py index 7de72b5..a48e0b3 100644 --- a/heatclient/tests/unit/test_template_utils.py +++ b/heatclient/tests/unit/test_template_utils.py @@ -15,10 +15,9 @@ import base64 import json import tempfile -import mox +import mock import six from six.moves.urllib import error -from six.moves.urllib import request import testtools from testtools import matchers import yaml @@ -32,30 +31,24 @@ class ShellEnvironmentTest(testtools.TestCase): template_a = b'{"heat_template_version": "2013-05-23"}' - def setUp(self): - super(ShellEnvironmentTest, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - def collect_links(self, env, content, url, env_base_url=''): - jenv = yaml.safe_load(env) files = {} if url: - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen(url).AndReturn(six.BytesIO(content)) - request.urlopen(url).AndReturn(six.BytesIO(content)) - self.m.ReplayAll() - - template_utils.resolve_environment_urls( - jenv.get('resource_registry'), files, env_base_url) - if url: - self.assertEqual(content.decode('utf-8'), files[url]) + def side_effect(args): + if url == args: + return six.BytesIO(content) + with mock.patch('six.moves.urllib.request.urlopen') as mock_url: + mock_url.side_effect = side_effect + template_utils.resolve_environment_urls( + jenv.get('resource_registry'), files, env_base_url) + self.assertEqual(content.decode('utf-8'), files[url]) + else: + template_utils.resolve_environment_urls( + jenv.get('resource_registry'), files, env_base_url) - def test_ignore_env_keys(self): - self.m.StubOutWithMock(request, 'urlopen') + @mock.patch('six.moves.urllib.request.urlopen') + def test_ignore_env_keys(self, mock_url): env_file = '/home/my/dir/env.yaml' env = b''' resource_registry: @@ -64,9 +57,7 @@ class ShellEnvironmentTest(testtools.TestCase): hooks: pre_create restricted_actions: replace ''' - request.urlopen('file://%s' % env_file).AndReturn( - six.BytesIO(env)) - self.m.ReplayAll() + mock_url.return_value = six.BytesIO(env) _, env_dict = template_utils.process_environment_and_files( env_file) self.assertEqual( @@ -74,24 +65,18 @@ class ShellEnvironmentTest(testtools.TestCase): u'bar': {u'hooks': u'pre_create', u'restricted_actions': u'replace'}}}}, env_dict) - self.m.VerifyAll() + mock_url.assert_called_with('file://%s' % env_file) - def test_process_environment_file(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_environment_file(self, mock_url): - self.m.StubOutWithMock(request, 'urlopen') env_file = '/home/my/dir/env.yaml' env = b''' resource_registry: "OS::Thingy": "file:///home/b/a.yaml" ''' - - request.urlopen('file://%s' % env_file).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env), six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] files, env_dict = template_utils.process_environment_and_files( env_file) @@ -101,10 +86,15 @@ class ShellEnvironmentTest(testtools.TestCase): env_dict) self.assertEqual(self.template_a.decode('utf-8'), files['file:///home/b/a.yaml']) + mock_url.assert_has_calls([ + mock.call('file://%s' % env_file), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/a.yaml') + ]) - def test_process_environment_relative_file(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_environment_relative_file(self, mock_url): - self.m.StubOutWithMock(request, 'urlopen') env_file = '/home/my/dir/env.yaml' env_url = 'file:///home/my/dir/env.yaml' env = b''' @@ -112,13 +102,8 @@ class ShellEnvironmentTest(testtools.TestCase): "OS::Thingy": a.yaml ''' - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/my/dir/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/my/dir/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env), six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] self.assertEqual( env_url, @@ -136,24 +121,23 @@ class ShellEnvironmentTest(testtools.TestCase): env_dict) self.assertEqual(self.template_a.decode('utf-8'), files['file:///home/my/dir/a.yaml']) + mock_url.assert_has_calls([ + mock.call(env_url), + mock.call('file:///home/my/dir/a.yaml'), + mock.call('file:///home/my/dir/a.yaml') + ]) - def test_process_environment_relative_file_up(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_environment_relative_file_up(self, mock_url): - self.m.StubOutWithMock(request, 'urlopen') env_file = '/home/my/dir/env.yaml' env_url = 'file:///home/my/dir/env.yaml' env = b''' resource_registry: "OS::Thingy": ../bar/a.yaml ''' - - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/my/bar/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/my/bar/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env), six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] env_url = 'file://%s' % env_file self.assertEqual( @@ -172,20 +156,22 @@ class ShellEnvironmentTest(testtools.TestCase): env_dict) self.assertEqual(self.template_a.decode('utf-8'), files['file:///home/my/bar/a.yaml']) - - def test_process_environment_url(self): + mock_url.assert_has_calls([ + mock.call(env_url), + mock.call('file:///home/my/bar/a.yaml'), + mock.call('file:///home/my/bar/a.yaml') + ]) + + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_environment_url(self, mock_url): env = b''' resource_registry: "OS::Thingy": "a.yaml" ''' url = 'http://no.where/some/path/to/file.yaml' tmpl_url = 'http://no.where/some/path/to/a.yaml' - - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen(url).AndReturn(six.BytesIO(env)) - request.urlopen(tmpl_url).AndReturn(six.BytesIO(self.template_a)) - request.urlopen(tmpl_url).AndReturn(six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env), six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] files, env_dict = template_utils.process_environment_and_files( url) @@ -193,30 +179,34 @@ class ShellEnvironmentTest(testtools.TestCase): self.assertEqual({'resource_registry': {'OS::Thingy': tmpl_url}}, env_dict) self.assertEqual(self.template_a.decode('utf-8'), files[tmpl_url]) + mock_url.assert_has_calls([ + mock.call(url), + mock.call(tmpl_url), + mock.call(tmpl_url) + ]) - def test_process_environment_empty_file(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_environment_empty_file(self, mock_url): - self.m.StubOutWithMock(request, 'urlopen') env_file = '/home/my/dir/env.yaml' env = b'' - - request.urlopen('file://%s' % env_file).AndReturn(six.BytesIO(env)) - self.m.ReplayAll() + mock_url.return_value = six.BytesIO(env) files, env_dict = template_utils.process_environment_and_files( env_file) self.assertEqual({}, env_dict) self.assertEqual({}, files) + mock_url.assert_called_with('file://%s' % env_file) def test_no_process_environment_and_files(self): files, env = template_utils.process_environment_and_files() self.assertEqual({}, env) self.assertEqual({}, files) - def test_process_multiple_environments_and_files(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_multiple_environments_and_files(self, mock_url): - self.m.StubOutWithMock(request, 'urlopen') env_file1 = '/home/my/dir/env1.yaml' env_file2 = '/home/my/dir/env2.yaml' @@ -233,19 +223,12 @@ class ShellEnvironmentTest(testtools.TestCase): "OS::Thingy2": "file:///home/b/b.yaml" ''' - request.urlopen('file://%s' % env_file1).AndReturn( - six.BytesIO(env1)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file://%s' % env_file2).AndReturn( - six.BytesIO(env2)) - request.urlopen('file:///home/b/b.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env1), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a), + six.BytesIO(env2), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] files, env = template_utils.process_multiple_environments_and_files( [env_file1, env_file2]) @@ -263,10 +246,18 @@ class ShellEnvironmentTest(testtools.TestCase): files['file:///home/b/a.yaml']) self.assertEqual(self.template_a.decode('utf-8'), files['file:///home/b/b.yaml']) + mock_url.assert_has_calls([ + mock.call('file://%s' % env_file1), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/a.yaml'), + mock.call('file://%s' % env_file2), + mock.call('file:///home/b/b.yaml'), + mock.call('file:///home/b/b.yaml') + ]) + + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_multiple_environments_default_resources(self, mock_url): - def test_process_multiple_environments_default_resources(self): - - self.m.StubOutWithMock(request, 'urlopen') env_file1 = '/home/my/dir/env1.yaml' env_file2 = '/home/my/dir/env2.yaml' @@ -286,28 +277,16 @@ class ShellEnvironmentTest(testtools.TestCase): resource2: "OS::Thingy4": "file:///home/b/b.yaml" ''' - - request.urlopen('file://%s' % env_file1).InAnyOrder().AndReturn( - six.BytesIO(env1)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file://%s' % env_file2).InAnyOrder().AndReturn( - six.BytesIO(env2)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/b.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env1), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a), + six.BytesIO(env2), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] files, env = template_utils.process_multiple_environments_and_files( [env_file1, env_file2]) @@ -331,6 +310,19 @@ class ShellEnvironmentTest(testtools.TestCase): files['file:///home/b/a.yaml']) self.assertEqual(self.template_a.decode('utf-8'), files['file:///home/b/b.yaml']) + mock_url.assert_has_calls([ + mock.call('file://%s' % env_file1), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/b.yaml'), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/b.yaml'), + mock.call('file://%s' % env_file2), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/b.yaml'), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/b.yaml'), + + ], any_order=True) def test_no_process_multiple_environments_and_files(self): files, env = template_utils.process_multiple_environments_and_files() @@ -347,8 +339,6 @@ class ShellEnvironmentTest(testtools.TestCase): "OS::Thingy1": "b/a.yaml" ''' - self.m.ReplayAll() - self.object_requested = False def env_path_is_object(object_url): @@ -376,9 +366,9 @@ class ShellEnvironmentTest(testtools.TestCase): self.assertEqual(self.template_a.decode('utf-8'), files['http://no.where/path/to/b/a.yaml']) - def test_process_multiple_environments_and_files_tracker(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_multiple_environments_and_files_tracker(self, mock_url): # Setup - self.m.StubOutWithMock(request, 'urlopen') env_file1 = '/home/my/dir/env1.yaml' env1 = b''' @@ -387,13 +377,9 @@ class ShellEnvironmentTest(testtools.TestCase): resource_registry: "OS::Thingy1": "file:///home/b/a.yaml" ''' - request.urlopen('file://%s' % env_file1).AndReturn( - six.BytesIO(env1)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/b/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env1), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] # Test env_file_list = [] @@ -414,24 +400,25 @@ class ShellEnvironmentTest(testtools.TestCase): self.assertIn('file:///home/my/dir/env1.yaml', files) self.assertEqual(expected_env, json.loads(files['file:///home/my/dir/env1.yaml'])) + mock_url.assert_has_calls([ + mock.call('file://%s' % env_file1), + mock.call('file:///home/b/a.yaml'), + mock.call('file:///home/b/a.yaml'), - def test_process_environment_relative_file_tracker(self): + ]) + + @mock.patch('six.moves.urllib.request.urlopen') + def test_process_environment_relative_file_tracker(self, mock_url): - self.m.StubOutWithMock(request, 'urlopen') env_file = '/home/my/dir/env.yaml' env_url = 'file:///home/my/dir/env.yaml' env = b''' resource_registry: "OS::Thingy": a.yaml ''' - - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen('file:///home/my/dir/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - request.urlopen('file:///home/my/dir/a.yaml').AndReturn( - six.BytesIO(self.template_a)) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(env), + six.BytesIO(self.template_a), + six.BytesIO(self.template_a)] self.assertEqual( env_url, @@ -454,6 +441,12 @@ class ShellEnvironmentTest(testtools.TestCase): self.assertEqual(['file:///home/my/dir/env.yaml'], env_file_list) self.assertEqual(json.dumps(expected_env), files['file:///home/my/dir/env.yaml']) + mock_url.assert_has_calls([ + mock.call(env_url), + mock.call('file:///home/my/dir/a.yaml'), + mock.call('file:///home/my/dir/a.yaml'), + + ]) def test_global_files(self): url = 'file:///home/b/a.yaml' @@ -538,13 +531,6 @@ class ShellEnvironmentTest(testtools.TestCase): class TestGetTemplateContents(testtools.TestCase): - def setUp(self): - super(TestGetTemplateContents, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - def test_get_template_contents_file(self): with tempfile.NamedTemporaryFile() as tmpl_file: tmpl = (b'{"AWSTemplateFormatVersion" : "2010-09-09",' @@ -611,23 +597,22 @@ class TestGetTemplateContents(testtools.TestCase): matchers.MatchesRegex( 'Error parsing template file://%s ' % tmpl_file.name)) - def test_get_template_contents_url(self): + @mock.patch('six.moves.urllib.request.urlopen') + def test_get_template_contents_url(self, mock_url): tmpl = b'{"AWSTemplateFormatVersion" : "2010-09-09", "foo": "bar"}' url = 'http://no.where/path/to/a.yaml' - self.m.StubOutWithMock(request, 'urlopen') - request.urlopen(url).AndReturn(six.BytesIO(tmpl)) - self.m.ReplayAll() + mock_url.return_value = six.BytesIO(tmpl) files, tmpl_parsed = template_utils.get_template_contents( template_url=url) self.assertEqual({"AWSTemplateFormatVersion": "2010-09-09", "foo": "bar"}, tmpl_parsed) self.assertEqual({}, files) + mock_url.assert_called_with(url) def test_get_template_contents_object(self): tmpl = '{"AWSTemplateFormatVersion" : "2010-09-09", "foo": "bar"}' url = 'http://no.where/path/to/a.yaml' - self.m.ReplayAll() self.object_requested = False @@ -652,7 +637,6 @@ class TestGetTemplateContents(testtools.TestCase): '"FooBar": {' '"type": "foo/bar.yaml"}}}') url = 'http://no.where/path/to/a.yaml' - self.m.ReplayAll() self.object_requested = False @@ -685,15 +669,15 @@ class TestGetTemplateContents(testtools.TestCase): [{'path': '/tmp/%s' % filename, 'content': {'get_file': url}, 'encoding': 'b64'}]}}}}} - self.m.StubOutWithMock(request, 'urlopen') - raw_content = base64.decodestring(content) - response = six.BytesIO(raw_content) - request.urlopen(url).AndReturn(response) - self.m.ReplayAll() - files = {} - template_utils.resolve_template_get_files( - template, files, base_url) - self.assertEqual({url: content}, files) + with mock.patch('six.moves.urllib.request.urlopen') as mock_url: + raw_content = base64.decodestring(content) + response = six.BytesIO(raw_content) + mock_url.return_value = response + files = {} + template_utils.resolve_template_get_files( + template, files, base_url) + self.assertEqual({url: content}, files) + mock_url.assert_called_with(url) def test_get_zip_content(self): filename = 'heat.zip' @@ -746,6 +730,7 @@ ABOkDAABQSwUGAAAAAAEAAQBOAAAARwAAAAAA\n''' self.check_non_utf8_content(filename=filename, content=content) +@mock.patch('six.moves.urllib.request.urlopen') class TestTemplateGetFileFunctions(testtools.TestCase): hot_template = b'''heat_template_version: 2013-05-23 @@ -769,49 +754,21 @@ resources: ignored_none: {get_file: } ''' - def setUp(self): - super(TestTemplateGetFileFunctions, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_hot_template(self): - self.m.StubOutWithMock(request, 'urlopen') + def test_hot_template(self, mock_url): tmpl_file = '/home/my/dir/template.yaml' url = 'file:///home/my/dir/template.yaml' - request.urlopen(url).AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen( - 'http://localhost/bar.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'bar contents')) - request.urlopen( - 'file:///home/my/dir/foo.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'foo contents')) - request.urlopen( - 'file:///home/my/dir/baz/baz1.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'baz1 contents')) - request.urlopen( - 'file:///home/my/dir/baz/baz2.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'baz2 contents')) - request.urlopen( - 'file:///home/my/dir/baz/baz3.yaml').InAnyOrder().AndReturn( - six.BytesIO(b'baz3 contents')) - - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(self.hot_template), + six.BytesIO(b'bar contents'), + six.BytesIO(b'foo contents'), + six.BytesIO(b'baz1 contents'), + six.BytesIO(b'baz2 contents'), + six.BytesIO(b'baz3 contents')] files, tmpl_parsed = template_utils.get_template_contents( template_file=tmpl_file) self.assertEqual({ - 'http://localhost/bar.yaml': b'bar contents', - 'file:///home/my/dir/foo.yaml': b'foo contents', - 'file:///home/my/dir/baz/baz1.yaml': b'baz1 contents', - 'file:///home/my/dir/baz/baz2.yaml': b'baz2 contents', - 'file:///home/my/dir/baz/baz3.yaml': b'baz3 contents', - }, files) - self.assertEqual({ 'heat_template_version': '2013-05-23', 'resources': { 'resource1': { @@ -836,9 +793,16 @@ resources: } } }, tmpl_parsed) - - def test_hot_template_outputs(self): - self.m.StubOutWithMock(request, 'urlopen') + mock_url.assert_has_calls([ + mock.call(url), + mock.call('http://localhost/bar.yaml'), + mock.call('file:///home/my/dir/foo.yaml'), + mock.call('file:///home/my/dir/baz/baz1.yaml'), + mock.call('file:///home/my/dir/baz/baz2.yaml'), + mock.call('file:///home/my/dir/baz/baz3.yaml') + ], any_order=True) + + def test_hot_template_outputs(self, mock_url): tmpl_file = '/home/my/dir/template.yaml' url = 'file://%s' % tmpl_file foo_url = 'file:///home/my/dir/foo.yaml' @@ -848,15 +812,17 @@ outputs:\n\ contents:\n\ value:\n\ get_file: foo.yaml\n''' - request.urlopen(url).AndReturn(six.BytesIO(contents)) - request.urlopen(foo_url).AndReturn(six.BytesIO(b'foo contents')) - self.m.ReplayAll() + mock_url.side_effect = [six.BytesIO(contents), + six.BytesIO(b'foo contents')] files = template_utils.get_template_contents( template_file=tmpl_file)[0] self.assertEqual({foo_url: b'foo contents'}, files) + mock_url.assert_has_calls([ + mock.call(url), + mock.call(foo_url) + ]) - def test_hot_template_same_file(self): - self.m.StubOutWithMock(request, 'urlopen') + def test_hot_template_same_file(self, mock_url): tmpl_file = '/home/my/dir/template.yaml' url = 'file://%s' % tmpl_file foo_url = 'file:///home/my/dir/foo.yaml' @@ -869,14 +835,17 @@ outputs:\n\ template:\n\ value:\n\ get_file: foo.yaml\n''' - request.urlopen(url).AndReturn(six.BytesIO(contents)) + mock_url.side_effect = [six.BytesIO(contents), + six.BytesIO(b'foo contents')] # asserts that is fetched only once even though it is # referenced in the template twice - request.urlopen(foo_url).AndReturn(six.BytesIO(b'foo contents')) - self.m.ReplayAll() files = template_utils.get_template_contents( template_file=tmpl_file)[0] self.assertEqual({foo_url: b'foo contents'}, files) + mock_url.assert_has_calls([ + mock.call(url), + mock.call(foo_url) + ]) class TestTemplateTypeFunctions(testtools.TestCase): @@ -909,38 +878,26 @@ parameters: type: string ''' - def setUp(self): - super(TestTemplateTypeFunctions, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_hot_template(self): - self.m.StubOutWithMock(request, 'urlopen') + @mock.patch('six.moves.urllib.request.urlopen') + def test_hot_template(self, mock_url): tmpl_file = '/home/my/dir/template.yaml' url = 'file:///home/my/dir/template.yaml' - request.urlopen( - 'file:///home/my/dir/foo.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen( - 'file:///home/my/dir/foo.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(url).InAnyOrder().AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen( - 'file:///home/my/dir/spam/egg.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - request.urlopen( - 'file:///home/my/dir/spam/egg.yaml').InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - self.m.ReplayAll() + + def side_effect(args): + if url == args: + return six.BytesIO(self.hot_template) + if 'file:///home/my/dir/foo.yaml' == args: + return six.BytesIO(self.foo_template) + if 'file:///home/my/dir/spam/egg.yaml' == args: + return six.BytesIO(self.egg_template) + mock_url.side_effect = side_effect files, tmpl_parsed = template_utils.get_template_contents( template_file=tmpl_file) self.assertEqual(yaml.safe_load(self.foo_template.decode('utf-8')), json.loads(files.get('file:///home/my/dir/foo.yaml'))) + self.assertEqual( yaml.safe_load(self.egg_template.decode('utf-8')), json.loads(files.get('file:///home/my/dir/spam/egg.yaml'))) @@ -968,6 +925,12 @@ parameters: } }, tmpl_parsed) + mock_url.assert_has_calls([ + mock.call('file:///home/my/dir/foo.yaml'), + mock.call(url), + mock.call('file:///home/my/dir/spam/egg.yaml'), + ], any_order=True) + class TestTemplateInFileFunctions(testtools.TestCase): @@ -993,30 +956,21 @@ parameters: type: string ''' - def setUp(self): - super(TestTemplateInFileFunctions, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_hot_template(self): - self.m.StubOutWithMock(request, 'urlopen') + @mock.patch('six.moves.urllib.request.urlopen') + def test_hot_template(self, mock_url): tmpl_file = '/home/my/dir/template.yaml' url = 'file:///home/my/dir/template.yaml' foo_url = 'file:///home/my/dir/foo.yaml' bar_url = 'file:///home/my/dir/bar.yaml' - request.urlopen(url).InAnyOrder().AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(bar_url).InAnyOrder().AndReturn( - six.BytesIO(self.bar_template)) - request.urlopen(bar_url).InAnyOrder().AndReturn( - six.BytesIO(self.bar_template)) - self.m.ReplayAll() + + def side_effect(args): + if url == args: + return six.BytesIO(self.hot_template) + if foo_url == args: + return six.BytesIO(self.foo_template) + if bar_url == args: + return six.BytesIO(self.bar_template) + mock_url.side_effect = side_effect files, tmpl_parsed = template_utils.get_template_contents( template_file=tmpl_file) @@ -1052,6 +1006,12 @@ parameters: } }, tmpl_parsed) + mock_url.assert_has_calls([ + mock.call(foo_url), + mock.call(url), + mock.call(bar_url), + ], any_order=True) + class TestNestedIncludes(testtools.TestCase): @@ -1095,15 +1055,8 @@ parameters: type: string ''' - def setUp(self): - super(TestNestedIncludes, self).setUp() - self.m = mox.Mox() - - self.addCleanup(self.m.VerifyAll) - self.addCleanup(self.m.UnsetStubs) - - def test_env_nested_includes(self): - self.m.StubOutWithMock(request, 'urlopen') + @mock.patch('six.moves.urllib.request.urlopen') + def test_env_nested_includes(self, mock_url): env_file = '/home/my/dir/env.yaml' env_url = 'file:///home/my/dir/env.yaml' env = b''' @@ -1118,35 +1071,24 @@ parameters: two_url = u'file:///home/my/dir/spam/two.yaml' three_url = u'file:///home/my/dir/spam/three.yaml' - request.urlopen(env_url).AndReturn( - six.BytesIO(env)) - request.urlopen(template_url).AndReturn( - six.BytesIO(self.hot_template)) - request.urlopen(template_url).AndReturn( - six.BytesIO(self.hot_template)) - - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(egg_url).InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - request.urlopen(ham_url).InAnyOrder().AndReturn( - six.BytesIO(b'ham contents')) - request.urlopen(one_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(two_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(three_url).InAnyOrder().AndReturn( - six.BytesIO(b'three contents')) - request.urlopen(foo_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(egg_url).InAnyOrder().AndReturn( - six.BytesIO(self.egg_template)) - request.urlopen(one_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - request.urlopen(two_url).InAnyOrder().AndReturn( - six.BytesIO(self.foo_template)) - - self.m.ReplayAll() + def side_effect(args): + if env_url == args: + return six.BytesIO(env) + if template_url == args: + return six.BytesIO(self.hot_template) + if foo_url == args: + return six.BytesIO(self.foo_template) + if egg_url == args: + return six.BytesIO(self.egg_template) + if ham_url == args: + return six.BytesIO(b'ham contents') + if one_url == args: + return six.BytesIO(self.foo_template) + if two_url == args: + return six.BytesIO(self.foo_template) + if three_url == args: + return six.BytesIO(b'three contents') + mock_url.side_effect = side_effect files, env_dict = template_utils.process_environment_and_files( env_file) @@ -1201,3 +1143,13 @@ parameters: json.loads(files.get(two_url))) self.assertEqual(b'three contents', files.get(three_url)) + mock_url.assert_has_calls([ + mock.call(env_url), + mock.call(template_url), + mock.call(foo_url), + mock.call(egg_url), + mock.call(ham_url), + mock.call(one_url), + mock.call(two_url), + mock.call(three_url), + ], any_order=True) |