From de40ce1e925803bb48e9576e0692ad3a6c5aa6d4 Mon Sep 17 00:00:00 2001 From: Henry Nash Date: Tue, 26 Jan 2016 03:38:15 +0000 Subject: [PATCH] Add tests in preparation of projects acting as a domain In subsequent patches projects will start acting as domains. This patch adds REST level tests, protected by the wip decorator, that will validate the external view of projects acting as domains. This patch also modifies the unit.new_project_ref() test helper to better represent that parent_id is an optional parameter for 1st level projects (since their parent is defined by their domain_id). This will allow us to better test the various mixtures of domain_id and parent_id in future patches. This exposed a number of test cases which did not allow for the fact that parent_id may not have been set by new_project_ref(). Co-Authored-By: Henrique Truta Co-Authored-By: Rodrigo Duarte Partially implements: blueprint reseller Change-Id: Ibd4b6ca35746ad497523c01352b7b6b985ac8a7f --- keystone/tests/unit/core.py | 7 +- keystone/tests/unit/test_backend.py | 58 ++++++------- keystone/tests/unit/test_backend_ldap.py | 10 +-- keystone/tests/unit/test_backend_sql.py | 4 +- keystone/tests/unit/test_v3.py | 3 +- keystone/tests/unit/test_v3_resource.py | 102 +++++++++++++++++++++-- 6 files changed, 138 insertions(+), 46 deletions(-) diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index 0c5776a646..c58d2c7444 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -307,16 +307,19 @@ def new_domain_ref(**kwargs): return ref -def new_project_ref(domain_id=None, parent_id=None, is_domain=False, **kwargs): +def new_project_ref(domain_id=None, is_domain=False, **kwargs): ref = { 'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'description': uuid.uuid4().hex, 'enabled': True, 'domain_id': domain_id, - 'parent_id': parent_id, 'is_domain': is_domain, } + # NOTE(henry-nash): We don't include parent_id in the initial list above + # since specifying it is optional depending on where the project sits in + # the hierarchy (and a parent_id of None has meaning - i.e. it's a top + # level project). ref.update(kwargs) return ref diff --git a/keystone/tests/unit/test_backend.py b/keystone/tests/unit/test_backend.py index 79d2b7615f..fd0edf09a0 100644 --- a/keystone/tests/unit/test_backend.py +++ b/keystone/tests/unit/test_backend.py @@ -2212,7 +2212,7 @@ class IdentityTests(AssignmentTestHelperMixin): project = unit.new_project_ref( name=unicode_project_name, domain_id=CONF.identity.default_domain_id) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) self.resource_api.update_project(project['id'], project) self.resource_api.delete_project(project['id']) @@ -2474,7 +2474,7 @@ class IdentityTests(AssignmentTestHelperMixin): def test_list_projects_with_multiple_filters(self): # Create a project project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) # Build driver hints with the project's name and inexistent description hints = driver_hints.Hints() @@ -2548,7 +2548,7 @@ class IdentityTests(AssignmentTestHelperMixin): project = unit.new_project_ref(domain_id=domain_id, is_domain=is_domain) project_id = project['id'] - self.resource_api.create_project(project_id, project) + project = self.resource_api.create_project(project_id, project) projects = [project] for i in range(1, hierarchy_size): @@ -2665,18 +2665,11 @@ class IdentityTests(AssignmentTestHelperMixin): @unit.skip_if_no_multiple_domains_support def test_create_subproject_acting_as_domain_fails(self): - root_project = {'id': uuid.uuid4().hex, - 'domain_id': DEFAULT_DOMAIN_ID, - 'name': uuid.uuid4().hex, - 'parent_id': None, - 'is_domain': True} + root_project = unit.new_project_ref(is_domain=True) self.resource_api.create_project(root_project['id'], root_project) - sub_project = {'id': uuid.uuid4().hex, - 'domain_id': DEFAULT_DOMAIN_ID, - 'name': uuid.uuid4().hex, - 'parent_id': root_project['id'], - 'is_domain': True} + sub_project = unit.new_project_ref(is_domain=True, + parent_id=root_project['id']) # Creation of sub projects acting as domains is not allowed yet self.assertRaises(exception.ValidationError, @@ -2719,8 +2712,7 @@ class IdentityTests(AssignmentTestHelperMixin): @unit.skip_if_no_multiple_domains_support def test_create_project_passing_is_domain_flag_true(self): - project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID, - is_domain=True) + project = unit.new_project_ref(is_domain=True) ref = self.resource_api.create_project(project['id'], project) self.assertTrue(ref['is_domain']) @@ -2732,11 +2724,12 @@ class IdentityTests(AssignmentTestHelperMixin): ref = self.resource_api.create_project(project['id'], project) self.assertIs(False, ref['is_domain']) - @test_utils.wip('waiting for projects acting as domains implementation') + @test_utils.wip('waiting for support for parent_id to imply domain_id') def test_create_project_with_parent_id_and_without_domain_id(self): - project = unit.new_project_ref(domain_id=None) + # First create a domain + project = unit.new_project_ref(is_domain=True) self.resource_api.create_project(project['id'], project) - + # Now create a child by just naming the parent_id sub_project = unit.new_project_ref(parent_id=project['id']) ref = self.resource_api.create_project(sub_project['id'], sub_project) @@ -2745,9 +2738,10 @@ class IdentityTests(AssignmentTestHelperMixin): @test_utils.wip('waiting for projects acting as domains implementation') def test_create_project_with_domain_id_and_without_parent_id(self): - project = unit.new_project_ref(parent_id=None) + # First create a domain + project = unit.new_project_ref(is_domain=True) self.resource_api.create_project(project['id'], project) - + # Now create a child by just naming the domain_id sub_project = unit.new_project_ref(domain_id=project['id']) ref = self.resource_api.create_project(sub_project['id'], sub_project) @@ -2796,7 +2790,7 @@ class IdentityTests(AssignmentTestHelperMixin): def test_list_projects_in_subtree_with_circular_reference(self): project1 = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID) - self.resource_api.create_project(project1['id'], project1) + project1 = self.resource_api.create_project(project1['id'], project1) project2 = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID, parent_id=project1['id']) @@ -3674,7 +3668,7 @@ class IdentityTests(AssignmentTestHelperMixin): # Creating a project with no description attribute. project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID) del project['description'] - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) # Add a description attribute. project['description'] = uuid.uuid4().hex @@ -3687,7 +3681,7 @@ class IdentityTests(AssignmentTestHelperMixin): # Creating a project with no description attribute. project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID) del project['description'] - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) # Add a description attribute. project['description'] = '' @@ -3698,12 +3692,14 @@ class IdentityTests(AssignmentTestHelperMixin): def test_domain_crud(self): domain = unit.new_domain_ref() - self.resource_api.create_domain(domain['id'], domain) + domain_ref = self.resource_api.create_domain(domain['id'], domain) + self.assertDictEqual(domain, domain_ref) domain_ref = self.resource_api.get_domain(domain['id']) self.assertDictEqual(domain, domain_ref) domain['name'] = uuid.uuid4().hex - self.resource_api.update_domain(domain['id'], domain) + domain_ref = self.resource_api.update_domain(domain['id'], domain) + self.assertDictEqual(domain, domain_ref) domain_ref = self.resource_api.get_domain(domain['id']) self.assertDictEqual(domain, domain_ref) @@ -6365,10 +6361,12 @@ class InheritanceTests(AssignmentTestHelperMixin): # Enable OS-INHERIT extension self.config_fixture.config(group='os_inherit', enabled=True) root_project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID) - self.resource_api.create_project(root_project['id'], root_project) + root_project = self.resource_api.create_project(root_project['id'], + root_project) leaf_project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID, parent_id=root_project['id']) - self.resource_api.create_project(leaf_project['id'], leaf_project) + leaf_project = self.resource_api.create_project(leaf_project['id'], + leaf_project) user = unit.new_user_ref(domain_id=DEFAULT_DOMAIN_ID) user = self.identity_api.create_user(user) @@ -6557,10 +6555,12 @@ class InheritanceTests(AssignmentTestHelperMixin): """ self.config_fixture.config(group='os_inherit', enabled=True) root_project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID) - self.resource_api.create_project(root_project['id'], root_project) + root_project = self.resource_api.create_project(root_project['id'], + root_project) leaf_project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID, parent_id=root_project['id']) - self.resource_api.create_project(leaf_project['id'], leaf_project) + leaf_project = self.resource_api.create_project(leaf_project['id'], + leaf_project) user = unit.new_user_ref(domain_id=DEFAULT_DOMAIN_ID) user = self.identity_api.create_user(user) diff --git a/keystone/tests/unit/test_backend_ldap.py b/keystone/tests/unit/test_backend_ldap.py index 36ccecf40b..46349196dd 100644 --- a/keystone/tests/unit/test_backend_ldap.py +++ b/keystone/tests/unit/test_backend_ldap.py @@ -1477,7 +1477,7 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase): project = unit.new_project_ref( domain_id=CONF.identity.default_domain_id) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) project_ref = self.resource_api.get_project(project['id']) self.assertDictEqual(project, project_ref) @@ -1501,7 +1501,7 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase): domain_id=CONF.identity.default_domain_id) project_id = project['id'] # Create a project - self.resource_api.create_project(project_id, project) + project = self.resource_api.create_project(project_id, project) self.resource_api.get_project(project_id) updated_project = copy.deepcopy(project) updated_project['description'] = uuid.uuid4().hex @@ -1548,7 +1548,7 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase): def test_update_is_domain_field(self): domain = self._get_domain_fixture() project = unit.new_project_ref(domain_id=domain['id']) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) # Try to update the is_domain field to True project['is_domain'] = True @@ -1908,7 +1908,7 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity): project = unit.new_project_ref( domain_id=CONF.identity.default_domain_id) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) project_ref = self.resource_api.get_project(project['id']) # self.resource_api.create_project adds an enabled @@ -2512,7 +2512,7 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, unit.SQLDriverOverrides, domain = unit.new_domain_ref() project = unit.new_project_ref(domain_id=domain['id']) self.resource_api.create_domain(domain['id'], domain) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) project_ref = self.resource_api.get_project(project['id']) self.assertDictEqual(project, project_ref) diff --git a/keystone/tests/unit/test_backend_sql.py b/keystone/tests/unit/test_backend_sql.py index 265325d86a..f23358daba 100644 --- a/keystone/tests/unit/test_backend_sql.py +++ b/keystone/tests/unit/test_backend_sql.py @@ -270,8 +270,8 @@ class SqlIdentity(SqlTests, test_backend.IdentityTests): self.assertEqual(arbitrary_value, ref[arbitrary_key]) self.assertIsNone(ref.get('extra')) - project['name'] = uuid.uuid4().hex - ref = self.resource_api.update_project(project['id'], project) + ref['name'] = uuid.uuid4().hex + ref = self.resource_api.update_project(ref['id'], ref) self.assertEqual(arbitrary_value, ref[arbitrary_key]) self.assertEqual(arbitrary_value, ref['extra'][arbitrary_key]) diff --git a/keystone/tests/unit/test_v3.py b/keystone/tests/unit/test_v3.py index efd2ad94a8..5013ad765c 100644 --- a/keystone/tests/unit/test_v3.py +++ b/keystone/tests/unit/test_v3.py @@ -315,7 +315,8 @@ class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase, self.project = unit.new_project_ref(domain_id=self.domain_id) self.project_id = self.project['id'] - self.resource_api.create_project(self.project_id, self.project) + self.project = self.resource_api.create_project(self.project_id, + self.project) self.user = unit.create_user(self.identity_api, domain_id=self.domain_id) diff --git a/keystone/tests/unit/test_v3_resource.py b/keystone/tests/unit/test_v3_resource.py index 7946c9a5ca..2a0ab12c19 100644 --- a/keystone/tests/unit/test_v3_resource.py +++ b/keystone/tests/unit/test_v3_resource.py @@ -20,6 +20,7 @@ from keystone.common import controller from keystone import exception from keystone.tests import unit from keystone.tests.unit import test_v3 +from keystone.tests.unit import utils as test_utils CONF = cfg.CONF @@ -92,6 +93,42 @@ class ResourceTestCase(test_v3.RestfulTestCase, '/domains', body={'domain': ref}) + @test_utils.wip('waiting for projects acting as domains implementation') + def test_create_domain_creates_is_domain_project(self): + """Call ``POST /domains`` and check a project that acts as a domain + is created. + """ + # Create a new domain + domain_ref = unit.new_domain_ref() + r = self.post('/domains', body={'domain': domain_ref}) + self.assertValidDomainResponse(r, domain_ref) + + # Retrieve its correspondent project + r = self.get('/projects/%(project_id)s' % { + 'project_id': r.result['domain']['id']}) + self.assertValidProjectResponse(r) + + # The created project has is_domain flag as True + self.assertTrue(r.result['project']['is_domain']) + + # And its parent_id and domain_id attributes are equal + self.assertIsNone(r.result['project']['parent_id']) + self.assertIsNone(r.result['project']['domain_id']) + + @test_utils.wip('waiting for projects acting as domains implementation') + def test_create_is_domain_project_creates_domain(self): + """Call ``POST /projects`` is_domain and check a domain is created.""" + # Create a new project that acts as a domain + project_ref = unit.new_project_ref(domain_id=None, is_domain=True) + r = self.post('/projects', body={'project': project_ref}) + self.assertValidProjectResponse(r) + + # Retrieve its correspondent domain + r = self.get('/domains/%(domain_id)s' % { + 'domain_id': r.result['project']['id']}) + self.assertValidDomainResponse(r) + self.assertIsNotNone(r.result['domain']) + def test_list_domains(self): """Call ``GET /domains``.""" resource_url = '/domains' @@ -148,6 +185,28 @@ class ResourceTestCase(test_v3.RestfulTestCase, 'domain_id': self.domain_id}, body={'domain': ref}) + @test_utils.wip('waiting for projects acting as domains implementation') + def test_update_domain_updates_is_domain_project(self): + """Call ``PATCH /domains`` and check the project that acts as a domain + is updated. + """ + # Create a new domain + domain_ref = unit.new_domain_ref() + r = self.post('/domains', body={'domain': domain_ref}) + self.assertValidDomainResponse(r, domain_ref) + + # Disable it + self.patch('/domains/%s' % r.result['domain']['id'], + body={'domain': {'enabled': False}}) + + # Retrieve its correspondent project + r = self.get('/projects/%(project_id)s' % { + 'project_id': r.result['domain']['id']}) + self.assertValidProjectResponse(r) + + # The created project is disabled as well + self.assertFalse(r.result['project']['enabled']) + def test_disable_domain(self): """Call ``PATCH /domains/{domain_id}`` (set enabled=False).""" # Create a 2nd set of entities in a 2nd domain @@ -261,7 +320,7 @@ class ResourceTestCase(test_v3.RestfulTestCase, self.resource_api.create_domain(domain2['id'], domain2) project2 = unit.new_project_ref(domain_id=domain2['id']) - self.resource_api.create_project(project2['id'], project2) + project2 = self.resource_api.create_project(project2['id'], project2) user2 = unit.new_user_ref(domain_id=domain2['id'], project_id=project2['id']) @@ -313,6 +372,29 @@ class ResourceTestCase(test_v3.RestfulTestCase, r = self.credential_api.get_credential(credential['id']) self.assertDictEqual(credential, r) + @test_utils.wip('waiting for projects acting as domains implementation') + def test_delete_domain_deletes_is_domain_project(self): + """Call ``DELETE /domains`` and check the project that acts as a domain + is deleted. + """ + # Create a new domain + domain_ref = unit.new_domain_ref() + r = self.post('/domains', body={'domain': domain_ref}) + self.assertValidDomainResponse(r, domain_ref) + + # Retrieve its correspondent project + self.get('/projects/%(project_id)s' % { + 'project_id': r.result['domain']['id']}) + + # Delete the domain + self.patch('/domains/%s' % r.result['domain']['id'], + body={'domain': {'enabled': False}}) + self.delete('/domains/%s' % r.result['domain']['id']) + + # The created project is deleted as well + self.get('/projects/%(project_id)s' % { + 'project_id': r.result['domain']['id']}, expected_status=404) + def test_delete_default_domain(self): # Need to disable it first. self.patch('/domains/%(domain_id)s' % { @@ -369,7 +451,8 @@ class ResourceTestCase(test_v3.RestfulTestCase, self.resource_api.create_domain(domain['id'], domain) root_project = unit.new_project_ref(domain_id=domain['id']) - self.resource_api.create_project(root_project['id'], root_project) + root_project = self.resource_api.create_project(root_project['id'], + root_project) leaf_project = unit.new_project_ref( domain_id=domain['id'], @@ -984,7 +1067,8 @@ class ResourceTestCase(test_v3.RestfulTestCase, def test_update_project(self): """Call ``PATCH /projects/{project_id}``.""" - ref = unit.new_project_ref(domain_id=self.domain_id) + ref = unit.new_project_ref(domain_id=self.domain_id, + parent_id=self.project['parent_id']) del ref['id'] r = self.patch( '/projects/%(project_id)s' % { @@ -999,7 +1083,8 @@ class ResourceTestCase(test_v3.RestfulTestCase, self.config_fixture.config(group='resource', project_name_url_safe='off') ref = unit.new_project_ref(name=unsafe_name, - domain_id=self.domain_id) + domain_id=self.domain_id, + parent_id=self.project['parent_id']) del ref['id'] self.patch( '/projects/%(project_id)s' % { @@ -1011,7 +1096,8 @@ class ResourceTestCase(test_v3.RestfulTestCase, self.config_fixture.config(group='resource', project_name_url_safe=config_setting) ref = unit.new_project_ref(name=unsafe_name, - domain_id=self.domain_id) + domain_id=self.domain_id, + parent_id=self.project['parent_id']) del ref['id'] self.patch( '/projects/%(project_id)s' % { @@ -1025,7 +1111,8 @@ class ResourceTestCase(test_v3.RestfulTestCase, # By default, we should be able to create unsafe names ref = unit.new_project_ref(name=unsafe_name, - domain_id=self.domain_id) + domain_id=self.domain_id, + parent_id=self.project['parent_id']) del ref['id'] self.patch( '/projects/%(project_id)s' % { @@ -1035,7 +1122,7 @@ class ResourceTestCase(test_v3.RestfulTestCase, def test_update_project_domain_id(self): """Call ``PATCH /projects/{project_id}`` with domain_id.""" project = unit.new_project_ref(domain_id=self.domain['id']) - self.resource_api.create_project(project['id'], project) + project = self.resource_api.create_project(project['id'], project) project['domain_id'] = CONF.identity.default_domain_id r = self.patch('/projects/%(project_id)s' % { 'project_id': project['id']}, @@ -1069,6 +1156,7 @@ class ResourceTestCase(test_v3.RestfulTestCase, body={'project': project}) self.assertFalse(resp.result['project']['is_domain']) + project['parent_id'] = resp.result['project']['parent_id'] project['is_domain'] = True self.patch('/projects/%(project_id)s' % { 'project_id': resp.result['project']['id']},