Improve unit tests for subunit_describe_calls

in order to enhance the cmd to support kubernetes url based
routing over port based routing.  Once this is merged I will
submit another patch set to subunit_describe_calls to support
-u --urls (Optional) The path to a JSON file describing the urls
  being used by different services. Note Can not be used with -p.

Added test cases for cliff subprocess and found a bug with the
-v. The standard -v --verbose of cliff conflict with this command
 so I changed the -v --verbose to -a --all for print header
with printing to stdout I am also getting ready to add test cases
for cliff subprocess support for different options. Bug #1890060

Correct os join in test cases to avoid a conflict with
https://review.opendev.org/#/c/683026

Closes-bug: #1890060
Change-Id: I9459db0dbeda721187ea5f4802c7453c2092dac3
This commit is contained in:
Doug Schveninger 2020-07-18 11:03:21 -05:00
parent b18d7dda30
commit 6aa733e0f3
5 changed files with 522 additions and 207 deletions

View File

@ -0,0 +1,10 @@
---
fixes:
- |
Fixed bug #1890060. tempest subunit_describe_calls --verbose not working with Cliff CLI.
The subunit_describe_calls --verbose argument was a boolean and worked in the non Cliff CLI
which is now deprecated, but does not work with cliff since --verbase is a standard cliff
argument which is an int. Since the tool is in lib directory we cannot change the interface,
so we add a new argument -a --all-stdout that will allow cliff CLI to support the
feature in subunnit_describe_calls to print request and response headers and bodies
to stdout.

View File

@ -30,6 +30,8 @@ Runtime Arguments
* ``--ports, -p``: (Optional) The path to a JSON file describing the ports * ``--ports, -p``: (Optional) The path to a JSON file describing the ports
being used by different services being used by different services
* ``--verbose, -v``: (Optional) Print Request and Response Headers and Body * ``--verbose, -v``: (Optional) Print Request and Response Headers and Body
data to stdout in the non cliff deprecated CLI
* ``--all-stdout, -a``: (Optional) Print Request and Response Headers and Body
data to stdout data to stdout
@ -278,7 +280,7 @@ def parse(stream, non_subunit_name, ports):
return url_parser return url_parser
def output(url_parser, output_file, verbose): def output(url_parser, output_file, all_stdout):
if output_file is not None: if output_file is not None:
with open(output_file, "w") as outfile: with open(output_file, "w") as outfile:
outfile.write(json.dumps(url_parser.test_logs)) outfile.write(json.dumps(url_parser.test_logs))
@ -294,7 +296,7 @@ def output(url_parser, output_file, verbose):
sys.stdout.write('\t- {0} {1} request for {2} to {3}\n'.format( sys.stdout.write('\t- {0} {1} request for {2} to {3}\n'.format(
item.get('status_code'), item.get('verb'), item.get('status_code'), item.get('verb'),
item.get('service'), item.get('url'))) item.get('service'), item.get('url')))
if verbose: if all_stdout:
sys.stdout.write('\t\t- request headers: {0}\n'.format( sys.stdout.write('\t\t- request headers: {0}\n'.format(
item.get('request_headers'))) item.get('request_headers')))
sys.stdout.write('\t\t- request body: {0}\n'.format( sys.stdout.write('\t\t- request body: {0}\n'.format(
@ -313,7 +315,7 @@ def entry_point(cl_args=None):
"please use: 'tempest subunit-describe-calls'") "please use: 'tempest subunit-describe-calls'")
cl_args = ArgumentParser().parse_args() cl_args = ArgumentParser().parse_args()
parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports) parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
output(parser, cl_args.output_file, cl_args.verbose) output(parser, cl_args.output_file, cl_args.all_stdout)
def _parser_add_args(parser): def _parser_add_args(parser):
@ -339,9 +341,23 @@ def _parser_add_args(parser):
help="A JSON file describing the ports for each service." help="A JSON file describing the ports for each service."
) )
parser.add_argument( group = parser.add_mutually_exclusive_group()
"-v", "--verbose", action='store_true', default=False, # the -v and --verbose command are for the old subunit-describe-calls
help="Add Request and Response header and body data to stdout." # main() CLI interface. It does not work with the new
# tempest subunit-describe-callss CLI. So when the main CLI approach is
# deleted this argument is not needed.
group.add_argument(
"-v", "--verbose", action='store_true', dest='all_stdout',
help='Add Request and Response header and body data to stdout print.'
' NOTE: This argument deprecated and does not work with'
' tempest subunit-describe-calls CLI.'
' Use new option: "-a", "--all-stdout"'
)
group.add_argument(
"-a", "--all-stdout", action='store_true',
help="Add Request and Response header and body data to stdout print."
" Note: this argument work with the subunit-describe-calls and"
" tempest subunit-describe-calls CLI commands."
) )

