diff options
| author | Ziad Sawalha <github@highbridgellc.com> | 2011-12-20 01:19:13 -0600 |
|---|---|---|
| committer | Ziad Sawalha <github@highbridgellc.com> | 2011-12-23 12:41:32 -0600 |
| commit | a5e7587c8dcb6f89ce49d694c8eca8d3baf8e854 (patch) | |
| tree | d9564d8262fe753e4078971eece58b0ed5de2532 | |
| parent | ed3c5aa9321ca5b1cbcd67651df6a981d4f987cc (diff) | |
Testing Refactor
- this is a squash of 6 commits
- original commits are vailable for cherry-picking here:
https://github.com/ziadsawalha/keystone/commits/tests
Removed test dependency on sampledata
- added fixtures in FunctionalTestCase class
- updated tests to use created fixtures
- only kept boot-strap creations in KeystoneTestCase
Note: This has significantly slowed down tests, but this will
allow us to continue refactoring tests to not go through HTTP
and not require a running server (where not needed).
Backend test suites (SQL, LDAP, etc) can also now be run
separately and in parallel since they don't need a TCP/IP
port. These tests no longer use http(s).
Update to Tenant serialization
- Correctly handle description as an optional attribute
of tenant
- added tests that fail where blank description was coming
back as 'None' string.
- added fixes to handle None correctly (check for None
before calling str(value)
Separated out test suites
- Unit, Functional, and Client (HTTP) suites can be run separately
- -O flag in run_tests now accepts UnitTests and ClientTests as options
- run_tests will run all suites (but end up executing less tests
overall since Unit tests run once and client tests run twice; for
HTTP and HTTPS/SSL tests
Fixed 'python setup.py test' not running
Change-Id: I3505740466abac0fbb35c822858c70a234893798
26 files changed, 962 insertions, 256 deletions
diff --git a/keystone/logic/service.py b/keystone/logic/service.py index c76c971e..5dc6a835 100755 --- a/keystone/logic/service.py +++ b/keystone/logic/service.py @@ -552,7 +552,7 @@ class IdentityService(object): dtenant = api.TENANT.get_by_name(tenant_name) if not dtenant: raise fault.ItemNotFoundFault("The tenant could not be found") - return Tenant(dtenant.id, dtenant.name, dtenant.desc, dtenant.enabled) + return dtenant @staticmethod def update_tenant(admin_token, tenant_id, tenant): diff --git a/keystone/logic/types/tenant.py b/keystone/logic/types/tenant.py index e363f9fc..8ed9fc48 100644 --- a/keystone/logic/types/tenant.py +++ b/keystone/logic/types/tenant.py @@ -58,8 +58,10 @@ class Tenant(object): desc = root.find("{http://docs.openstack.org/identity/api/v2.0}" "description") if desc is None: - raise fault.BadRequestFault("Expecting Tenant Description") - return models.Tenant(id=id, name=name, description=desc.text, + description = None + else: + description = desc.text + return models.Tenant(id=id, name=name, description=description, enabled=set_enabled) except etree.LxmlError as e: raise fault.BadRequestFault("Cannot parse Tenant", str(e)) @@ -86,9 +88,7 @@ class Tenant(object): set_enabled = tenant["enabled"] if not isinstance(set_enabled, bool): raise fault.BadRequestFault("Bad enabled attribute!") - if not "description" in tenant: - raise fault.BadRequestFault("Expecting Tenant Description") - description = tenant["description"] + description = tenant.get("description") return Tenant(id=id, name=name, description=description, enabled=set_enabled) except (ValueError, TypeError) as e: @@ -103,7 +103,8 @@ class Tenant(object): if self.name: dom.set("name", unicode(self.name)) desc = etree.Element("description") - desc.text = unicode(self.description) + if self.description: + desc.text = unicode(self.description) dom.append(desc) return dom @@ -112,8 +113,9 @@ class Tenant(object): def to_dict(self): tenant = { - "description": unicode(self.description), "enabled": self.enabled} + if self.description: + tenant['description'] = unicode(self.description) if self.id: tenant["id"] = unicode(self.id) if self.name: diff --git a/keystone/models.py b/keystone/models.py index c7b52a34..c3b4c5c5 100644 --- a/keystone/models.py +++ b/keystone/models.py @@ -106,7 +106,7 @@ class Resource(dict): if name in self: return self[name] return None - elif name == 'desc': + elif name == 'desc': # TODO(zns): deprecate this # We need to maintain this compatibility with this nasty attribute # until we're done refactoring return self.description @@ -134,7 +134,7 @@ class Resource(dict): if super(Resource, self).__contains__(name): return super(Resource, self).__getitem__(name) return None - elif name == 'desc': + elif name == 'desc': # TODO(zns): deprecate thise # We need to maintain this compatibility with this nasty attribute # until we're done refactoring return self.description @@ -217,7 +217,8 @@ class Resource(dict): if isinstance(value, dict): Resource.write_dict_to_xml(value, element) else: - element.text = str(value) + if value: + element.text = str(value) else: if value is not None: if isinstance(value, dict): diff --git a/keystone/test/__init__.py b/keystone/test/__init__.py index 875410fc..094a8b48 100644 --- a/keystone/test/__init__.py +++ b/keystone/test/__init__.py @@ -47,27 +47,22 @@ test suites""" import cgitb -import gettext import heapq -import logging from nose import config as noseconfig from nose import core from nose import result import optparse import os import sys -import subprocess import tempfile import time import unittest cgitb.enable(format="text") -from functional.common import HttpTestCase import keystone import keystone.server import keystone.version -from keystone.common import config, wsgi -from keystone import backends +from keystone.common import config TEST_DIR = os.path.abspath(os.path.dirname(__file__)) BASE_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir, os.pardir)) @@ -353,17 +348,24 @@ class KeystoneTest(object): """ CONF_PARAMS = {'test_dir': TEST_DIR, 'base_dir': BASE_DIR} isSsl = False + config_name = None + test_files = None + server = None + admin_server = None + conf_fp = None + directory_base = None def clear_database(self): """Remove any test databases or files generated by previous tests.""" - for fname in self.test_files: - paths = [os.path.join(os.curdir, fname), - os.path.join(os.getcwd(), fname), - os.path.join(TEST_DIR, fname)] - for fpath in paths: - if os.path.exists(fpath): - print "Removing test file %s" % fname - os.unlink(fpath) + if self.test_files: + for fname in self.test_files: + paths = [os.path.join(os.curdir, fname), + os.path.join(os.getcwd(), fname), + os.path.join(TEST_DIR, fname)] + for fpath in paths: + if os.path.exists(fpath): + print "Removing test file %s" % fname + os.unlink(fpath) def construct_temp_conf_file(self): """Populates a configuration template, and writes to a file pointer.""" @@ -375,8 +377,12 @@ class KeystoneTest(object): self.conf_fp.flush() def setUp(self): + pass + + def startServer(self): self.server = None self.admin_server = None + self.clear_database() self.construct_temp_conf_file() @@ -426,10 +432,24 @@ class KeystoneTest(object): self.server = service self.admin_server = admin - # Load sample data - from keystone.test import sampledata + # Load bootstrap data + from keystone import manage manage_args = ['--config-file', self.conf_fp.name] - sampledata.load_fixture(args=manage_args) + manage.parse_args(args=manage_args) + #TODO(zns): make this come from config + options['keystone-admin-role'] = 'Admin' + options['keystone-service-admin-role'] = 'KeystoneServiceAdmin' + + #TODO(zns): this should end up being run by a 'bootstrap' script + fixtures = [ + ('role', 'add', options['keystone-admin-role']), + ('user', 'add', 'admin', 'secrete'), + ('role', 'grant', options['keystone-admin-role'], 'admin'), + ('role', 'add', options['keystone-service-admin-role']), + ('role', 'add', 'Member'), + ] + for cmd in fixtures: + manage.process(*cmd) def tearDown(self): # kill the keystone server @@ -441,18 +461,20 @@ class KeystoneTest(object): if self.admin_server is not None: self.admin_server.stop() self.admin_server = None - self.conf_fp.close() - self.conf_fp = None + if self.conf_fp: + self.conf_fp.close() + self.conf_fp = None except Exception as e: print "Error cleaning up %s" % e finally: self.clear_database() - def run(self): + def run(self, args=None): try: self.setUp() # discover and run tests + # TODO(zns): check if we still need a verbosity flag verbosity = 1 if '--verbose' in sys.argv: verbosity = 2 @@ -462,17 +484,28 @@ class KeystoneTest(object): # have to type as much show_elapsed = True argv = [] - for x in sys.argv: - if x.startswith('functional') or x.startswith('unit'): + if args is None: + args = sys.argv + has_base = False + for x in args: + if x.startswith(('functional', 'unit', 'client')): argv.append('keystone.test.%s' % x) + has_base = True elif x.startswith('--hide-elapsed'): show_elapsed = False elif x.startswith('--trace-calls'): pass elif x.startswith('--debug'): pass + elif x.startswith('-'): + argv.append(x) else: argv.append(x) + if x != args[0]: + has_base = True + + if not has_base and self.directory_base is not None: + argv.append(self.directory_base) c = noseconfig.Config(stream=sys.stdout, env=os.environ, @@ -486,7 +519,7 @@ class KeystoneTest(object): show_elapsed=show_elapsed) return not core.run(config=c, testRunner=runner, - argv=argv + ['-P']) + argv=argv + ['--no-path-adjustment']) finally: self.tearDown() @@ -496,10 +529,81 @@ def runtests(): return SQLTest().run() +class UnitTests(KeystoneTest): + """ Class that runs unit tests """ + directory_base = 'unit' + + def run(self): + """ Run unit tests + + Filters arguments and leaves only ones relevant to unit tests + """ + + argv = [] + scoped_to_unit = False + for x in sys.argv: + if x.startswith(('functional', 'client')): + pass + elif x.startswith('unit'): + argv.append('keystone.test.%s' % x) + scoped_to_unit = True + elif x.startswith('--trace-calls'): + pass + elif x.startswith('--debug'): + pass + else: + argv.append(x) + + if not scoped_to_unit: + argv.append('keystone.test.unit') + + super(UnitTests, self).run(args=argv) + + +class ClientTests(KeystoneTest): + """ Class that runs client tests + + Client tests are the tests that need a running http[s] server running + and make web service calls to that server + + """ + config_name = 'sql.conf.template' + directory_base = 'client' + + def run(self): + """ Run client tests + + Filters arguments and leaves only ones relevant to client tests + """ + + argv = [] + scoped_to_client = False + for x in sys.argv: + if x.startswith(('functional', 'unit')): + pass + elif x.startswith('client'): + argv.append('keystone.test.%s' % x) + scoped_to_client = True + elif x.startswith('--trace-calls'): + pass + elif x.startswith('--debug'): + pass + else: + argv.append(x) + + if not scoped_to_client: + argv.append('keystone.test.client') + + self.startServer() + + super(ClientTests, self).run(args=argv) + + class SQLTest(KeystoneTest): """Test defined using only SQLAlchemy back-end""" config_name = 'sql.conf.template' test_files = ('keystone.sqltest.db',) + directory_base = 'functional' def clear_database(self): # Disconnect the database before deleting @@ -509,7 +613,7 @@ class SQLTest(KeystoneTest): super(SQLTest, self).clear_database() -class SSLTest(SQLTest): +class SSLTest(ClientTests): config_name = 'ssl.conf.template' isSsl = True test_files = ('keystone.ssltest.db',) diff --git a/keystone/test/client/__init__.py b/keystone/test/client/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/keystone/test/client/__init__.py diff --git a/keystone/test/functional/test_client.py b/keystone/test/client/test_client.py index 099ab1ff..c1062703 100644 --- a/keystone/test/functional/test_client.py +++ b/keystone/test/client/test_client.py @@ -2,13 +2,14 @@ import unittest import keystone.common.exception import keystone.client -from common import isSsl +from keystone.test.functional.common import isSsl class TestAdminClient(unittest.TestCase): """ Quick functional tests for the Keystone HTTP admin client. """ + use_server = True def setUp(self): """ diff --git a/keystone/test/functional/test_d5_compat_calls.py b/keystone/test/client/test_d5_compat_calls.py index acdec94b..1c3e99c0 100644 --- a/keystone/test/functional/test_d5_compat_calls.py +++ b/keystone/test/client/test_d5_compat_calls.py @@ -22,6 +22,8 @@ from keystone.test.functional import common class D5_AuthenticationTest(common.FunctionalTestCase): """ Tests the functionality of the D5 compat module """ + use_server = True + def setUp(self, *args, **kwargs): super(D5_AuthenticationTest, self).setUp(*args, **kwargs) diff --git a/keystone/test/functional/test_extensions.py b/keystone/test/client/test_extensions.py index de0fe841..551df491 100644 --- a/keystone/test/functional/test_extensions.py +++ b/keystone/test/client/test_extensions.py @@ -3,6 +3,8 @@ from keystone.test.functional import common class TestExtensions(common.FunctionalTestCase): + use_server = True + def test_extensions_json(self): r = self.service_request(path='/extensions.json') self.assertTrue('json' in r.getheader('Content-Type')) @@ -16,6 +18,8 @@ class TestExtensions(common.FunctionalTestCase): class TestAdminExtensions(common.ApiTestCase): + use_server = True + def test_extensions_json(self): r = self.admin_request(path='/extensions.json') self.assertTrue('json' in r.getheader('Content-Type')) diff --git a/keystone/test/client/test_frontends.py b/keystone/test/client/test_frontends.py new file mode 100644 index 00000000..636f6607 --- /dev/null +++ b/keystone/test/client/test_frontends.py @@ -0,0 +1,38 @@ +import unittest2 as unittest +from keystone.test.functional import common + + +class LegacyAuthenticationTest(common.FunctionalTestCase): + use_server = True + + def setUp(self, *args, **kwargs): + super(LegacyAuthenticationTest, self).setUp(*args, **kwargs) + + password = common.unique_str() + self.tenant = self.create_tenant().json['tenant'] + self.user = self.create_user(user_password=password, + tenant_id=self.tenant['id']).json['user'] + self.user['password'] = password + + self.services = {} + self.endpoint_templates = {} + for x in range(5): + self.services[x] = self.create_service().json['OS-KSADM:service'] + self.endpoint_templates[x] = self.create_endpoint_template( + name=self.services[x]['name'], \ + type=self.services[x]['type']).\ + json['OS-KSCATALOG:endpointTemplate'] + self.create_endpoint_for_tenant(self.tenant['id'], + self.endpoint_templates[x]['id']) + + def test_authenticate_legacy(self): + r = self.service_request(version='1.0', assert_status=204, headers={ + "X-Auth-User": self.user['name'], + "X-Auth-Key": self.user['password']}) + + self.assertIsNotNone(r.getheader('x-auth-token')) + for service in self.services.values(): + self.assertIsNotNone(r.getheader('x-' + service['name'])) + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/functional/test_middleware.py b/keystone/test/client/test_middleware.py index e8ad55f0..1526944e 100644 --- a/keystone/test/functional/test_middleware.py +++ b/keystone/test/client/test_middleware.py @@ -51,6 +51,9 @@ class TestQuantumMiddleware(common.MiddlewareTestCase): """ def setUp(self): + access = self.authenticate(self.admin_username, self.admin_password).\ + json['access'] + self.admin_token = access['token']['id'] settings = {'delay_auth_decision': '0', 'auth_host': '127.0.0.1', 'auth_port': '35357', @@ -85,7 +88,7 @@ except ImportError as e: # 'auth_port': '35357', # 'auth_protocol': 'http', # 'auth_uri': 'http://localhost:35357/', -# 'admin_token': '999888777666', +# 'admin_token': self.admin_token, # 'set log_facility': 'LOG_NULL'} # super(TestSwiftMiddleware, self).setUp(swift_auth, settings) diff --git a/keystone/test/functional/test_request_specs.py b/keystone/test/client/test_request_specs.py index bb4513d0..74494056 100644 --- a/keystone/test/functional/test_request_specs.py +++ b/keystone/test/client/test_request_specs.py @@ -4,6 +4,7 @@ from keystone.test.functional import common class TestUrlHandling(common.FunctionalTestCase): """Tests API's global URL handling behaviors""" + use_server = True def test_optional_trailing_slash(self): """Same response returned regardless of a trailing slash in the url.""" @@ -14,6 +15,7 @@ class TestUrlHandling(common.FunctionalTestCase): class TestContentTypes(common.FunctionalTestCase): """Tests API's Content-Type handling""" + use_server = True def test_default_content_type(self): """Service returns JSON without being asked to""" diff --git a/keystone/test/client/test_static_files.py b/keystone/test/client/test_static_files.py new file mode 100644 index 00000000..df3bc620 --- /dev/null +++ b/keystone/test/client/test_static_files.py @@ -0,0 +1,94 @@ +import unittest2 as unittest +from keystone.test.functional import common + + +class TestStaticFiles(common.ApiTestCase): + use_server = True + + def test_pdf_contract(self): + if not common.isSsl(): + #TODO(ziad): Caller hangs in SSL (but works with cURL) + r = self.service_request(path='/identitydevguide.pdf') + self.assertTrue('pdf' in r.getheader('Content-Type')) + + def test_wadl_contract(self): + r = self.service_request(path='/identity.wadl') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_wadl_common(self): + r = self.service_request(path='/common.ent') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_xsd_contract(self): + r = self.service_request(path='/xsd/api.xsd') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_xsd_atom_contract(self): + r = self.service_request(path='/xsd/atom/atom.xsd') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_xslt(self): + r = self.service_request(path='/xslt/schema.xslt') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_js(self): + r = self.service_request(path='/js/shjs/sh_java.js') + self.assertTrue('javascript' in r.getheader('Content-Type')) + + def test_xml_sample(self): + r = self.service_request(path='/samples/auth.xml') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_json_sample(self): + r = self.service_request(path='/samples/auth.json') + self.assertTrue('json' in r.getheader('Content-Type')) + + def test_stylesheet(self): + r = self.service_request(path='/style/shjs/sh_acid.css') + self.assertTrue('css' in r.getheader('Content-Type')) + + +class TestAdminStaticFiles(common.FunctionalTestCase): + use_server = True + + def test_pdf_contract(self): + if not common.isSsl(): + #TODO(ziad): Caller hangs in SSL (but works with cURL) + r = self.admin_request(path='/identityadminguide.pdf') + self.assertTrue('pdf' in r.getheader('Content-Type')) + + def test_wadl_contract(self): + r = self.admin_request(path='/identity-admin.wadl') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_xsd_contract(self): + r = self.admin_request(path='/xsd/api.xsd') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_xsd_atom_contract(self): + r = self.admin_request(path='/xsd/atom/atom.xsd') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_xslt(self): + r = self.admin_request(path='/xslt/schema.xslt') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_js(self): + r = self.admin_request(path='/js/shjs/sh_java.js') + self.assertTrue('javascript' in r.getheader('Content-Type')) + + def test_xml_sample(self): + r = self.admin_request(path='/samples/auth.xml') + self.assertTrue('xml' in r.getheader('Content-Type')) + + def test_json_sample(self): + r = self.admin_request(path='/samples/auth.json') + self.assertTrue('json' in r.getheader('Content-Type')) + + def test_stylesheet(self): + r = self.admin_request(path='/style/shjs/sh_acid.css') + self.assertTrue('css' in r.getheader('Content-Type')) + + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/functional/common.py b/keystone/test/functional/common.py index 0e9bbaa6..e16bf538 100644 --- a/keystone/test/functional/common.py +++ b/keystone/test/functional/common.py @@ -1,11 +1,18 @@ -import unittest2 as unittest +import datetime import httplib -import uuid import json +import logging import os +import unittest2 as unittest +import uuid from webob import Request, Response from xml.etree import ElementTree +from keystone import server +import keystone.backends.api as db_api + +logger = logging.getLogger('test.functional.common') + def isSsl(): """ See if we are testing with SSL. If cert is non-empty, we are! """ @@ -44,7 +51,13 @@ class HttpTestCase(unittest.TestCase): # Retrieve the response so can go ahead and close the connection response = connection.getresponse() + logger.debug("%s %s returned %s", method, path, response.status) + response.body = response.read() + if response.status != httplib.OK: + logger.debug("Response Body:") + for line in response.body.split("\n"): + logger.debug(line) # Close the connection connection.close() @@ -183,10 +196,223 @@ class RestfulTestCase(HttpTestCase): class ApiTestCase(RestfulTestCase): """Abstracts REST verbs & resources of the service & admin API.""" + use_server = False + + admin_role_name = 'Admin' + service_admin_role_name = 'KeystoneServiceAdmin' + member_role_name = 'Member' + + # Same as KeystoneTest settings + admin_username = 'admin' + admin_password = 'secrete' service_token = None admin_token = None + service_api = None + admin_api = None + + """ + Dict of configuration options to pass to the API controller + """ + options = { + 'backends': "keystone.backends.sqlalchemy", + 'keystone.backends.sqlalchemy': { + # in-memory db + 'sql_connection': 'sqlite://', + 'verbose': False, + 'debug': False, + 'backend_entities': + "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " + "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " + "'Token', 'Service']", + }, + 'extensions': 'osksadm,oskscatalog', + 'keystone-admin-role': 'Admin', + 'keystone-service-admin-role': 'KeystoneServiceAdmin', + 'hash-password': 'True', + } + + def fixture_create_role(self, **kwargs): + """ + Creates a role fixture. + + :params \*\*kwargs: Attributes of the role to create + """ + values = kwargs.copy() + role = db_api.ROLE.create(values) + logger.debug("Created role fixture %s (id=%s)", role.name, role.id) + return role + + def fixture_create_token(self, **kwargs): + """ + Creates a token fixture. + + :params \*\*kwargs: Attributes of the token to create + """ + values = kwargs.copy() + token = db_api.TOKEN.create(values) + logger.debug("Created token fixture %s", token.id) + return token + + def fixture_create_tenant(self, **kwargs): + """ + Creates a tenant fixture. + + :params \*\*kwargs: Attributes of the tenant to create + """ + values = kwargs.copy() + tenant = db_api.TENANT.create(values) + logger.debug("Created tenant fixture %s (id=%s)", tenant.name, + tenant.id) + return tenant + + def fixture_create_user(self, **kwargs): + """ + Creates a user fixture. If the user's tenant ID is set, and the tenant + does not exist in the database, the tenant is created. + + :params \*\*kwargs: Attributes of the user to create + """ + values = kwargs.copy() + tenant_name = values.get('tenant_name') + if tenant_name: + if not db_api.TENANT.get_by_name(tenant_name): + tenant = db_api.TENANT.create({'name': tenant_name, + 'enabled': True, + 'desc': tenant_name}) + values['tenant_id'] = tenant.id + user = db_api.USER.create(values) + logger.debug("Created user fixture %s (id=%s)", user.name, user.id) + return user + + def setUp(self): + if self.use_server: + return + + self.service_api = server.ServiceApi(self.options) + self.admin_api = server.AdminApi(self.options) + + # ADMIN ROLE + self.admin_role = self.fixture_create_role( + name=self.admin_role_name) + + # ADMIN + password = unique_str() + self.admin_user = self.fixture_create_user( + name="admin-user-%s" % uuid.uuid4().hex, enabled=True, + password=password) + self.admin_user['password'] = password + self.admin_password = password + self.admin_username = self.admin_user['name'] + + obj = {} + obj['role_id'] = self.admin_role['id'] + obj['user_id'] = self.admin_user['id'] + obj['tenant_id'] = None + result = db_api.USER.user_role_add(obj) + logger.debug("Created grant fixture %s", result.id) + + # SERVICE ADMIN ROLE + self.service_admin_role = self.fixture_create_role( + name=self.service_admin_role_name) + + # MEMBER ROLE + self.member_role = self.fixture_create_role( + name='Member') + + def tearDown(self): + pass + + def request(self, host='127.0.0.1', port=80, method='GET', path='/', + headers=None, body=None, assert_status=None, server=None): + """Overrides HttpTestCase and uses local calls""" + if self.use_server: + # Call a real server (bypass the override) + return super(ApiTestCase, self).request(host=host, port=port, + method=method, path=path, headers=headers, + body=body, assert_status=assert_status) + req = Request.blank(path) + req.method = method + req.headers = headers + if isinstance(body, unicode): + req.body = body.encode('utf-8') + else: + req.body = body + + res = req.get_response(server) + logger.debug("%s %s returned %s", req.method, req.path_qs, + res.status) + if res.status_int != httplib.OK: + logger.debug("Response Body:") + for line in res.body.split("\n"): + logger.debug(line) + + # Automatically assert HTTP status code + if assert_status: + self.assertEqual(res.status_int, assert_status, + 'Status code %s is not %s, as expected)\n\n%s' % + (res.status_int, assert_status, res.body)) + else: + self.assertTrue(299 >= res.status_int >= 200, + 'Status code %d is outside of the expected range (2xx)\n\n%s' % + (res.status_int, res.body)) + + # Contains the response headers, body, etc + return res + + def _decode_response_body(self, response): + """Override to support webob.Response. + """ + if self.use_server: + # Call a real server (bypass the override) + return super(ApiTestCase, self)._decode_response_body(response) + + if response.body != None and response.body.strip(): + if 'application/json' in response.content_type: + response.json = self._decode_json(response.body) + elif 'application/xml' in response.content_type: + response.xml = self._decode_xml(response.body) + return response + + def assertResponseSuccessful(self, response): + """Asserts that a status code lies inside the 2xx range + + :param response: :py:class:`webob.Response` to be + verified to have a status code between 200 and 299. + + example:: + + >>> self.assertResponseSuccessful(response, 203) + """ + if self.use_server: + # Call a real server (bypass the override) + return super(ApiTestCase, self).assertResponseSuccessful(response) + + self.assertTrue(response.status_int >= 200 and + response.status_int <= 299, + 'Status code %d is outside of the expected range (2xx)\n\n%s' % + (response.status_int, response.body)) + + def assertResponseStatus(self, response, assert_status): + """Asserts a specific status code on the response + + :param response: :py:class:`webob.Response` + :param assert_status: The specific ``status`` result expected + + example:: + + >>> self.assertResponseStatus(response, 203) + """ + if self.use_server: + # Call a real server (bypass the override) + return super(ApiTestCase, self).assertResponseStatus(response, + assert_status) + + self.assertEqual(response.status_int, assert_status, + 'Status code %s is not %s, as expected)\n\n%s' % + (response.status_int, assert_status, response.body)) + def service_request(self, version='2.0', path='', port=5000, headers=None, **kwargs): """Returns a request to the service API""" @@ -194,7 +420,8 @@ class ApiTestCase(RestfulTestCase): # Initialize headers dictionary headers = {} if not headers else headers - path = ApiTestCase._version_path(version, path) + if self.use_server: + path = ApiTestCase._version_path(version, path) if self.service_token: headers['X-Auth-Token'] = self.service_token @@ -202,7 +429,7 @@ class ApiTestCase(RestfulTestCase): headers['X-Auth-Token'] = self.admin_token return self.restful_request(port=port, path=path, headers=headers, - **kwargs) + server=self.service_api, **kwargs) def admin_request(self, version='2.0', path='', port=35357, headers=None, **kwargs): @@ -211,13 +438,14 @@ class ApiTestCase(RestfulTestCase): # Initialize headers dictionary headers = {} if not headers else headers - path = ApiTestCase._version_path(version, path) + if self.use_server: + path = ApiTestCase._version_path(version, path) if self.admin_token: headers['X-Auth-Token'] = self.admin_token return self.restful_request(port=port, path=path, headers=headers, - **kwargs) + server=self.admin_api, **kwargs) @staticmethod def _version_path(version, path): @@ -359,13 +587,13 @@ class ApiTestCase(RestfulTestCase): def put_user_role(self, user_id, role_id, tenant_id, **kwargs): if tenant_id is None: - """PUT /users/{user_id}/roles/OS-KSADM/{role_id}""" + # PUT /users/{user_id}/roles/OS-KSADM/{role_id} return self.admin_request(method='PUT', path='/users/%s/roles/OS-KSADM/%s' % (user_id, role_id), **kwargs) else: - """PUT /tenants/{tenant_id}/users/{user_id}/ - roles/OS-KSADM/{role_id}""" + # PUT /tenants/{tenant_id}/users/{user_id}/ + # roles/OS-KSADM/{role_id} return self.admin_request(method='PUT', path='/tenants/%s/users/%s/roles/OS-KSADM/%s' % (tenant_id, user_id, role_id,), **kwargs) @@ -574,7 +802,7 @@ class ApiTestCase(RestfulTestCase): path='/users/%s/OS-KSADM/credentials/%s' %\ (user_id, credentials_type), **kwargs) - def delete_user_credentials_by_type(self, user_id,\ + def delete_user_credentials_by_type(self, user_id, credentials_type, **kwargs): """DELETE /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" return self.admin_request(method='DELETE', @@ -602,16 +830,24 @@ optional_url = lambda x: x if x is not None else unique_url() class FunctionalTestCase(ApiTestCase): """Abstracts functional CRUD of the identity API""" - service_token = None + admin_user_id = None admin_token = None - admin_user_id = None - admin_username = 'admin' - admin_password = 'secrete' + service_token = None + expired_admin_token = None + disabled_admin_token = None + service_admin_token = None - expired_admin_token = '000999' - disabled_admin_token = '999888777' - service_admin_token = '111222333444' + user = None + user_token = None + service_user = None + + tenant = None + tenant_user = None # user with default tenant + tenant_user_token = None + + disabled_tenant = None + disabled_user = None xmlns = 'http://docs.openstack.org/identity/api/v2.0' xmlns_ksadm = 'http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0' @@ -620,6 +856,8 @@ class FunctionalTestCase(ApiTestCase): def setUp(self): """Prepare keystone for system tests""" + super(FunctionalTestCase, self).setUp() + # Authenticate as admin user to establish admin_token access = self.authenticate(self.admin_username, self.admin_password).\ json['access'] @@ -627,6 +865,100 @@ class FunctionalTestCase(ApiTestCase): self.admin_token = access['token']['id'] self.admin_user_id = access['user']['id'] + def fixture_create_service_admin(self): + if self.service_user: + return + # SERVICE ADMIN + password = unique_str() + self.service_user = self.fixture_create_user( + name="service-user-%s" % uuid.uuid4().hex, enabled=True, + password=password) + self.service_user['password'] = password + + self.service_admin_role = self.fetch_role_by_name( + self.service_admin_role_name, + assert_status=200).json['role'] + self.grant_global_role_to_user(self.service_user['id'], + self.service_admin_role['id'], + assert_status=201) + + self.service_user_token = self.authenticate(self.service_user['name'], + self.service_user['password']).\ + json['access']['token'] + self.service_admin_token = self.service_user_token['id'] + + def fixture_create_normal_tenant(self): + if self.tenant: + return + # TENANT + self.tenant = self.fixture_create_tenant( + name="tenant-%s" % uuid.uuid4().hex, enabled=True) + + def fixture_create_disabled_tenant(self): + if self.disabled_tenant: + return + # DISABLED TENANT + self.disabled_tenant = self.fixture_create_tenant( + name="disabled-tenant-%s" % uuid.uuid4().hex, enabled=False) + + def fixture_create_normal_user(self): + if self.user: + return + # USER + password = unique_str() + self.user = self.fixture_create_user( + name="user-%s" % uuid.uuid4().hex, enabled=True, + password=password) + self.user['password'] = password + + self.user_token = self.authenticate(self.user['name'], + self.user['password']).\ + json['access']['token'] + + def fixture_create_tenant_user(self): + if self.tenant_user: + return + self.fixture_create_tenant() + # USER with DEFAULT TENANT + password = unique_str() + self.tenant_user = self.fixture_create_user( + name="user_in_tenant-%s" % uuid.uuid4().hex, enabled=True, + tenant_id=self.tenant.id, password=password) + self.tenant_user['password'] = password + + self.tenant_user_token = self.authenticate(self.tenant_user['name'], + self.tenant_user['password'], + self.tenant.id).\ + json['access']['token'] + + def fixture_create_disabled_user_and_token(self): + if self.disabled_user: + return + self.fixture_create_normal_tenant() + # DISABLED USER + self.disabled_user = self.fixture_create_user( + name="disabled_user-%s" % uuid.uuid4().hex, enabled=False) + + # TOKEN for DISABLED user + token = self.fixture_create_token( + id="disabled-user-tenant-token-%s" % uuid.uuid4().hex, + user_id=self.disabled_user.id, + tenant_id=self.tenant.id, + expires=datetime.datetime.now() + datetime.timedelta(1)) + self.disabled_admin_token = token.id + + def fixture_create_expired_token(self): + if self.expired_admin_token: + return + self.fixture_create_normal_tenant() + # EXPIRED token (for enabled user) + token = self.fixture_create_token( + id="expired-admin-token-%s" % uuid.uuid4().hex, + user_id=self.admin_user_id, + tenant_id=self.tenant.id, + expires=datetime.datetime.now() - datetime.timedelta(1)) + self.expired_admin_token = token.id + def authenticate(self, user_name=None, user_password=None, tenant_id=None, **kwargs): user_name = optional_str(user_name) @@ -1152,6 +1484,7 @@ class MiddlewareTestCase(FunctionalTestCase): """ Base class to run tests for Keystone WSGI middleware. """ + use_server = True def setUp(self, middleware, settings=None): super(MiddlewareTestCase, self).setUp() @@ -1161,7 +1494,7 @@ class MiddlewareTestCase(FunctionalTestCase): 'auth_port': '35357', 'auth_protocol': 'http', 'auth_uri': 'http://localhost:35357/', - 'admin_token': '999888777666', + 'admin_token': self.admin_token, 'auth_admin_user': self.admin_username, 'auth_admin_password': self.admin_password} cert_file = isSsl() @@ -1178,11 +1511,21 @@ class MiddlewareTestCase(FunctionalTestCase): self.test_middleware = \ middleware.filter_factory(settings)(HeaderApp()) + name = unique_str() + r = self.create_tenant(tenant_name=name, assert_status=201) + self.tenant = r.json.get('tenant') + + user_name = unique_str() password = unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password + r = self.create_user(user_name=user_name, + user_password=password, + tenant_id=self.tenant['id']) + self.tenant_user = r.json.get('user') + self.tenant_user['password'] = password + + access = self.authenticate(user_name, password).\ + json['access'] + self.tenant_user_token = access['token'] self.services = {} self.endpoint_templates = {} @@ -1195,10 +1538,6 @@ class MiddlewareTestCase(FunctionalTestCase): self.create_endpoint_for_tenant(self.tenant['id'], self.endpoint_templates[x]['id']) - r = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - self.user_token = r.json['access']['token']['id'] - def test_401_without_token(self): resp = Request.blank('/').get_response(self.test_middleware) self.assertEquals(resp.status_int, 401) @@ -1219,7 +1558,7 @@ class MiddlewareTestCase(FunctionalTestCase): def test_200_good_token(self): resp = Request.blank('/', - headers={'X-Auth-Token': self.user_token}) \ + headers={'X-Auth-Token': self.tenant_user_token['id']}) \ .get_response(self.test_middleware) self.assertEquals(resp.status_int, 200) @@ -1229,10 +1568,10 @@ class MiddlewareTestCase(FunctionalTestCase): header = "HTTP_X_IDENTITY_STATUS: Confirmed" self.assertTrue(header in headers, "Missing %s" % header) - header = "HTTP_X_USER_ID: %s" % self.user['id'] + header = "HTTP_X_USER_ID: %s" % self.tenant_user['id'] self.assertTrue(header in headers, "Missing %s" % header) - header = "HTTP_X_USER_NAME: %s" % self.user['name'] + header = "HTTP_X_USER_NAME: %s" % self.tenant_user['name'] self.assertTrue(header in headers, "Missing %s" % header) header = "HTTP_X_TENANT_ID: %s" % self.tenant['id'] @@ -1245,5 +1584,5 @@ class MiddlewareTestCase(FunctionalTestCase): header = "HTTP_X_TENANT: %s" % self.tenant['id'] self.assertTrue(header in headers, "Missing %s" % header) - header = "HTTP_X_USER: %s" % self.user['id'] + header = "HTTP_X_USER: %s" % self.tenant_user['id'] self.assertTrue(header in headers, "Missing %s" % header) diff --git a/keystone/test/functional/test_auth.py b/keystone/test/functional/test_auth.py index ea4baa53..0846a5ef 100644 --- a/keystone/test/functional/test_auth.py +++ b/keystone/test/functional/test_auth.py @@ -5,10 +5,6 @@ from keystone.test.functional import common class TestAdminAuthentication(common.FunctionalTestCase): """Test admin-side user authentication""" - def setUp(self): - """Empty method to prevent KeystoneTestCase from authenticating""" - pass - def test_bootstrapped_admin_user(self): """Bootstrap script should create an 'admin' user with 'Admin' role""" # Authenticate as admin diff --git a/keystone/test/functional/test_authentication.py b/keystone/test/functional/test_authentication.py index d5fc0e03..86789c99 100644 --- a/keystone/test/functional/test_authentication.py +++ b/keystone/test/functional/test_authentication.py @@ -314,37 +314,6 @@ class AdminUserAuthenticationTest(common.FunctionalTestCase): self.check_urls_for_admin_user_xml(service_catalog) -class LegacyAuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(LegacyAuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate_legacy(self): - r = self.service_request(version='1.0', assert_status=204, headers={ - "X-Auth-User": self.user['name'], - "X-Auth-Key": self.user['password']}) - - self.assertIsNotNone(r.getheader('x-auth-token')) - for service in self.services.values(): - self.assertIsNotNone(r.getheader('x-' + service['name'])) - - class MultiTokenTest(common.FunctionalTestCase): def setUp(self, *args, **kwargs): super(MultiTokenTest, self).setUp(*args, **kwargs) diff --git a/keystone/test/functional/test_credentials.py b/keystone/test/functional/test_credentials.py index d13248aa..2db01579 100644 --- a/keystone/test/functional/test_credentials.py +++ b/keystone/test/functional/test_credentials.py @@ -7,8 +7,7 @@ class TestGetCredentials(common.FunctionalTestCase): def setUp(self): super(TestGetCredentials, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] + self.fixture_create_normal_user() def test_get_user_credentials(self): password_credentials = self.fetch_user_credentials( @@ -26,14 +25,17 @@ class TestGetCredentials(common.FunctionalTestCase): password_credentials.get('username'), self.user['name']) def test_get_user_credentials_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_user_credentials(self.user['id'], assert_status=403) def test_get_user_credentials_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_user_credentials(self.user['id'], assert_status=403) def test_get_user_credentials_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_user_credentials(self.user['name'], assert_status=403) @@ -51,8 +53,7 @@ class TestGetPasswordCredentials(common.FunctionalTestCase): def setUp(self): super(TestGetPasswordCredentials, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] + self.fixture_create_normal_user() def test_get_user_credentials(self): password_credentials = self.fetch_password_credentials( @@ -68,14 +69,17 @@ class TestGetPasswordCredentials(common.FunctionalTestCase): password_credentials.get('username'), self.user['name']) def test_get_user_credentials_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_password_credentials(self.user['id'], assert_status=403) def test_get_user_credentials_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_password_credentials(self.user['id'], assert_status=403) def test_get_user_credentials_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_password_credentials(self.user['name'], assert_status=403) @@ -93,9 +97,7 @@ class TestCreatePasswordCredentials(common.FunctionalTestCase): def setUp(self): super(TestCreatePasswordCredentials, self).setUp() - password = common.unique_str() - self.user = self.create_user( - user_password=password).json['user'] + self.fixture_create_normal_user() self.delete_user_credentials_by_type( self.user['id'], 'passwordCredentials') @@ -130,6 +132,7 @@ class TestCreatePasswordCredentials(common.FunctionalTestCase): assert_status=400) def test_create_password_credentials_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_password_credentials(self.user['id'], self.user['name'], assert_status=403) @@ -150,8 +153,7 @@ class TestUpdatePasswordCredentials(common.FunctionalTestCase): def setUp(self): super(TestUpdatePasswordCredentials, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] + self.fixture_create_normal_user() def test_update_password_credentials(self): self.update_password_credentials(self.user['id'], self.user['name'], @@ -166,6 +168,7 @@ class TestUpdatePasswordCredentials(common.FunctionalTestCase): as_xml=data, assert_status=200) def test_update_password_credentials_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.update_password_credentials(self.user['id'], self.user['name'], assert_status=403) @@ -186,14 +189,14 @@ class TestDeletePasswordCredentials(common.FunctionalTestCase): def setUp(self): super(TestDeletePasswordCredentials, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] + self.fixture_create_normal_user() def test_delete_password_credentials(self): self.delete_password_credentials(self.user['id'], assert_status=204) def test_delete_password_credentials_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.delete_password_credentials(self.user['id'], assert_status=403) @@ -213,8 +216,7 @@ class TestAuthentication(common.FunctionalTestCase): """Test authentication after a password update.""" def setUp(self): super(TestAuthentication, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] + self.fixture_create_normal_user() def test_authentication_after_password_change(self): self.authenticate(self.user['name'], self.user['password'], diff --git a/keystone/test/functional/test_endpoints.py b/keystone/test/functional/test_endpoints.py index a02ba830..9f7c989e 100644 --- a/keystone/test/functional/test_endpoints.py +++ b/keystone/test/functional/test_endpoints.py @@ -29,6 +29,7 @@ class EndpointTemplatesTest(common.FunctionalTestCase): type=self.service['type']).\ json['OS-KSCATALOG:endpointTemplate'] + self.fixture_create_service_admin() admin_token = self.admin_token self.admin_token = self.service_admin_token self.my_service = self.create_service().json['OS-KSADM:service'] @@ -156,10 +157,12 @@ class GetEndpointTemplatesTest(EndpointTemplatesTest): self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) def test_get_endpoint_templates_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_endpoint_templates(assert_status=403) def test_get_endpoint_templates_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.list_endpoint_templates(assert_status=403) @@ -178,11 +181,13 @@ class GetEndpointTemplatesTest(EndpointTemplatesTest): "{%s}endpointTemplates" % self.xmlns_kscatalog) def test_get_endpoint_templates_xml_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_endpoint_templates(assert_status=403, headers={ 'Accept': 'application/xml'}) def test_get_endpoint_templates_xml_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.get_endpoint_templates(assert_status=403, headers={ 'Accept': 'application/xml'}) @@ -222,11 +227,13 @@ class GetEndpointTemplatesByServiceTest(EndpointTemplatesTest): self.service['type']) def test_get_endpoint_templates_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_endpoint_templates( service_id=self.service['id'], assert_status=403) def test_get_endpoint_templates_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.list_endpoint_templates( service_id=self.service['id'], assert_status=403) @@ -254,12 +261,14 @@ class GetEndpointTemplatesByServiceTest(EndpointTemplatesTest): self.assertEqual(endpoint_template.get('type'), self.service['type']) def test_get_endpoint_templates_xml_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_endpoint_templates_by_service( service_id=self.service['id'], assert_status=403, headers={ 'Accept': 'application/xml'}) def test_get_endpoint_templates_xml_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.get_endpoint_templates_by_service( service_id=self.service['id'], assert_status=403, headers={ @@ -289,11 +298,13 @@ class GetEndpointTemplateTest(EndpointTemplatesTest): # self.assertIsNotNone(r.json['endpointTemplate']) def test_get_endpoint_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_endpoint_template(self.endpoint_template['id'], assert_status=403) def test_get_endpoint_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_endpoint_template(self.endpoint_template['id'], assert_status=403) @@ -405,6 +416,7 @@ class UpdateEndpointTemplateTest(EndpointTemplatesTest): # self.test_update_endpoint_xml() def test_update_endpoint_template_with_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.update_endpoint_template(self.endpoint_template['id'], assert_status=403) @@ -415,6 +427,7 @@ class UpdateEndpointTemplateTest(EndpointTemplatesTest): assert_status=401) def test_update_endpoint_template_with_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.update_endpoint_template(self.endpoint_template['id'], assert_status=403) @@ -428,18 +441,20 @@ class UpdateEndpointTemplateTest(EndpointTemplatesTest): self.update_endpoint_template(assert_status=404) -class CreateEndpointRefsTest(EndpointTemplatesTest): +class CreateEndpointsTest(EndpointTemplatesTest): def setUp(self, *args, **kwargs): - super(CreateEndpointRefsTest, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] + super(CreateEndpointsTest, self).setUp(*args, **kwargs) + self.fixture_create_normal_tenant() def test_endpoint_create_json_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.create_endpoint_for_tenant(self.tenant['id'], self.endpoint_template['id'], assert_status=403) def test_endpoint_create_json_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_endpoint_for_tenant(self.tenant['id'], self.endpoint_template['id'], assert_status=403) @@ -496,6 +511,7 @@ class CreateEndpointRefsTest(EndpointTemplatesTest): self.endpoint_template["internalURL"]) def test_endpoint_create_xml_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token data = ('<?xml version="1.0" encoding="UTF-8"?> ' '<endpointTemplate ' @@ -512,6 +528,7 @@ class CreateEndpointRefsTest(EndpointTemplatesTest): 'Accept': 'application/xml'}) def test_endpoint_create_xml_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token data = ('<?xml version="1.0" encoding="UTF-8"?> ' '<endpointTemplate ' @@ -555,22 +572,23 @@ class CreateEndpointRefsTest(EndpointTemplatesTest): 'Accept': 'application/xml'}) -class GetEndPointTest(EndpointTemplatesTest): +class GetEndpointsTest(EndpointTemplatesTest): def setUp(self, *args, **kwargs): - super(GetEndPointTest, self).setUp(*args, **kwargs) - - self.tenant = self.create_tenant().json['tenant'] + super(GetEndpointsTest, self).setUp(*args, **kwargs) + self.fixture_create_normal_tenant() def test_get_tenant_endpoint_xml(self): self.get_tenant_endpoints(self.tenant['id'], assert_status=200, headers={"Accept": "application/xml"}) def test_get_tenant_endpoint_xml_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_tenant_endpoints(self.tenant['id'], assert_status=403, headers={"Accept": "application/xml"}) def test_get_tenant_endpoint_xml_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.get_tenant_endpoints(self.tenant['id'], assert_status=403, headers={"Accept": "application/xml"}) @@ -590,10 +608,12 @@ class GetEndPointTest(EndpointTemplatesTest): self.assertIsNotNone(r.json.get('endpoints'), r.json) def test_get_tenant_endpoint_json_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_tenant_endpoints(self.tenant['id'], assert_status=403) def test_get_endpoint_json_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.get_tenant_endpoints(self.tenant['id'], assert_status=403) @@ -609,8 +629,7 @@ class GetEndPointTest(EndpointTemplatesTest): class DeleteEndpointsTest(EndpointTemplatesTest): def setUp(self, *args, **kwargs): super(DeleteEndpointsTest, self).setUp(*args, **kwargs) - - self.tenant = self.create_tenant().json['tenant'] + self.fixture_create_normal_tenant() self.create_endpoint_for_tenant(self.tenant['id'], self.endpoint_template['id']) @@ -619,11 +638,13 @@ class DeleteEndpointsTest(EndpointTemplatesTest): self.endpoint_template['id'], assert_status=204) def test_delete_endpoint_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.delete_tenant_endpoint(self.tenant['id'], self.endpoint_template['id'], assert_status=403) def test_delete_endpoint_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.delete_tenant_endpoint(self.tenant['id'], self.endpoint_template['id'], assert_status=403) @@ -639,14 +660,11 @@ class DeleteEndpointsTest(EndpointTemplatesTest): self.endpoint_template['id'], assert_status=401) -class GetEndPointsForTokenTest(EndpointTemplatesTest): +class GetEndpointsForTokenTest(EndpointTemplatesTest): def setUp(self, *args, **kwargs): - super(GetEndPointsForTokenTest, self).setUp(*args, **kwargs) - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password + super(GetEndpointsForTokenTest, self).setUp(*args, **kwargs) + self.fixture_create_normal_tenant() + self.fixture_create_tenant_user() self.services = {} self.endpoint_templates = {} @@ -658,53 +676,64 @@ class GetEndPointsForTokenTest(EndpointTemplatesTest): json['OS-KSCATALOG:endpointTemplate'] self.create_endpoint_for_tenant(self.tenant['id'], self.endpoint_templates[x]['id']) - r = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - self.token = r.json['access']['token']['id'] def test_get_token_endpoints_xml(self): - self.get_token_endpoints(self.token, assert_status=200, - headers={"Accept": "application/xml"}) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=200, + headers={"Accept": "application/xml"}) def test_get_token_endpoints_xml_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.get_token_endpoints(self.token, assert_status=403, - headers={"Accept": "application/xml"}) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=403, + headers={"Accept": "application/xml"}) def test_get_token_endpoints_xml_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.get_token_endpoints(self.token, assert_status=403, - headers={"Accept": "application/xml"}) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=403, + headers={"Accept": "application/xml"}) def test_get_token_endpoints_xml_using_missing_auth_token(self): self.admin_token = '' - self.get_token_endpoints(self.token, assert_status=401, - headers={"Accept": "application/xml"}) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=401, + headers={"Accept": "application/xml"}) def test_get_token_endpoints_xml_using_invalid_auth_token(self): self.admin_token = common.unique_str() - self.get_token_endpoints(self.token, assert_status=401, - headers={"Accept": "application/xml"}) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=401, + headers={"Accept": "application/xml"}) def test_get_token_endpoints_json(self): - r = self.get_token_endpoints(self.token, assert_status=200) + r = self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=200) self.assertIsNotNone(r.json.get('endpoints'), r.json) def test_get_token_endpoints_json_using_expired_auth_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.get_token_endpoints(self.token, assert_status=403) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=403) def test_get_token_endpoints_json_using_disabled_auth_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.get_token_endpoints(self.token, assert_status=403) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=403) def test_get_token_endpoints_json_using_missing_auth_token(self): self.admin_token = '' - self.get_token_endpoints(self.token, assert_status=401) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=401) def test_get_token_endpoints_json_using_invalid_auth_token(self): self.admin_token = common.unique_str() - self.get_token_endpoints(self.token, assert_status=401) + self.get_token_endpoints(self.tenant_user_token['id'], + assert_status=401) if __name__ == '__main__': diff --git a/keystone/test/functional/test_roles.py b/keystone/test/functional/test_roles.py index 6950e7d6..9b04618d 100644 --- a/keystone/test/functional/test_roles.py +++ b/keystone/test/functional/test_roles.py @@ -42,6 +42,7 @@ class CreateRolesTest(RolesTest): self.create_role(assert_status=401) def test_create_roles_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_role(assert_status=403) @@ -50,6 +51,7 @@ class CreateRolesTest(RolesTest): self.create_role(assert_status=401) def test_create_roles_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.create_role(assert_status=403) @@ -102,6 +104,7 @@ class DeleteRoleTest(RolesTest): self.role = self.create_role().json['role'] def test_delete_roles_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.delete_role(self.role['id'], assert_status=403) @@ -110,6 +113,7 @@ class DeleteRoleTest(RolesTest): self.delete_role(self.role['id'], assert_status=401) def test_delete_roles_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.delete_role(self.role['id'], assert_status=403) @@ -144,7 +148,7 @@ class DeleteRoleTest(RolesTest): self.assertEqual(role['id'], role_id) self.assertEqual(role['serviceId'], service['id']) - def test_create__service_role_using_incorrect_role_name(self): + def test_create_service_role_using_incorrect_role_name(self): """ Formerly: test_create_role_mapped_to_a_service_using_incorrect_role_name""" self.create_role(common.unique_str(), service_id=common.unique_str(), @@ -166,10 +170,12 @@ class GetRolesTest(RolesTest): self.assertIsNotNone(role.get('id')) def test_get_roles_exp_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_roles(assert_status=403) def test_get_roles_exp_token_xml(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_roles(assert_status=403, headers={ 'Accept': 'application/xml'}) @@ -205,19 +211,23 @@ class GetRoleTest(RolesTest): 'Accept': 'application/xml'}) def test_get_role_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_role(self.role['id'], assert_status=403) def test_get_role_xml_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_role(self.role['id'], assert_status=403, headers={ 'Accept': 'application/xml'}) def test_get_role_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_role(self.role['id'], assert_status=403) def test_get_role_xml_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.get_role(self.role['id'], assert_status=403, headers={ 'Accept': 'application/xml'}) @@ -273,19 +283,23 @@ class GetRoleByNameTest(RolesTest): 'Accept': 'application/xml'}) def test_get_role_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_role_by_name(self.role['name'], assert_status=403) def test_get_role_xml_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_role_by_name(self.role['name'], assert_status=403, headers={ 'Accept': 'application/xml'}) def test_get_role_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_role_by_name(self.role['name'], assert_status=403) def test_get_role_xml_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.get_role_by_name(self.role['name'], assert_status=403, headers={ 'Accept': 'application/xml'}) @@ -313,13 +327,13 @@ class CreateRoleAssignmentTest(RolesTest): def setUp(self, *args, **kwargs): super(CreateRoleAssignmentTest, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] + self.fixture_create_normal_tenant() + self.fixture_create_tenant_user() + self.role = self.create_role().json['role'] def test_grant_role(self): - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=201) # def test_grant_role_json_using_service_admin_token(self): @@ -339,122 +353,129 @@ class CreateRoleAssignmentTest(RolesTest): # assert_status=201) def test_grant_role_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=403) def test_grant_role_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=403) def test_grant_role_using_missing_token(self): self.admin_token = '' - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=401) def test_grant_role_using_invalid_token(self): self.admin_token = common.unique_str() - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=401) def test_grant_global_role_json(self): self.grant_global_role_to_user( - self.user['id'], self.role['id'], assert_status=201) + self.tenant_user['id'], self.role['id'], assert_status=201) class GetRoleAssignmentsTest(RolesTest): def setUp(self, *args, **kwargs): super(GetRoleAssignmentsTest, self).setUp(*args, **kwargs) + self.fixture_create_normal_tenant() + self.fixture_create_tenant_user() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] self.role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id']) def test_get_role_assignments(self): - r = self.get_user_roles(self.user['id'], assert_status=200) + r = self.get_user_roles(self.tenant_user['id'], assert_status=200) self.assertIsNotNone(r.json['roles']) def test_get_roler_assignments_xml(self): - r = self.get_user_roles(self.user['id'], assert_status=200, + r = self.get_user_roles(self.tenant_user['id'], assert_status=200, headers={'Accept': 'application/xml'}) self.assertEqual(r.xml.tag, "{%s}roles" % self.xmlns) def test_get_role_assignments_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.get_user_roles(self.user['id'], assert_status=403) + self.get_user_roles(self.tenant_user['id'], assert_status=403) def test_get_role_assignments_xml_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.get_user_roles(self.user['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) + self.get_user_roles(self.tenant_user['id'], assert_status=403, + headers={'Accept': 'application/xml'}) def test_get_role_assignments_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.get_user_roles(self.user['id'], assert_status=403) + self.get_user_roles(self.tenant_user['id'], assert_status=403) def test_get_role_assignments_xml_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.get_user_roles(self.user['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) + self.get_user_roles(self.tenant_user['id'], assert_status=403, + headers={'Accept': 'application/xml'}) def test_get_role_assignments_using_missing_token(self): self.admin_token = '' - self.get_user_roles(self.user['id'], assert_status=401) + self.get_user_roles(self.tenant_user['id'], assert_status=401) def test_get_role_assignments_xml_using_missing_token(self): self.admin_token = '' - self.get_user_roles(self.user['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) + self.get_user_roles(self.tenant_user['id'], assert_status=401, + headers={'Accept': 'application/xml'}) def test_get_role_assignments_json_using_invalid_token(self): self.admin_token = common.unique_str() - self.get_user_roles(self.user['id'], assert_status=401) + self.get_user_roles(self.tenant_user['id'], assert_status=401) def test_get_role_assignments_xml_using_invalid_token(self): self.admin_token = common.unique_str() - self.get_user_roles(self.user['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) + self.get_user_roles(self.tenant_user['id'], assert_status=401, + headers={'Accept': 'application/xml'}) class DeleteRoleAssignmentsTest(RolesTest): def setUp(self, *args, **kwargs): super(DeleteRoleAssignmentsTest, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] + self.fixture_create_normal_tenant() + self.fixture_create_tenant_user() + self.role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], self.role['id'], + self.grant_role_to_user(self.tenant_user['id'], self.role['id'], self.tenant['id']) - self.roles = self.get_user_roles(self.user['id']).\ + self.roles = self.get_user_roles(self.tenant_user['id']).\ json['roles'] def test_delete_role_assignment(self): - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=204) def test_delete_role_assignment_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=403) def test_delete_role_assignment_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=403) def test_delete_role_assignment_using_missing_token(self): self.admin_token = '' - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=401) def test_delete_role_assignment_using_invalid_token(self): self.admin_token = common.unique_str() - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], self.tenant['id'], assert_status=401) @@ -462,36 +483,38 @@ class DeleteGlobalRoleAssignmentsTest(RolesTest): def setUp(self, *args, **kwargs): super(DeleteGlobalRoleAssignmentsTest, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] + self.fixture_create_normal_tenant() + self.fixture_create_tenant_user() + self.role = self.create_role().json['role'] - self.grant_global_role_to_user(self.user['id'], self.role['id']) - self.roles = self.get_user_roles(self.user['id']).\ + self.grant_global_role_to_user(self.tenant_user['id'], self.role['id']) + self.roles = self.get_user_roles(self.tenant_user['id']).\ json['roles'] def test_delete_role_assignment(self): - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], None, assert_status=204) def test_delete_role_assignment_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], None, assert_status=403) def test_delete_role_assignment_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], None, assert_status=403) def test_delete_role_assignment_using_missing_token(self): self.admin_token = '' - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], None, assert_status=401) def test_delete_role_assignment_using_invalid_token(self): self.admin_token = common.unique_str() - self.delete_user_role(self.user['id'], self.role['id'], + self.delete_user_role(self.tenant_user['id'], self.role['id'], None, assert_status=401) if __name__ == '__main__': diff --git a/keystone/test/functional/test_services.py b/keystone/test/functional/test_services.py index 16dd4cd0..276a5c19 100644 --- a/keystone/test/functional/test_services.py +++ b/keystone/test/functional/test_services.py @@ -15,6 +15,8 @@ # limitations under the License. import unittest2 as unittest +import uuid + from keystone.test.functional import common @@ -22,6 +24,12 @@ class ServicesTest(common.FunctionalTestCase): def setUp(self, *args, **kwargs): super(ServicesTest, self).setUp(*args, **kwargs) + service = self.create_service( + service_name="service-%s" % uuid.uuid4().hex, + service_type='identity', + service_description='Sample service', + assert_status=201).json['OS-KSADM:service'] + def tearDown(self, *args, **kwargs): super(ServicesTest, self).tearDown(*args, **kwargs) @@ -42,6 +50,7 @@ class GetServicesTest(ServicesTest): self.assertTrue(len(services)) def test_get_services_using_service_admin_token(self): + self.fixture_create_service_admin() self.admin_token = self.service_admin_token services = self.list_services(assert_status=200).\ json['OS-KSADM:services'] @@ -49,6 +58,7 @@ class GetServicesTest(ServicesTest): self.assertTrue(len(services)) def test_get_services_using_service_admin_token_xml(self): + self.fixture_create_service_admin() self.admin_token = self.service_admin_token r = self.get_services(assert_status=200, headers={ 'Accept': 'application/xml'}) @@ -58,6 +68,7 @@ class GetServicesTest(ServicesTest): self.assertTrue(len(services)) def test_get_services_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.list_services(assert_status=403) @@ -66,6 +77,7 @@ class GetServicesTest(ServicesTest): self.list_services(assert_status=401) def test_get_services_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_services(assert_status=403) @@ -96,6 +108,7 @@ class GetServiceTest(ServicesTest): self.assertIsNotNone(service.get('description')) def test_get_service_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_service(service_id=self.service['id'], assert_status=403) @@ -104,6 +117,7 @@ class GetServiceTest(ServicesTest): self.fetch_service(service_id=self.service['id'], assert_status=401) def test_get_service_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_service(service_id=self.service['id'], assert_status=403) @@ -135,6 +149,7 @@ class GetServiceByNameTest(ServicesTest): self.assertIsNotNone(service.get('description')) def test_get_service_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_service_by_name( service_name=self.service['name'], assert_status=403) @@ -145,6 +160,7 @@ class GetServiceByNameTest(ServicesTest): service_name=self.service['name'], assert_status=401) def test_get_service_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_service_by_name( service_name=self.service['name'], assert_status=403) @@ -190,10 +206,12 @@ class CreateServiceTest(ServicesTest): self.create_service(service_name=service_name, assert_status=409) def test_service_create_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.create_service(assert_status=403) def test_service_create_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_service(assert_status=403) @@ -254,10 +272,12 @@ class DeleteServiceTest(ServicesTest): self.remove_service(self.service['id'], assert_status=204) def test_service_delete_json_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.remove_service(self.service['id'], assert_status=403) def test_service_delete_json_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.remove_service(self.service['id'], assert_status=403) diff --git a/keystone/test/functional/test_static_files.py b/keystone/test/functional/test_static_files.py index 08d8b02f..75b445f6 100644 --- a/keystone/test/functional/test_static_files.py +++ b/keystone/test/functional/test_static_files.py @@ -7,43 +7,43 @@ class TestStaticFiles(common.ApiTestCase): if not common.isSsl(): #TODO(ziad): Caller hangs in SSL (but works with cURL) r = self.service_request(path='/identitydevguide.pdf') - self.assertTrue('pdf' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_wadl_contract(self): r = self.service_request(path='/identity.wadl') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_wadl_common(self): r = self.service_request(path='/common.ent') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xsd_contract(self): r = self.service_request(path='/xsd/api.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xsd_atom_contract(self): r = self.service_request(path='/xsd/atom/atom.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xslt(self): r = self.service_request(path='/xslt/schema.xslt') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_js(self): r = self.service_request(path='/js/shjs/sh_java.js') - self.assertTrue('javascript' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xml_sample(self): r = self.service_request(path='/samples/auth.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_json_sample(self): r = self.service_request(path='/samples/auth.json') - self.assertTrue('json' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_stylesheet(self): r = self.service_request(path='/style/shjs/sh_acid.css') - self.assertTrue('css' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) class TestAdminStaticFiles(common.FunctionalTestCase): @@ -51,39 +51,39 @@ class TestAdminStaticFiles(common.FunctionalTestCase): if not common.isSsl(): #TODO(ziad): Caller hangs in SSL (but works with cURL) r = self.admin_request(path='/identityadminguide.pdf') - self.assertTrue('pdf' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_wadl_contract(self): r = self.admin_request(path='/identity-admin.wadl') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xsd_contract(self): r = self.admin_request(path='/xsd/api.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xsd_atom_contract(self): r = self.admin_request(path='/xsd/atom/atom.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xslt(self): r = self.admin_request(path='/xslt/schema.xslt') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_js(self): r = self.admin_request(path='/js/shjs/sh_java.js') - self.assertTrue('javascript' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_xml_sample(self): r = self.admin_request(path='/samples/auth.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_json_sample(self): r = self.admin_request(path='/samples/auth.json') - self.assertTrue('json' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) def test_stylesheet(self): r = self.admin_request(path='/style/shjs/sh_acid.css') - self.assertTrue('css' in r.getheader('Content-Type')) + self.assertResponseSuccessful(r) if __name__ == '__main__': diff --git a/keystone/test/functional/test_tenants.py b/keystone/test/functional/test_tenants.py index 62e0035b..5c52a2b8 100644 --- a/keystone/test/functional/test_tenants.py +++ b/keystone/test/functional/test_tenants.py @@ -41,7 +41,7 @@ class TenantTest(common.FunctionalTestCase): self._assertValidTenant(xml) description = xml.find('{%s}description' % self.xmlns) - self.assertIsNotNone(description.text) + self.assertIsNotNone(description) self.assertIn(xml.get('enabled'), ['true', 'false']) return xml @@ -131,10 +131,12 @@ class CreateTenantTest(TenantTest): assert_status=409) def test_create_tenant_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.create_tenant(assert_status=403) def test_create_tenant_expired_token_xml(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token data = '<?xml version="1.0" encoding="UTF-8"?> \ <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ @@ -159,10 +161,12 @@ class CreateTenantTest(TenantTest): self.post_tenant(as_xml=data, assert_status=401) def test_create_tenant_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_tenant(assert_status=403) def test_create_tenant_disabled_token_xml(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token data = '<?xml version="1.0" encoding="UTF-8"?> \ <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ @@ -265,10 +269,12 @@ class GetTenantsTest(TenantTest): self.assertIn(tenant['id'], [t.get('id') for t in tenants]) def test_get_tenants_exp_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_tenants(assert_status=403) def test_get_tenants_exp_token_xml(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.get_tenants(assert_status=403, headers={ 'Accept': 'application/xml'}) @@ -277,16 +283,14 @@ class GetTenantsTest(TenantTest): class GetTenantTest(TenantTest): def setUp(self, *args, **kwargs): super(GetTenantTest, self).setUp(*args, **kwargs) - - r = self.create_tenant() - self.tenant = self.assertValidJsonTenantResponse(r) + self.fixture_create_normal_tenant() def test_get_tenant(self): r = self.fetch_tenant(self.tenant['id'], assert_status=200) tenant = self.assertValidJsonTenantResponse(r) self.assertEquals(self.tenant['id'], tenant['id']) self.assertEquals(self.tenant['name'], tenant['name']) - self.assertEquals(self.tenant['description'], tenant['description']) + self.assertFalse('description' in tenant) self.assertEquals(self.tenant['enabled'], tenant['enabled']) def test_get_tenant_xml(self): @@ -312,9 +316,9 @@ class GetTenantTest(TenantTest): class GetTenantUsersTest(TenantTest): def setUp(self, *args, **kwargs): super(GetTenantUsersTest, self).setUp(*args, **kwargs) - r = self.create_tenant() - self.tenant = self.assertValidJsonTenantResponse(r) - self.user = self.create_user_with_known_password().json['user'] + self.fixture_create_normal_tenant() + self.fixture_create_normal_user() + role = self.create_role().json['role'] self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) @@ -333,10 +337,12 @@ class GetTenantUsersTest(TenantTest): self.assertEqual(user.get('name'), self.user['name']) def test_list_tenant_users_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_tenant_users(self.tenant['id'], assert_status=403) def test_list_tenant_users_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.list_tenant_users(self.tenant['id'], assert_status=403) @@ -352,9 +358,9 @@ class GetTenantUsersTest(TenantTest): class GetTenantUsersByRoleTest(TenantTest): def setUp(self, *args, **kwargs): super(GetTenantUsersByRoleTest, self).setUp(*args, **kwargs) - r = self.create_tenant() - self.tenant = self.assertValidJsonTenantResponse(r) - self.user = self.create_user_with_known_password().json['user'] + self.fixture_create_normal_tenant() + self.fixture_create_normal_user() + self.role = self.create_role().json['role'] self.grant_role_to_user(self.user['id'], self.role['id'], self.tenant['id']) @@ -374,11 +380,13 @@ class GetTenantUsersByRoleTest(TenantTest): self.assertEqual(user.get('name'), self.user['name']) def test_list_tenant_users_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_tenant_users(self.tenant['id'], self.role['id'], assert_status=403) def test_list_tenant_users_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.list_tenant_users(self.tenant['id'], self.role['id'], assert_status=403) @@ -397,17 +405,26 @@ class GetTenantUsersByRoleTest(TenantTest): class GetTenantByNameTest(TenantTest): def setUp(self, *args, **kwargs): super(GetTenantByNameTest, self).setUp(*args, **kwargs) - r = self.create_tenant() - self.tenant = self.assertValidJsonTenantResponse(r) + self.fixture_create_normal_tenant() def test_get_tenant(self): r = self.fetch_tenant_by_name(self.tenant['name'], assert_status=200) tenant = self.assertValidJsonTenantResponse(r) self.assertEquals(self.tenant['id'], tenant['id']) self.assertEquals(self.tenant['name'], tenant['name']) - self.assertEquals(self.tenant['description'], tenant['description']) self.assertEquals(self.tenant['enabled'], tenant['enabled']) + def test_get_tenant_data(self): + tenant = self.fixture_create_tenant(name=common.unique_str(), + description=common.unique_str(), + enabled=True) + r = self.fetch_tenant_by_name(tenant['name'], assert_status=200) + returned = self.assertValidJsonTenantResponse(r) + self.assertEquals(returned['id'], tenant['id']) + self.assertEquals(returned['name'], tenant['name']) + self.assertEquals(returned['description'], tenant['description']) + self.assertEquals(returned['enabled'], tenant['enabled']) + def test_get_tenant_xml(self): r = self.fetch_tenant_by_name( self.tenant['name'], assert_status=200, headers={ @@ -434,8 +451,7 @@ class GetTenantByNameTest(TenantTest): class UpdateTenantTest(TenantTest): def setUp(self, *args, **kwargs): super(UpdateTenantTest, self).setUp(*args, **kwargs) - r = self.create_tenant() - self.tenant = self.assertValidJsonTenantResponse(r) + self.fixture_create_normal_tenant() def test_update_tenant(self): new_tenant_name = common.unique_str() @@ -498,8 +514,7 @@ class UpdateTenantTest(TenantTest): class DeleteTenantTest(TenantTest): def setUp(self, *args, **kwargs): super(DeleteTenantTest, self).setUp(*args, **kwargs) - - self.tenant = self.create_tenant().json['tenant'] + self.fixture_create_normal_tenant() def test_delete_tenant(self): self.remove_tenant(self.tenant['id'], assert_status=204) diff --git a/keystone/test/functional/test_token.py b/keystone/test/functional/test_token.py index 4f4abc76..50ec413d 100644 --- a/keystone/test/functional/test_token.py +++ b/keystone/test/functional/test_token.py @@ -22,8 +22,8 @@ from keystone.test.functional import common class ValidateToken(common.FunctionalTestCase): def setUp(self, *args, **kwargs): super(ValidateToken, self).setUp(*args, **kwargs) + self.fixture_create_normal_tenant() - self.tenant = self.create_tenant().json['tenant'] self.user = self.create_user_with_known_password( tenant_id=self.tenant['id']).json['user'] self.role = self.create_role().json['role'] @@ -46,6 +46,7 @@ class ValidateToken(common.FunctionalTestCase): self.user['name']) def test_validate_token_true_using_service_token(self): + self.fixture_create_service_admin() self.admin_token = self.service_admin_token r = self.get_token_belongsto(self.token['id'], self.tenant['id'], assert_status=200) @@ -93,25 +94,29 @@ class ValidateToken(common.FunctionalTestCase): class CheckToken(common.FunctionalTestCase): def setUp(self, *args, **kwargs): super(CheckToken, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] - self.token = self.authenticate(self.user['name'], - self.user['password'], self.tenant['id']).json['access']['token'] + self.fixture_create_normal_tenant() + self.fixture_create_tenant_user() + + self.token = self.authenticate(self.tenant_user['name'], + self.tenant_user['password'], + self.tenant['id']).json['access']['token'] def test_validate_token_true(self): self.check_token_belongs_to(self.token['id'], self.tenant['id'], assert_status=200) def test_validate_token_true_using_service_token(self): + self.fixture_create_service_admin() self.admin_token = self.service_admin_token self.check_token_belongs_to(self.token['id'], self.tenant['id'], assert_status=200) def test_validate_token_expired(self): + self.fixture_create_expired_token() self.check_token(self.expired_admin_token, assert_status=404) def test_validate_token_expired_xml(self): + self.fixture_create_expired_token() self.check_token(self.expired_admin_token, assert_status=404, headers={ 'Accept': 'application/xml'}) diff --git a/keystone/test/functional/test_users.py b/keystone/test/functional/test_users.py index d65ce6bd..7027986c 100644 --- a/keystone/test/functional/test_users.py +++ b/keystone/test/functional/test_users.py @@ -55,10 +55,12 @@ class CreateUserTest(UserTest): self.create_user(user_name='', assert_status=400) def test_create_user_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.create_user(assert_status=403) def test_create_user_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_user(assert_status=403) @@ -91,18 +93,22 @@ class GetUserTest(UserTest): self.fetch_user_by_name(self.user['name']) def test_get_user_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_user(self.user['id'], assert_status=403) def test_query_user_using_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.fetch_user_by_name(self.user['name'], assert_status=403) def test_get_user_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_user(self.user['id'], assert_status=403) def test_query_user_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.fetch_user_by_name(self.user['name'], assert_status=403) @@ -142,6 +148,7 @@ class DeleteUserTest(UserTest): self.remove_user(self.user['id'], assert_status=204) def test_user_delete_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.remove_user(self.user['id'], assert_status=403) @@ -165,10 +172,12 @@ class GetAllUsersTest(UserTest): self.list_users(assert_status=200) def test_list_users_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.list_users(assert_status=403) def test_list_users_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.list_users(assert_status=403) @@ -219,10 +228,14 @@ class UpdateUserTest(UserTest): "Content-Type": "application/json"}) def test_update_user_expired_token(self): + self.fixture_create_expired_token() + self.fixture_create_normal_user() + self.admin_token = self.expired_admin_token self.update_user(self.user['id'], assert_status=403) def test_update_user_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.update_user(self.user['id'], user_email=common.unique_email(), assert_status=403) @@ -254,7 +267,7 @@ class TestUpdateConflict(UserTest): class SetPasswordTest(UserTest): def setUp(self, *args, **kwargs): super(SetPasswordTest, self).setUp(*args, **kwargs) - self.user = self.create_user().json['user'] + self.fixture_create_normal_user() def test_update_user_password(self): new_password = common.unique_str() @@ -275,10 +288,12 @@ class SetPasswordTest(UserTest): "Content-Type": "application/json"}) def test_user_password_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.update_user_password(self.user['id'], assert_status=403) def test_user_password_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.update_user_password(self.user['id'], assert_status=403) @@ -294,7 +309,7 @@ class SetPasswordTest(UserTest): class SetEnabledTest(UserTest): def setUp(self, *args, **kwargs): super(SetEnabledTest, self).setUp(*args, **kwargs) - self.user = self.create_user().json['user'] + self.fixture_create_normal_user() def test_user_enabled_bad_request(self): data = '{"user_bad": { "enabled": true}}' @@ -303,10 +318,12 @@ class SetEnabledTest(UserTest): "Content-Type": "application/json"}) def test_user_enabled_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.disable_user(self.user['id'], assert_status=403) def test_user_enabled_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.disable_user(self.user['id'], assert_status=403) @@ -314,8 +331,8 @@ class SetEnabledTest(UserTest): class TenantUpdateTest(UserTest): def setUp(self, *args, **kwargs): super(TenantUpdateTest, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user().json['user'] + self.fixture_create_normal_user() + self.fixture_create_normal_tenant() def test_update_user_tenant(self): r = self.update_user_tenant(self.user['id'], self.tenant['id']) @@ -342,11 +359,13 @@ class TenantUpdateTest(UserTest): assert_status=401) def test_update_user_tenant_using_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.update_user_tenant(self.user['id'], self.tenant['id'], assert_status=403) def test_update_user_tenant_using_exp_admin_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.update_user_tenant(self.user['id'], self.tenant['id'], assert_status=403) @@ -355,16 +374,18 @@ class TenantUpdateTest(UserTest): class AddUserTest(UserTest): def setUp(self, *args, **kwargs): super(AddUserTest, self).setUp(*args, **kwargs) - self.tenant = self.create_tenant().json['tenant'] + self.fixture_create_normal_tenant() def test_add_user_tenant(self): self.create_user(tenant_id=self.tenant['id'], assert_status=201) def test_add_user_tenant_expired_token(self): + self.fixture_create_expired_token() self.admin_token = self.expired_admin_token self.create_user(assert_status=403) def test_add_user_tenant_disabled_token(self): + self.fixture_create_disabled_user_and_token() self.admin_token = self.disabled_admin_token self.create_user(assert_status=403) diff --git a/keystone/test/unit/test_models_Tenant.py b/keystone/test/unit/test_models_Tenant.py index 963cafca..a86fc011 100644 --- a/keystone/test/unit/test_models_Tenant.py +++ b/keystone/test/unit/test_models_Tenant.py @@ -1,5 +1,4 @@ import json -from lxml import etree import unittest2 as unittest from keystone.models import Tenant @@ -24,12 +23,7 @@ class TestModelsTenant(unittest.TestCase): self.assertEquals(tenant.name, "the tenant") self.assertTrue(tenant.enabled) self.assertEquals(tenant.description, None) - try: - x = tenant.some_bad_property - except AttributeError: - pass - except: - self.assert_(False, "Invalid attribute on tenant should fail") + self.assertRaises(AttributeError, getattr, tenant, 'some_bad_property') def test_tenant_properties(self): tenant = Tenant(id=2, name="the tenant", blank=None) @@ -111,6 +105,40 @@ class TestModelsTenant(unittest.TestCase): tenant = Tenant(id=9, name="the tenant", blank=None) self.assertTrue(tenant.validate()) + def test_tenant_description_values(self): + tenant = Tenant(id=10, name="the tenant") + self.assertIsNone(tenant.description, + "Uninitialized description should be None") + xml = tenant.to_dom() + desc = xml.find("{http://docs.openstack.org/identity/api/v2.0}" + "description") + self.assertIsNone(desc, + "Uninitialized description should not exist in xml") + + tenant = Tenant(id=10, name="the tenant", description=None) + self.assertIsNone(tenant.description, + "Description initialized to None should be None") + xml = tenant.to_dom() + desc = xml.find("{http://docs.openstack.org/identity/api/v2.0}" + "description") + self.assertIsNone(desc, + "Uninitialized description should not exist in xml") + + tenant = Tenant(id=10, name="the tenant", description='') + self.assertEquals(tenant.description, '', + 'Description initialized to empty string should be empty string') + xml = tenant.to_dom() + desc = xml.find("description") + self.assertEquals(desc.text, None, + "Blank Description should show as empty tag in xml") + + tenant = Tenant(id=10, name="the tenant", description=None) + xml = tenant.to_xml(hints={"tags": ["description"]}) + xml = tenant.to_dom() + desc = xml.find("description") + self.assertEquals(desc.text, None, + "'None' Description should show as empty tag in xml") + if __name__ == '__main__': unittest.main() diff --git a/run_tests.py b/run_tests.py index 7967a624..c4cf012f 100755 --- a/run_tests.py +++ b/run_tests.py @@ -20,6 +20,8 @@ from keystone import test TESTS = [ + test.UnitTests, + test.ClientTests, test.SQLTest, test.LDAPTest, # Waiting on instructions on how to start memcached in jenkins: @@ -48,6 +50,7 @@ if __name__ == '__main__': print 'No test configuration by the name %s found' % filter exit() + #Run test suites if len(TESTS) > 1: # We have a problem with resetting SQLAlchemy, so we need to fire # off a separate process for each test now @@ -61,7 +64,6 @@ if __name__ == '__main__': else: for test_num, test_cls in enumerate(TESTS): - print 'Starting test %d of %d with config: %s' % \ - (test_num + 1, len(TESTS), test_cls.config_name) + print 'Runing test suite: %s' % test_cls.__name__ if test_cls().run(): exit(1) diff --git a/run_tests.sh b/run_tests.sh index d33b8b83..e7731f05 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -6,8 +6,14 @@ function usage { echo "Usage: $0 [OPTION]..." echo "Run Keystone's test suite(s)" echo "" - echo " -O test_name Only run the specified test" - echo " Note: valid options now are SQLTest, LDAPTest, SSLTest, and MemcacheTest" + echo " -O test_name Only run the specified test suite. Valid values are:" + echo " UnitTests: runs unit tests" + echo " ClientTests: runs tests that start and hit an HTTP[S] server" + echo " SQLTest: runs functional tests with SQLAlchemy backend" + echo " SSLTest: runs client tests with SSL configured" + echo " LDAPTest: runs functional tests with LDAP backend" + echo " MemcacheTest: runs functional tests with memcached storing tokens" + echo " Note: by default, run tests will run all suites" echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" echo " -x, --stop Stop running tests after the first error or failure." |
