diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-09-16 17:24:30 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-09-16 17:24:30 +0000 |
commit | f51b76bc7fbd72857b4dbea743fd9fe469e5842e (patch) | |
tree | 5c2187bbdfd6867100fab28dffdc09bae127e0d2 | |
parent | 65d88df1b8c57bc26346c341404c1d28ceb1676a (diff) | |
parent | e489ec703bb0e23d442bd81ea62d4b248d00e37c (diff) | |
download | keystone-f51b76bc7fbd72857b4dbea743fd9fe469e5842e.tar.gz |
Merge "Support project hierarchies in data driver tests"
-rw-r--r-- | keystone/tests/unit/test_backend.py | 249 |
1 files changed, 198 insertions, 51 deletions
diff --git a/keystone/tests/unit/test_backend.py b/keystone/tests/unit/test_backend.py index 6de953377..302fc2c29 100644 --- a/keystone/tests/unit/test_backend.py +++ b/keystone/tests/unit/test_backend.py @@ -87,6 +87,20 @@ class AssignmentTestHelperMixin(object): # 'entities': {'domains': [{'id': DEFAULT_DOMAIN, 'users': 3}, # {'projects': 3}, 5]}, # + # A project hierarchy can be specified within the 'projects' section by + # nesting the 'project' key, for example to create a project with three + # sub-projects you would use: + + 'projects': {'project': 3} + + # A more complex hierarchy can also be defined, for example the + # following would define three projects each containing a + # sub-project, each of which contain a further three sub-projects. + + 'projects': [{'project': {'project': 3}}, + {'project': {'project': 3}}, + {'project': {'project': 3}}] + # A list of groups and their members. In this case make users with # index 0 and 1 members of group with index 0. Users and Groups are # indexed in the order they appear in the 'entities' key above. @@ -122,31 +136,54 @@ class AssignmentTestHelperMixin(object): # 'inherited_to_projects' options to list_role_assignments.} """ - def create_entities(self, entity_pattern): - """Create the entities specified in the test plan. + def _handle_project_spec(self, test_data, domain_id, project_spec, + parent_id=None): + """Handle the creation of a project or hierarchy of projects. - Process the 'entities' key in the test plan, creating the requested - entities. Each created entity will be added to the array of entities - stored in the returned test_data object, e.g.: + project_spec may either be a count of the number of projects to + create, or it may be a list of the form: - test_data['users'] = [user[0], user[1]....] + [{'project': project_spec}, {'project': project_spec}, ...] + + This method is called recursively to handle the creation of a + hierarchy of projects. """ - def _create_entity_in_domain(entity_type, domain_id): - new_entity = {'name': uuid.uuid4().hex, 'domain_id': domain_id} - if entity_type == 'users': - new_entity = self.identity_api.create_user(new_entity) - elif entity_type == 'groups': - new_entity = self.identity_api.create_group(new_entity) - elif entity_type == 'projects': - new_entity['id'] = uuid.uuid4().hex - new_entity = self.resource_api.create_project(new_entity['id'], - new_entity) - else: - # Must be a bad test plan - raise exception.NotImplemented() - return new_entity + def _create_project(domain_id, parent_id): + new_project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, + 'domain_id': domain_id, 'parent_id': parent_id} + new_project = self.resource_api.create_project(new_project['id'], + new_project) + return new_project + + if isinstance(project_spec, list): + for this_spec in project_spec: + self._handle_project_spec( + test_data, domain_id, this_spec, parent_id=parent_id) + elif isinstance(project_spec, dict): + new_proj = _create_project(domain_id, parent_id) + test_data['projects'].append(new_proj) + self._handle_project_spec( + test_data, domain_id, project_spec['project'], + parent_id=new_proj['id']) + else: + for _ in range(project_spec): + test_data['projects'].append( + _create_project(domain_id, parent_id)) + + def _handle_domain_spec(self, test_data, domain_spec): + """Handle the creation of domains and their contents. + + domain_spec may either be a count of the number of empty domains to + create, a dict describing the domain contents, or a list of + domain_specs. + + In the case when a list is provided, this method calls itself + recursively to handle the list elements. + This method will insert any entities created into test_data + + """ def _create_domain(domain_id=None): if domain_id is None: new_domain = {'id': uuid.uuid4().hex, @@ -158,40 +195,59 @@ class AssignmentTestHelperMixin(object): # The test plan specified an existing domain to use return self.resource_api.get_domain(domain_id) - def _create_role(): - new_role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} - return self.role_api.create_role(new_role['id'], new_role) + def _create_entity_in_domain(entity_type, domain_id): + """Create a user or group entity in the domain.""" + + new_entity = {'name': uuid.uuid4().hex, 'domain_id': domain_id} + if entity_type == 'users': + new_entity = self.identity_api.create_user(new_entity) + elif entity_type == 'groups': + new_entity = self.identity_api.create_group(new_entity) + else: + # Must be a bad test plan + raise exception.NotImplemented() + return new_entity - def _handle_domain_spec(domain_spec): - """Handle the creation of domains and their contents. - - domain_spec may either be a count of the number of empty domains to - create, a dict describing the domain contents, or a list of - domain_specs. - - In the case when a list is provided, this method calls itself - recursively to handle the list elements. - - """ - if isinstance(domain_spec, list): - for x in domain_spec: - _handle_domain_spec(x) - elif isinstance(domain_spec, dict): - # If there is a domain ID specified, then use it - the_domain = _create_domain(domain_id=domain_spec.get('id')) - test_data['domains'].append(the_domain) - for entity_type, count in domain_spec.items(): - if entity_type == 'id': - # We already used this above to determine whether to - # use and existing domain - continue - for _ in range(count): + if isinstance(domain_spec, list): + for x in domain_spec: + self._handle_domain_spec(test_data, x) + elif isinstance(domain_spec, dict): + # If there is a domain ID specified, then use it + the_domain = _create_domain(domain_spec.get('id')) + test_data['domains'].append(the_domain) + for entity_type, value in domain_spec.items(): + if entity_type == 'id': + # We already used this above to determine whether to + # use and existing domain + continue + if entity_type == 'projects': + # If it's projects, we need to handle the potential + # specification of a project hierarchy + self._handle_project_spec( + test_data, the_domain['id'], value) + else: + # It's a count of number of entities + for _ in range(value): test_data[entity_type].append( _create_entity_in_domain( entity_type, the_domain['id'])) - else: - for _ in range(domain_spec): - test_data['domains'].append(_create_domain()) + else: + for _ in range(domain_spec): + test_data['domains'].append(_create_domain()) + + def create_entities(self, entity_pattern): + """Create the entities specified in the test plan. + + Process the 'entities' key in the test plan, creating the requested + entities. Each created entity will be added to the array of entities + stored in the returned test_data object, e.g.: + + test_data['users'] = [user[0], user[1]....] + + """ + def _create_role(): + new_role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + return self.role_api.create_role(new_role['id'], new_role) test_data = {} for entity in ['users', 'groups', 'domains', 'projects', 'roles']: @@ -200,7 +256,7 @@ class AssignmentTestHelperMixin(object): # Create any domains requested and, if specified, any entities within # those domains if 'domains' in entity_pattern: - _handle_domain_spec(entity_pattern['domains']) + self._handle_domain_spec(test_data, entity_pattern['domains']) # Create any roles requested if 'roles' in entity_pattern: @@ -6272,6 +6328,50 @@ class InheritanceTests(AssignmentTestHelperMixin): self.assertEqual(1, len(user_projects)) self.assertIn(root_project, user_projects) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project and sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': DEFAULT_DOMAIN_ID, 'users': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + # A direct role and an inherited role on the parent + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_test_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_tests( + test_plan_with_os_inherit_disabled, test_data) + def test_list_projects_for_user_with_inherited_group_grants(self): """Test inherited group roles. @@ -6441,6 +6541,53 @@ class InheritanceTests(AssignmentTestHelperMixin): self.assertEqual(1, len(user_projects)) self.assertIn(root_project, user_projects) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project ans sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': DEFAULT_DOMAIN_ID, 'users': 1, 'groups': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + # A direct role and an inherited role on the parent + 'assignments': [{'group': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'group': 0, 'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_test_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_tests( + test_plan_with_os_inherit_disabled, test_data) + class FilterTests(filtering.FilterTests): def test_list_entities_filtered(self): |