From a5e7587c8dcb6f89ce49d694c8eca8d3baf8e854 Mon Sep 17 00:00:00 2001 From: Ziad Sawalha Date: Tue, 20 Dec 2011 01:19:13 -0600 Subject: 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 --- keystone/logic/service.py | 2 +- keystone/logic/types/tenant.py | 16 +- keystone/models.py | 7 +- keystone/test/__init__.py | 152 +++++++-- keystone/test/client/__init__.py | 0 keystone/test/client/test_client.py | 96 ++++++ keystone/test/client/test_d5_compat_calls.py | 169 ++++++++++ keystone/test/client/test_extensions.py | 57 ++++ keystone/test/client/test_frontends.py | 38 +++ keystone/test/client/test_middleware.py | 97 ++++++ keystone/test/client/test_request_specs.py | 93 ++++++ keystone/test/client/test_static_files.py | 94 ++++++ keystone/test/functional/common.py | 399 +++++++++++++++++++++-- keystone/test/functional/test_auth.py | 4 - keystone/test/functional/test_authentication.py | 31 -- keystone/test/functional/test_client.py | 95 ------ keystone/test/functional/test_credentials.py | 28 +- keystone/test/functional/test_d5_compat_calls.py | 167 ---------- keystone/test/functional/test_endpoints.py | 97 ++++-- keystone/test/functional/test_extensions.py | 53 --- keystone/test/functional/test_middleware.py | 94 ------ keystone/test/functional/test_request_specs.py | 91 ------ keystone/test/functional/test_roles.py | 119 ++++--- keystone/test/functional/test_services.py | 20 ++ keystone/test/functional/test_static_files.py | 38 +-- keystone/test/functional/test_tenants.py | 51 ++- keystone/test/functional/test_token.py | 17 +- keystone/test/functional/test_users.py | 31 +- keystone/test/unit/test_models_Tenant.py | 42 ++- run_tests.py | 6 +- run_tests.sh | 10 +- 31 files changed, 1460 insertions(+), 754 deletions(-) create mode 100644 keystone/test/client/__init__.py create mode 100644 keystone/test/client/test_client.py create mode 100644 keystone/test/client/test_d5_compat_calls.py create mode 100644 keystone/test/client/test_extensions.py create mode 100644 keystone/test/client/test_frontends.py create mode 100644 keystone/test/client/test_middleware.py create mode 100644 keystone/test/client/test_request_specs.py create mode 100644 keystone/test/client/test_static_files.py delete mode 100644 keystone/test/functional/test_client.py delete mode 100644 keystone/test/functional/test_d5_compat_calls.py delete mode 100644 keystone/test/functional/test_extensions.py delete mode 100644 keystone/test/functional/test_middleware.py delete mode 100644 keystone/test/functional/test_request_specs.py 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 diff --git a/keystone/test/client/test_client.py b/keystone/test/client/test_client.py new file mode 100644 index 00000000..c1062703 --- /dev/null +++ b/keystone/test/client/test_client.py @@ -0,0 +1,96 @@ +import unittest + +import keystone.common.exception +import keystone.client +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): + """ + Run before each test. + """ + cert_file = isSsl() + self.client = keystone.client.AdminClient("127.0.0.1", + is_ssl=(cert_file != None), + cert_file=cert_file, + admin_name="admin", + admin_pass="secrete") + + def test_admin_validate_token(self): + """ + Test that our admin token is valid. (HTTP GET) + """ + token = self.client.admin_token + result = self.client.validate_token(token) + self.assertEquals("admin", + result["access"]["user"]["name"]) + + def test_admin_check_token(self): + """ + Test that our admin token is valid. (HTTP HEAD) + """ + token = self.client.admin_token + self.assertTrue(self.client.check_token(token)) + + def test_admin_validate_token_fail(self): + """ + Test that validating an invalid token results in None. (HTTP GET) + """ + token = "bad_token" + self.assertTrue(self.client.validate_token(token) is None) + + def test_admin_check_token_fail(self): + """ + Test that checking an invalid token results in False. (HTTP HEAD) + """ + token = "bad_token" + self.assertFalse(self.client.check_token(token)) + + def test_admin_get_token(self): + """ + Test that we can generate a token given correct credentials. + """ + token = self.client.get_token("admin", "secrete") + self.assertEquals(self.client.admin_token, token) + + def test_admin_get_token_bad_auth(self): + """ + Test incorrect credentials generates a client error. + """ + self.assertRaises(keystone.common.exception.ClientError, + self.client.get_token, "bad_user", "bad_pass") + + +class TestServiceClient(unittest.TestCase): + """ + Quick functional tests for the Keystone HTTP service client. + """ + + def setUp(self): + """ + Run before each test. + """ + cert_file = isSsl() + self.client = keystone.client.ServiceClient("127.0.0.1", + is_ssl=(cert_file != None), + cert_file=cert_file) + + def test_admin_get_token(self): + """ + Test that we can generate a token given correct credentials. + """ + token = self.client.get_token("admin", "secrete") + self.assertTrue(36, len(token)) + + def test_admin_get_token_bad_auth(self): + """ + Test incorrect credentials generates a client error. + """ + self.assertRaises(keystone.common.exception.ClientError, + self.client.get_token, "bad_user", "bad_pass") diff --git a/keystone/test/client/test_d5_compat_calls.py b/keystone/test/client/test_d5_compat_calls.py new file mode 100644 index 00000000..1c3e99c0 --- /dev/null +++ b/keystone/test/client/test_d5_compat_calls.py @@ -0,0 +1,169 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2010-2011 OpenStack, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest2 as unittest + +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) + + 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 = {} + self.services = self.create_service().json['OS-KSADM:service'] + self.endpoint_templates = self.create_endpoint_template( + name=self.services['name'], \ + type=self.services['type']).\ + json['OS-KSCATALOG:endpointTemplate'] + self.create_endpoint_for_tenant(self.tenant['id'], + self.endpoint_templates['id']) + + def test_validate_unscoped_token(self): + """Admin should be able to validate a user's token""" + # Authenticate as user to get a token + self.service_token = self.post_token(as_json={ + 'passwordCredentials': { + 'username': self.user['name'], + 'password': self.user['password']}}).\ + json['auth']['token']['id'] + + # In the real world, the service user would then pass his/her token + # to some service that depends on keystone, which would then need to + # use keystone to validate the provided token. + + # Admin independently validates the user token + r = self.get_token(self.service_token) + self.assertEqual(r.json['auth']['token']['id'], self.service_token) + self.assertTrue(r.json['auth']['token']['expires']) + self.assertEqual(r.json['auth']['user']['username'], + self.user['name']) + self.assertEqual(r.json['auth']['user']['roleRefs'], []) + + def test_validate_scoped_token(self): + """Admin should be able to validate a user's scoped token""" + # Authenticate as user to get a token + self.service_token = self.post_token(as_json={ + 'passwordCredentials': { + 'tenantId': self.tenant['id'], + 'username': self.user['name'], + 'password': self.user['password']}}).\ + json['auth']['token']['id'] + + # In the real world, the service user would then pass his/her token + # to some service that depends on keystone, which would then need to + # use keystone to validate the provided token. + + # Admin independently validates the user token + r = self.get_token(self.service_token) + self.assertEqual(r.json['auth']['token']['id'], self.service_token) + self.assertEqual(r.json['auth']['token']['tenantId'], + self.tenant['id']) + self.assertTrue(r.json['auth']['token']['expires']) + self.assertEqual(r.json['auth']['user']['username'], + self.user['name']) + self.assertEqual(r.json['auth']['user']['roleRefs'], []) + + def test_authenticate_for_a_tenant(self): + r = self.authenticate_D5(self.user['name'], self.user['password'], + self.tenant['id'], assert_status=200) + + self.assertIsNotNone(r.json['auth']['token']) + service_catalog = r.json['auth']['serviceCatalog'] + self.assertIsNotNone(service_catalog) + self.check_urls_for_regular_user(service_catalog) + + def test_authenticate_for_a_tenant_xml(self): + data = (' ' + '') % ( + self.xmlns, self.tenant['id'], + self.user['name'], self.user['password']) + r = self.post_token(as_xml=data, assert_status=200) + + self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) + service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) + self.check_urls_for_regular_user_xml(service_catalog) + + def test_authenticate_for_a_tenant_on_admin_api(self): + r = self.authenticate_D5(self.user['name'], self.user['password'], + self.tenant['id'], assert_status=200, request_type='admin') + + self.assertIsNotNone(r.json['auth']['token']) + self.assertIsNotNone(r.json['auth']['serviceCatalog']) + service_catalog = r.json['auth']['serviceCatalog'] + self.check_urls_for_regular_user(service_catalog) + + def test_authenticate_for_a_tenant_xml_on_admin_api(self): + data = (' ' + '') % ( + self.xmlns, self.tenant['id'], + self.user['name'], self.user['password']) + r = self.post_token(as_xml=data, assert_status=200, + request_type='admin') + + self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) + service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) + self.check_urls_for_regular_user_xml(service_catalog) + + def test_authenticate_user_disabled(self): + self.disable_user(self.user['id']) + self.authenticate_D5(self.user['name'], self.user['password'], + self.tenant['id'], assert_status=403) + + def test_authenticate_user_wrong(self): + data = {"passwordCredentials": { + "username-field-completely-wrong": self.user['name'], + "password": self.user['password'], + "tenantId": self.tenant['id']}} + self.post_token(as_json=data, assert_status=400) + + def test_authenticate_user_wrong_xml(self): + data = (' ' + '') % ( + self.user['name'], self.user['password'], self.tenant['id']) + + self.post_token(as_xml=data, assert_status=400) + + def check_urls_for_regular_user(self, service_catalog): + self.assertIsNotNone(service_catalog) + for k in service_catalog.keys(): + endpoints = service_catalog[k] + for endpoint in endpoints: + for key in endpoint: + #Checks whether adminURL is not present. + self.assertNotEquals(key, 'adminURL') + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/client/test_extensions.py b/keystone/test/client/test_extensions.py new file mode 100644 index 00000000..551df491 --- /dev/null +++ b/keystone/test/client/test_extensions.py @@ -0,0 +1,57 @@ +import unittest2 as unittest +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')) + content = r.json + self.assertIsNotNone(content['extensions']) + self.assertIsNotNone(content['extensions']['values']) + + def test_extensions_xml(self): + r = self.service_request(path='/extensions.xml') + self.assertTrue('xml' in r.getheader('Content-Type')) + + +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')) + content = r.json + self.assertIsNotNone(content['extensions']) + self.assertIsNotNone(content['extensions']['values']) + found_osksadm = False + found_oskscatalog = False + for value in content['extensions']['values']: + if value['extension']['alias'] == 'OS-KSADM': + found_osksadm = True + if value['extension']['alias'] == 'OS-KSCATALOG': + found_oskscatalog = True + self.assertTrue(found_osksadm, "Missing OS-KSADM extension.") + self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.") + + def test_extensions_xml(self): + r = self.admin_request(path='/extensions.xml') + self.assertTrue('xml' in r.getheader('Content-Type')) + content = r.xml + extensions = content.findall( + "{http://docs.openstack.org/common/api/v1.0}extension") + found_osksadm = False + found_oskscatalog = False + for extension in extensions: + if extension.get("alias") == 'OS-KSADM': + found_osksadm = True + if extension.get("alias") == 'OS-KSCATALOG': + found_oskscatalog = True + self.assertTrue(found_osksadm, "Missing OS-KSADM extension.") + self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.") + + +if __name__ == '__main__': + unittest.main() 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/client/test_middleware.py b/keystone/test/client/test_middleware.py new file mode 100644 index 00000000..1526944e --- /dev/null +++ b/keystone/test/client/test_middleware.py @@ -0,0 +1,97 @@ +import unittest2 as unittest + +import keystone.common.exception +from keystone.test.functional import common + +# +# Auth Token +# +from keystone.middleware import auth_token + + +class TestAuthTokenMiddleware(common.MiddlewareTestCase): + """ + Tests for Keystone WSGI middleware: Auth Token + """ + + def setUp(self): + super(TestAuthTokenMiddleware, self).setUp(auth_token) + + +# +# Glance +# +try: + from keystone.middleware import glance_auth_token +except ImportError as e: + print 'Could not load glance_auth_token: %s' % e + + +@unittest.skipUnless('glance_auth_token' in vars(), + "Glance Auth Token not imported") +class TestGlanceMiddleware(common.MiddlewareTestCase): + """ + Tests for Keystone WSGI middleware: Glance + """ + + def setUp(self): + super(TestGlanceMiddleware, self).setUp( + (auth_token, glance_auth_token)) + + +# +# Quantum +# +from keystone.middleware import quantum_auth_token + + +class TestQuantumMiddleware(common.MiddlewareTestCase): + """ + Tests for Keystone WSGI middleware: Glance + """ + + 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', + 'auth_protocol': 'http', + 'auth_uri': 'http://localhost:35357/', + 'auth_version': '2.0', + 'auth_admin_token': self.admin_token, + 'auth_admin_user': self.admin_username, + 'auth_admin_password': self.admin_password} + super(TestQuantumMiddleware, self).setUp(quantum_auth_token, settings) + + +# +# Swift +# +try: + from keystone.middleware import swift_auth +except ImportError as e: + print 'Could not load swift_auth: %s' % e + +#TODO(Ziad): find out how to disable swift logging +#@unittest.skipUnless('swift_auth' in vars(), +# "swift_auth not imported") +#class TestSwiftMiddleware(common.MiddlewareTestCase): +# """ +# Tests for Keystone WSGI middleware: Glance +# """ +# +# def setUp(self): +# settings = {'delay_auth_decision': '0', +# 'auth_host': '127.0.0.1', +# 'auth_port': '35357', +# 'auth_protocol': 'http', +# 'auth_uri': 'http://localhost:35357/', +# 'admin_token': self.admin_token, +# 'set log_facility': 'LOG_NULL'} +# super(TestSwiftMiddleware, self).setUp(swift_auth, settings) + + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/client/test_request_specs.py b/keystone/test/client/test_request_specs.py new file mode 100644 index 00000000..74494056 --- /dev/null +++ b/keystone/test/client/test_request_specs.py @@ -0,0 +1,93 @@ +import unittest2 as unittest +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.""" + r1 = self.service_request(path='/') + r2 = self.service_request(path='') + self.assertEqual(r1.read(), r2.read()) + + +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""" + r = self.service_request() + self.assertTrue('application/json' in r.getheader('Content-Type')) + + def test_xml_extension(self): + """Service responds to .xml URL extension""" + r = self.service_request(path='.xml') + self.assertTrue('application/xml' in r.getheader('Content-Type')) + + def test_json_extension(self): + """Service responds to .json URL extension""" + r = self.service_request(path='.json') + self.assertTrue('application/json' in r.getheader('Content-Type')) + + def test_xml_accept_header(self): + """Service responds to xml Accept header""" + r = self.service_request(headers={'Accept': 'application/xml'}) + self.assertTrue('application/xml' in r.getheader('Content-Type')) + + def test_json_accept_header(self): + """Service responds to json Accept header""" + r = self.service_request(headers={'Accept': 'application/json'}) + self.assertTrue('application/json' in r.getheader('Content-Type')) + + def test_versioned_xml_accept_header(self): + """Service responds to versioned xml Accept header""" + r = self.service_request(headers={ + 'Accept': 'application/vnd.openstack.identity-v2.0+xml'}) + self.assertTrue('application/xml' in r.getheader('Content-Type')) + + def test_versioned_json_accept_header(self): + """Service responds to versioned json Accept header""" + r = self.service_request(headers={ + 'Accept': 'application/vnd.openstack.identity-v2.0+json'}) + self.assertTrue('application/json' in r.getheader('Content-Type')) + + def test_xml_extension_overrides_conflicting_header(self): + """Service returns XML when Accept header conflicts with extension""" + r = self.service_request(path='.xml', + headers={'Accept': 'application/json'}) + + self.assertTrue('application/xml' in r.getheader('Content-Type')) + + def test_json_extension_overrides_conflicting_header(self): + """Service returns JSON when Accept header conflicts with extension""" + r = self.service_request(path='.json', + headers={'Accept': 'application/xml'}) + + self.assertTrue('application/json' in r.getheader('Content-Type')) + + def test_xml_content_type_on_404(self): + """Content-Type should be honored even on 404 errors (Issue #13)""" + r = self.service_request(path='/completely-invalid-path', + headers={'Accept': 'application/xml'}, + assert_status=404) + + # Commenting this assertion out, as it currently fails + self.assertTrue('application/xml' in r.getheader('Content-Type'), + 'application/xml not in %s' % r.getheader('Content-Type')) + + def test_json_content_type_on_404(self): + """Content-Type should be honored even on 404 errors (Issue #13)""" + r = self.service_request(path='/completely-invalid-path', + headers={'Accept': 'application/json'}, + assert_status=404) + + # Commenting this assertion out, as it currently fails + self.assertTrue('application/json' in r.getheader('Content-Type'), + 'application/json not in %s' % r.getheader('Content-Type')) + + +if __name__ == '__main__': + unittest.main() 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_client.py b/keystone/test/functional/test_client.py deleted file mode 100644 index 099ab1ff..00000000 --- a/keystone/test/functional/test_client.py +++ /dev/null @@ -1,95 +0,0 @@ -import unittest - -import keystone.common.exception -import keystone.client -from common import isSsl - - -class TestAdminClient(unittest.TestCase): - """ - Quick functional tests for the Keystone HTTP admin client. - """ - - def setUp(self): - """ - Run before each test. - """ - cert_file = isSsl() - self.client = keystone.client.AdminClient("127.0.0.1", - is_ssl=(cert_file != None), - cert_file=cert_file, - admin_name="admin", - admin_pass="secrete") - - def test_admin_validate_token(self): - """ - Test that our admin token is valid. (HTTP GET) - """ - token = self.client.admin_token - result = self.client.validate_token(token) - self.assertEquals("admin", - result["access"]["user"]["name"]) - - def test_admin_check_token(self): - """ - Test that our admin token is valid. (HTTP HEAD) - """ - token = self.client.admin_token - self.assertTrue(self.client.check_token(token)) - - def test_admin_validate_token_fail(self): - """ - Test that validating an invalid token results in None. (HTTP GET) - """ - token = "bad_token" - self.assertTrue(self.client.validate_token(token) is None) - - def test_admin_check_token_fail(self): - """ - Test that checking an invalid token results in False. (HTTP HEAD) - """ - token = "bad_token" - self.assertFalse(self.client.check_token(token)) - - def test_admin_get_token(self): - """ - Test that we can generate a token given correct credentials. - """ - token = self.client.get_token("admin", "secrete") - self.assertEquals(self.client.admin_token, token) - - def test_admin_get_token_bad_auth(self): - """ - Test incorrect credentials generates a client error. - """ - self.assertRaises(keystone.common.exception.ClientError, - self.client.get_token, "bad_user", "bad_pass") - - -class TestServiceClient(unittest.TestCase): - """ - Quick functional tests for the Keystone HTTP service client. - """ - - def setUp(self): - """ - Run before each test. - """ - cert_file = isSsl() - self.client = keystone.client.ServiceClient("127.0.0.1", - is_ssl=(cert_file != None), - cert_file=cert_file) - - def test_admin_get_token(self): - """ - Test that we can generate a token given correct credentials. - """ - token = self.client.get_token("admin", "secrete") - self.assertTrue(36, len(token)) - - def test_admin_get_token_bad_auth(self): - """ - Test incorrect credentials generates a client error. - """ - self.assertRaises(keystone.common.exception.ClientError, - self.client.get_token, "bad_user", "bad_pass") 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_d5_compat_calls.py b/keystone/test/functional/test_d5_compat_calls.py deleted file mode 100644 index acdec94b..00000000 --- a/keystone/test/functional/test_d5_compat_calls.py +++ /dev/null @@ -1,167 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 OpenStack, LLC. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import unittest2 as unittest - -from keystone.test.functional import common - - -class D5_AuthenticationTest(common.FunctionalTestCase): - """ Tests the functionality of the D5 compat module """ - def setUp(self, *args, **kwargs): - super(D5_AuthenticationTest, 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 = {} - self.services = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], \ - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_validate_unscoped_token(self): - """Admin should be able to validate a user's token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}).\ - json['auth']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['auth']['token']['id'], self.service_token) - self.assertTrue(r.json['auth']['token']['expires']) - self.assertEqual(r.json['auth']['user']['username'], - self.user['name']) - self.assertEqual(r.json['auth']['user']['roleRefs'], []) - - def test_validate_scoped_token(self): - """Admin should be able to validate a user's scoped token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'passwordCredentials': { - 'tenantId': self.tenant['id'], - 'username': self.user['name'], - 'password': self.user['password']}}).\ - json['auth']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['auth']['token']['id'], self.service_token) - self.assertEqual(r.json['auth']['token']['tenantId'], - self.tenant['id']) - self.assertTrue(r.json['auth']['token']['expires']) - self.assertEqual(r.json['auth']['user']['username'], - self.user['name']) - self.assertEqual(r.json['auth']['user']['roleRefs'], []) - - def test_authenticate_for_a_tenant(self): - r = self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(r.json['auth']['token']) - service_catalog = r.json['auth']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = (' ' - '') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - r = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) - service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - r = self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200, request_type='admin') - - self.assertIsNotNone(r.json['auth']['token']) - self.assertIsNotNone(r.json['auth']['serviceCatalog']) - service_catalog = r.json['auth']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = (' ' - '') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - r = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) - service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_user_disabled(self): - self.disable_user(self.user['id']) - self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=403) - - def test_authenticate_user_wrong(self): - data = {"passwordCredentials": { - "username-field-completely-wrong": self.user['name'], - "password": self.user['password'], - "tenantId": self.tenant['id']}} - self.post_token(as_json=data, assert_status=400) - - def test_authenticate_user_wrong_xml(self): - data = (' ' - '') % ( - self.user['name'], self.user['password'], self.tenant['id']) - - self.post_token(as_xml=data, assert_status=400) - - def check_urls_for_regular_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for k in service_catalog.keys(): - endpoints = service_catalog[k] - for endpoint in endpoints: - for key in endpoint: - #Checks whether adminURL is not present. - self.assertNotEquals(key, 'adminURL') - -if __name__ == '__main__': - unittest.main() 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 = (' ' ' ' ' \ 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." -- cgit