View File

@ -0,0 +1,87 @@
{"bar":[
{
"name":"AgentsAdminTestJSON:setUp",
"request_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-424013832\", \"os\": \"linux\"}}",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-424013832\", \"os\": \"linux\", \"agent_id\": 1}}",
"response_headers":"{'status': '200', 'content-length': '203', 'x-compute-request-id': 'req-25ddaae2-0ef1-40d1-8228-59bd64a7e75b', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents",
"verb":"POST"
},
{
"name":"AgentsAdminTestJSON:test_create_agent",
"request_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"kvm\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86-252246646\", \"os\": \"win\"}}",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"kvm\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86-252246646\", \"os\": \"win\", \"agent_id\": 2}}",
"response_headers":"{'status': '200', 'content-length': '195', 'x-compute-request-id': 'req-b4136f06-c015-4e7e-995f-c43831e3ecce', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents",
"verb":"POST"
},
{
"name":"AgentsAdminTestJSON:tearDown",
"request_body":"None",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_body":"",
"response_headers":"{'status': '200', 'content-length': '0', 'x-compute-request-id': 'req-ee905fd6-a5b5-4da4-8c37-5363cb25bd9d', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents/1",
"verb":"DELETE"
},
{
"name":"AgentsAdminTestJSON:_run_cleanups",
"request_body":"None",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_headers":"{'status': '200', 'content-length': '0', 'x-compute-request-id': 'req-e912cac0-63e0-4679-a68a-b6d18ddca074', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents/2",
"verb":"DELETE"
}], "foo":[
{
"name":"AgentsAdminTestJSON:setUp",
"request_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-948635295\", \"os\": \"linux\"}}",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-948635295\", \"os\": \"linux\", \"agent_id\": 3}}",
"response_headers":"{'status': '200', 'content-length': '203', 'x-compute-request-id': 'req-ccd2116d-04b1-4ffe-ae32-fb623f68bf1c', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents",
"verb":"POST"
},
{
"name":"AgentsAdminTestJSON:test_delete_agent",
"request_body":"None",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_body":"",
"response_headers":"{'status': '200', 'content-length': '0', 'x-compute-request-id': 'req-6e7fa28f-ae61-4388-9a78-947c58bc0588', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents/3",
"verb":"DELETE"
},
{
"name":"AgentsAdminTestJSON:test_delete_agent",
"request_body":"None",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_body":"{\"agents\": []}",
"response_headers":"{'status': '200', 'content-length': '14', 'content-location': 'http://23.253.76.97:8774/v2.1/cf6b1933fe5b476fbbabb876f6d1b924/os-agents', 'x-compute-request-id': 'req-e41aa9b4-41a6-4138-ae04-220b768eb644', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': 'application/json'}",
"service":"Nova",
"status_code":"200",
"url":"v2.1/<id>/os-agents",
"verb":"GET"
},
{
"name":"AgentsAdminTestJSON:tearDown",
"request_body":"None",
"request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
"response_headers":"{'status': '404', 'content-length': '82', 'x-compute-request-id': 'req-e297aeea-91cf-4f26-b49c-8f46b1b7a926', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:02 GMT', 'content-type': 'application/json; charset=UTF-8'}",
"service":"Nova",
"status_code":"404",
"url":"v2.1/<id>/os-agents/3",
"verb":"DELETE"
}]}

View File

@ -14,220 +14,422 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import argparse
from io import StringIO
import os import os
import shutil
import subprocess import subprocess
import sys
import tempfile import tempfile
from unittest import mock
from unittest.mock import patch
from oslo_serialization import jsonutils as json
from tempest.cmd import subunit_describe_calls from tempest.cmd import subunit_describe_calls
from tempest.tests import base from tempest.tests import base
class TestSubunitDescribeCalls(base.TestCase): class TestArgumentParser(base.TestCase):
def test_return_code(self): def test_init(self):
subunit_file = os.path.join( test_object = subunit_describe_calls.ArgumentParser()
os.path.dirname(os.path.abspath(__file__)), self.assertEqual("subunit-describe-calls", test_object.prog)
'sample_streams/calls.subunit') self.assertEqual(subunit_describe_calls.DESCRIPTION,
p = subprocess.Popen([ test_object.description)
'subunit-describe-calls', '-s', subunit_file,
'-o', tempfile.mkstemp()[1]], stdin=subprocess.PIPE)
p.communicate()
self.assertEqual(0, p.returncode)
def test_verbose(self):
subunit_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'sample_streams/calls.subunit')
p = subprocess.Popen([
'subunit-describe-calls', '-s', subunit_file,
'-v'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout = p.communicate()
self.assertEqual(0, p.returncode)
self.assertIn(b'- request headers:', stdout[0])
self.assertIn(b'- request body:', stdout[0])
self.assertIn(b'- response headers:', stdout[0])
self.assertIn(b'- response body:', stdout[0])
def test_return_code_no_output(self): class TestUrlParser(base.TestCase):
subunit_file = os.path.join( services_custom_ports = {
"18776": "Block Storage",
"18774": "Nova",
"18773": "Nova-API",
"18386": "Sahara",
"35358": "Keystone",
"19292": "Glance",
"19696": "Neutron",
"16000": "Swift",
"18004": "Heat",
"18777": "Ceilometer",
"10080": "Horizon",
"18080": "Swift",
"1873": "rsync",
"13260": "iSCSI",
"13306": "MySQL",
"15672": "AMQP",
"18082": "murano"}
def setUp(self):
super(TestUrlParser, self).setUp()
self.test_object = subunit_describe_calls.UrlParser()
def test_get_service_default_ports(self):
base_url = "http://site.something.com:"
for port in self.test_object.services:
url = base_url + port + "/v2/action"
service = self.test_object.services[port]
self.assertEqual(service, self.test_object.get_service(url))
def test_get_service_custom_ports(self):
self.test_object = subunit_describe_calls.\
UrlParser(services=self.services_custom_ports)
base_url = "http://site.something.com:"
for port in self.services_custom_ports:
url = base_url + port + "/v2/action"
service = self.services_custom_ports[port]
self.assertEqual(service, self.test_object.get_service(url))
def test_get_service_port_not_found(self):
url = "https://site.somewhere.com:1234/v2/action"
self.assertEqual("Unknown", self.test_object.get_service(url))
self.assertEqual("Unknown", self.test_object.get_service(""))
def test_parse_details_none(self):
self.assertIsNone(self.test_object.parse_details(None))
def test_url_path_ports(self):
uuid_sample1 = "3715e0bb-b1b3-4291-aa13-2c86c3b9ec93"
uuid_sample2 = "2715e0bb-b1b4-4291-aa13-2c86c3b9ec88"
# test http url
host = "http://host.company.com"
url = host + ":8776/v3/" + uuid_sample1 + "/types/" + \
uuid_sample2 + "/extra_specs"
self.assertEqual("v3/<uuid>/types/<uuid>/extra_specs",
self.test_object.url_path(url))
url = host + ":8774/v2.1/servers/" + uuid_sample1
self.assertEqual("v2.1/servers/<uuid>",
self.test_object.url_path(url))
# test https url
host = "https://host.company.com"
url = host + ":8776/v3/" + uuid_sample1 + "/types/" + \
uuid_sample2 + "/extra_specs"
self.assertEqual("v3/<uuid>/types/<uuid>/extra_specs",
self.test_object.url_path(url))
url = host + ":8774/v2.1/servers/" + uuid_sample1
self.assertEqual("v2.1/servers/<uuid>",
self.test_object.url_path(url))
def test_url_path_no_match(self):
host_port = 'https://host.company.com:1234/'
url = 'v2/action/no/special/data'
self.assertEqual(url, self.test_object.url_path(host_port + url))
url = 'data'
self.assertEqual(url, self.test_object.url_path(url))
class TestCliBase(base.TestCase):
"""Base class for share code on all CLI sub-process testing"""
def setUp(self):
super(TestCliBase, self).setUp()
self._subunit_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
'sample_streams/calls.subunit') 'subunit_describe_calls_data', 'calls.subunit')
def _bytes_to_string(self, data):
if isinstance(data, (bytes, bytearray)):
data = str(data, 'utf-8')
return data
def _assert_cli_message(self, data):
data = self._bytes_to_string(data)
self.assertIn("Running subunit_describe_calls ...", data)
def _assert_deprecated_warning(self, stdout):
self.assertIn(
b"Use of: 'subunit-describe-calls' is deprecated, "
b"please use: 'tempest subunit-describe-calls'", stdout)
def _assert_expect_json(self, json_data):
expected_file_name = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'subunit_describe_calls_data', 'calls_subunit_expected.json')
with open(expected_file_name, "rb") as read_file:
expected_result = json.load(read_file)
self.assertDictEqual(expected_result, json_data)
def _assert_headers_and_bodies(self, data):
data = self._bytes_to_string(data)
self.assertIn('- request headers:', data)
self.assertIn('- request body:', data)
self.assertIn('- response headers:', data)
self.assertIn('- response body:', data)
def _assert_methods_details(self, data):
data = self._bytes_to_string(data)
self.assertIn('foo', data)
self.assertIn('- 200 POST request for Nova to v2.1/<id>/',
data)
self.assertIn('- 200 DELETE request for Nova to v2.1/<id>/',
data)
self.assertIn('- 200 GET request for Nova to v2.1/<id>/',
data)
self.assertIn('- 404 DELETE request for Nova to v2.1/<id>/',
data)
def _assert_mutual_exclusive_message(self, stderr):
self.assertIn(b"usage: subunit-describe-calls "
b"[-h] [-s [<subunit file>]]", stderr)
self.assertIn(b"[-n <non subunit name>] [-o <output file>]",
stderr)
self.assertIn(b"[-p <ports file>] [-v | -a]", stderr)
self.assertIn(
b"subunit-describe-calls: error: argument -v/--verbose: "
b"not allowed with argument -a/--all-stdout", stderr)
def _assert_no_headers_and_bodies(self, data):
data = self._bytes_to_string(data)
self.assertNotIn('- request headers:', data)
self.assertNotIn('- request body:', data)
self.assertNotIn('- response headers:', data)
self.assertNotIn('- response body:', data)
class TestMainCli(TestCliBase):
"""Test cases that use subunit_describe_calls module main interface
via subprocess calls to make sure the total user experience
is well defined and tested. This interface is deprecated.
Note: these test do not affect code coverage percentages.
"""
def test_main_output_file(self):
temp_file = tempfile.mkstemp()[1]
p = subprocess.Popen([ p = subprocess.Popen([
'subunit-describe-calls', '-s', subunit_file], 'subunit-describe-calls', '-s', self._subunit_file,
stdin=subprocess.PIPE, stdout=subprocess.PIPE) '-o', temp_file], stdin=subprocess.PIPE,
stdout = p.communicate() stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode) self.assertEqual(0, p.returncode)
self.assertIn(b'foo', stdout[0]) self._assert_cli_message(stdout)
self.assertIn(b'- 200 POST request for Nova to v2.1/<id>/', self._assert_deprecated_warning(stdout)
stdout[0]) with open(temp_file, 'r') as file:
self.assertIn(b'- 200 DELETE request for Nova to v2.1/<id>/', data = json.loads(file.read())
stdout[0]) self._assert_expect_json(data)
self.assertIn(b'- 200 GET request for Nova to v2.1/<id>/',
stdout[0]) def test_main_verbose(self):
self.assertIn(b'- 404 DELETE request for Nova to v2.1/<id>/', p = subprocess.Popen([
stdout[0]) 'subunit-describe-calls', '-s', self._subunit_file,
self.assertNotIn(b'- request headers:', stdout[0]) '-v'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
self.assertNotIn(b'- request body:', stdout[0]) stderr=subprocess.PIPE)
self.assertNotIn(b'- response headers:', stdout[0]) stdout, stderr = p.communicate()
self.assertNotIn(b'- response body:', stdout[0]) self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_deprecated_warning(stdout)
self._assert_methods_details(stdout)
self._assert_headers_and_bodies(stdout)
def test_main_all_stdout(self):
p = subprocess.Popen([
'subunit-describe-calls', '-s', self._subunit_file,
'--all-stdout'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_deprecated_warning(stdout)
self._assert_methods_details(stdout)
self._assert_headers_and_bodies(stdout)
def test_main(self):
p = subprocess.Popen([
'subunit-describe-calls', '-s', self._subunit_file],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_deprecated_warning(stdout)
self._assert_methods_details(stdout)
self._assert_no_headers_and_bodies(stdout)
def test_main_verbose_and_all_stdout(self):
p = subprocess.Popen([
'subunit-describe-calls', '-s', self._subunit_file,
'-a', '-v'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(2, p.returncode)
self._assert_cli_message(stdout)
self._assert_deprecated_warning(stdout)
self._assert_mutual_exclusive_message(stderr)
class TestCli(TestCliBase):
"""Test cases that use tempest subunit_describe_calls cliff interface
via subprocess calls to make sure the total user experience
is well defined and tested.
Note: these test do not affect code coverage percentages.
"""
def _assert_cliff_verbose(self, stdout):
self.assertIn(b'tempest initialize_app', stdout)
self.assertIn(b'prepare_to_run_command TempestSubunitDescribeCalls',
stdout)
self.assertIn(b'tempest clean_up TempestSubunitDescribeCalls',
stdout)
def test_run_all_stdout(self):
p = subprocess.Popen(['tempest', 'subunit-describe-calls',
'-s', self._subunit_file, '-a'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_methods_details(stdout)
self._assert_headers_and_bodies(stdout)
def test_run_verbose(self):
p = subprocess.Popen(['tempest', 'subunit-describe-calls',
'-s', self._subunit_file, '-v'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_methods_details(stdout)
self._assert_no_headers_and_bodies(stdout)
self._assert_cliff_verbose(stderr)
def test_run_min(self):
p = subprocess.Popen(['tempest', 'subunit-describe-calls',
'-s', self._subunit_file],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_methods_details(stdout)
self._assert_no_headers_and_bodies(stdout)
def test_run_verbose_all_stdout(self):
"""Test Cliff -v argument
Since Cliff framework has a argument at the
abstract command level the -v or --verbose for
this command is not processed as a boolean.
So the use of verbose only exists for the
deprecated main CLI interface. When the
main is deleted this test would not be needed.
"""
p = subprocess.Popen(['tempest', 'subunit-describe-calls',
'-s', self._subunit_file, '-a', '-v'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
self.assertEqual(0, p.returncode)
self._assert_cli_message(stdout)
self._assert_cliff_verbose(stderr)
self._assert_methods_details(stdout)
class TestSubunitDescribeCalls(TestCliBase):
"""Test cases use the subunit_describe_calls module interface
and effect code coverage reporting
"""
def setUp(self):
super(TestSubunitDescribeCalls, self).setUp()
self.test_object = subunit_describe_calls.TempestSubunitDescribeCalls(
app=mock.Mock(),
app_args=mock.Mock(spec=argparse.Namespace))
def test_parse(self): def test_parse(self):
subunit_file = os.path.join( with open(self._subunit_file, 'r') as read_file:
os.path.dirname(os.path.abspath(__file__)), parser = subunit_describe_calls.parse(
'sample_streams/calls.subunit') read_file, "pythonlogging", None)
parser = subunit_describe_calls.parse( self._assert_expect_json(parser.test_logs)
open(subunit_file), "pythonlogging", None)
expected_result = {
'bar': [{
'name': 'AgentsAdminTestJSON:setUp',
'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
'"hypervisor": "common", "md5hash": '
'"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
'"architecture": "tempest-x86_64-424013832", "os": "linux"}}',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
'"hypervisor": "common", "md5hash": '
'"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
'"architecture": "tempest-x86_64-424013832", "os": "linux", '
'"agent_id": 1}}',
'response_headers': "{'status': '200', 'content-length': "
"'203', 'x-compute-request-id': "
"'req-25ddaae2-0ef1-40d1-8228-59bd64a7e75b', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents',
'verb': 'POST'}, {
'name': 'AgentsAdminTestJSON:test_create_agent',
'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
'"hypervisor": "kvm", "md5hash": '
'"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
'"architecture": "tempest-x86-252246646", "os": "win"}}',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
'"hypervisor": "kvm", "md5hash": '
'"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
'"architecture": "tempest-x86-252246646", "os": "win", '
'"agent_id": 2}}',
'response_headers': "{'status': '200', 'content-length': "
"'195', 'x-compute-request-id': "
"'req-b4136f06-c015-4e7e-995f-c43831e3ecce', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents',
'verb': 'POST'}, {
'name': 'AgentsAdminTestJSON:tearDown',
'request_body': 'None',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_body': '',
'response_headers': "{'status': '200', 'content-length': "
"'0', 'x-compute-request-id': "
"'req-ee905fd6-a5b5-4da4-8c37-5363cb25bd9d', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents/1',
'verb': 'DELETE'}, {
'name': 'AgentsAdminTestJSON:_run_cleanups',
'request_body': 'None',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_headers': "{'status': '200', 'content-length': "
"'0', 'x-compute-request-id': "
"'req-e912cac0-63e0-4679-a68a-b6d18ddca074', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents/2',
'verb': 'DELETE'}],
'foo': [{
'name': 'AgentsAdminTestJSON:setUp',
'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
'"hypervisor": "common", "md5hash": '
'"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
'"architecture": "tempest-x86_64-948635295", "os": "linux"}}',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
'"hypervisor": "common", "md5hash": '
'"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
'"architecture": "tempest-x86_64-948635295", "os": "linux", '
'"agent_id": 3}}',
'response_headers': "{'status': '200', 'content-length': "
"'203', 'x-compute-request-id': "
"'req-ccd2116d-04b1-4ffe-ae32-fb623f68bf1c', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents',
'verb': 'POST'}, {
'name': 'AgentsAdminTestJSON:test_delete_agent',
'request_body': 'None',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_body': '',
'response_headers': "{'status': '200', 'content-length': "
"'0', 'x-compute-request-id': "
"'req-6e7fa28f-ae61-4388-9a78-947c58bc0588', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents/3',
'verb': 'DELETE'}, {
'name': 'AgentsAdminTestJSON:test_delete_agent',
'request_body': 'None',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_body': '{"agents": []}',
'response_headers': "{'status': '200', 'content-length': "
"'14', 'content-location': "
"'http://23.253.76.97:8774/v2.1/"
"cf6b1933fe5b476fbbabb876f6d1b924/os-agents', "
"'x-compute-request-id': "
"'req-e41aa9b4-41a6-4138-ae04-220b768eb644', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
"'application/json'}",
'service': 'Nova',
'status_code': '200',
'url': 'v2.1/<id>/os-agents',
'verb': 'GET'}, {
'name': 'AgentsAdminTestJSON:tearDown',
'request_body': 'None',
'request_headers': "{'Content-Type': 'application/json', "
"'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
'response_headers': "{'status': '404', 'content-length': "
"'82', 'x-compute-request-id': "
"'req-e297aeea-91cf-4f26-b49c-8f46b1b7a926', 'vary': "
"'X-OpenStack-Nova-API-Version', 'connection': 'close', "
"'x-openstack-nova-api-version': '2.1', 'date': "
"'Tue, 02 Feb 2016 03:27:02 GMT', 'content-type': "
"'application/json; charset=UTF-8'}",
'service': 'Nova',
'status_code': '404',
'url': 'v2.1/<id>/os-agents/3',
'verb': 'DELETE'}]}
self.assertEqual(expected_result, parser.test_logs) def test_get_description(self):
self.assertEqual(subunit_describe_calls.DESCRIPTION,
self.test_object.get_description())
def test_get_parser_default_min(self):
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args([])
self.assertIsNone(parsed_args.output_file)
self.assertIsNone(parsed_args.ports)
self.assertFalse(parsed_args.all_stdout)
self.assertEqual(parsed_args.subunit, sys.stdin)
def test_get_parser_default_max(self):
temp_dir = tempfile.mkdtemp(prefix="parser")
self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
outfile_name = os.path.join(temp_dir, 'output.json')
open(outfile_name, 'a').close()
portfile_name = os.path.join(temp_dir, 'ports.json')
open(portfile_name, 'a').close()
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args(["-a", "-o " + outfile_name,
"-p " + portfile_name])
self.assertIsNotNone(parsed_args.output_file)
self.assertIsNotNone(parsed_args.ports)
self.assertTrue(parsed_args.all_stdout)
self.assertEqual(parsed_args.subunit, sys.stdin)
def test_take_action_min(self):
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args(["-s" + self._subunit_file],)
with patch('sys.stdout', new=StringIO()) as mock_stdout:
self.test_object.take_action(parsed_args)
stdout_data = mock_stdout.getvalue()
self._assert_methods_details(stdout_data)
self._assert_no_headers_and_bodies(stdout_data)
def test_take_action_all_stdout(self):
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args(["-as" + self._subunit_file],)
with patch('sys.stdout', new=StringIO()) as mock_stdout:
self.test_object.take_action(parsed_args)
stdout_data = mock_stdout.getvalue()
self._assert_methods_details(stdout_data)
self._assert_headers_and_bodies(stdout_data)
def test_take_action_outfile_files(self):
temp_file = tempfile.mkstemp()[1]
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args(
["-as" + self._subunit_file, '-o', temp_file], )
with patch('sys.stdout', new=StringIO()) as mock_stdout:
self.test_object.take_action(parsed_args)
stdout_data = mock_stdout.getvalue()
self._assert_cli_message(stdout_data)
with open(temp_file, 'r') as file:
data = json.loads(file.read())
self._assert_expect_json(data)
def test_take_action_no_items(self):
temp_file = tempfile.mkstemp()[1]
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args(
["-as" + temp_file], )
with patch('sys.stdout', new=StringIO()) as mock_stdout:
self.test_object.take_action(parsed_args)
stdout_data = mock_stdout.getvalue()
self._assert_cli_message(stdout_data)
def test_take_action_exception(self):
parser = self.test_object.get_parser('NAME')
parsed_args = parser.parse_args(["-s" + self._subunit_file],)
with patch('sys.stderr', new=StringIO()) as mock_stderr:
with patch('tempest.cmd.subunit_describe_calls.entry_point') \
as mock_method:
mock_method.side_effect = OSError()
self.assertRaises(OSError, self.test_object.take_action,
parsed_args)
stderr_data = mock_stderr.getvalue()
self.assertIn("Traceback (most recent call last):", stderr_data)
self.assertIn("entry_point(parsed_args)", stderr_data)