diff --git a/moto/organizations/models.py b/moto/organizations/models.py index a165395f7..0f3c67400 100644 --- a/moto/organizations/models.py +++ b/moto/organizations/models.py @@ -7,14 +7,6 @@ from moto.core import BaseBackend, BaseModel from moto.core.utils import unix_time from moto.organizations import utils -MASTER_ACCOUNT_ID = '123456789012' -MASTER_ACCOUNT_EMAIL = 'fakeorg@moto-example.com' -ORGANIZATION_ARN_FORMAT = 'arn:aws:organizations::{0}:organization/{1}' -MASTER_ACCOUNT_ARN_FORMAT = 'arn:aws:organizations::{0}:account/{1}/{0}' -ACCOUNT_ARN_FORMAT = 'arn:aws:organizations::{0}:account/{1}/{2}' -ROOT_ARN_FORMAT = 'arn:aws:organizations::{0}:root/{1}/{2}' -OU_ARN_FORMAT = 'arn:aws:organizations::{0}:ou/{1}/{2}' - class FakeOrganization(BaseModel): @@ -22,8 +14,8 @@ class FakeOrganization(BaseModel): self.id = utils.make_random_org_id() self.root_id = utils.make_random_root_id() self.feature_set = feature_set - self.master_account_id = MASTER_ACCOUNT_ID - self.master_account_email = MASTER_ACCOUNT_EMAIL + self.master_account_id = utils.MASTER_ACCOUNT_ID + self.master_account_email = utils.MASTER_ACCOUNT_EMAIL self.available_policy_types = [{ 'Type': 'SERVICE_CONTROL_POLICY', 'Status': 'ENABLED' @@ -31,11 +23,11 @@ class FakeOrganization(BaseModel): @property def arn(self): - return ORGANIZATION_ARN_FORMAT.format(self.master_account_id, self.id) + return utils.ORGANIZATION_ARN_FORMAT.format(self.master_account_id, self.id) @property def master_account_arn(self): - return MASTER_ACCOUNT_ARN_FORMAT.format(self.master_account_id, self.id) + return utils.MASTER_ACCOUNT_ARN_FORMAT.format(self.master_account_id, self.id) def describe(self): return { @@ -67,7 +59,7 @@ class FakeAccount(BaseModel): @property def arn(self): - return ACCOUNT_ARN_FORMAT.format( + return utils.ACCOUNT_ARN_FORMAT.format( self.master_account_id, self.organization_id, self.id @@ -109,7 +101,7 @@ class FakeOrganizationalUnit(BaseModel): self.id = utils.make_random_ou_id(organization.root_id) self.name = kwargs.get('Name') self.parent_id = kwargs.get('ParentId') - self._arn_format = OU_ARN_FORMAT + self._arn_format = utils.OU_ARN_FORMAT @property def arn(self): @@ -140,7 +132,7 @@ class FakeRoot(FakeOrganizationalUnit): 'Type': 'SERVICE_CONTROL_POLICY', 'Status': 'ENABLED' }] - self._arn_format = ROOT_ARN_FORMAT + self._arn_format = utils.ROOT_ARN_FORMAT def describe(self): return { diff --git a/moto/organizations/utils.py b/moto/organizations/utils.py index c7e5c71cd..007afa6ed 100644 --- a/moto/organizations/utils.py +++ b/moto/organizations/utils.py @@ -3,6 +3,14 @@ from __future__ import unicode_literals import random import string +MASTER_ACCOUNT_ID = '123456789012' +MASTER_ACCOUNT_EMAIL = 'fakeorg@moto-example.com' +ORGANIZATION_ARN_FORMAT = 'arn:aws:organizations::{0}:organization/{1}' +MASTER_ACCOUNT_ARN_FORMAT = 'arn:aws:organizations::{0}:account/{1}/{0}' +ACCOUNT_ARN_FORMAT = 'arn:aws:organizations::{0}:account/{1}/{2}' +ROOT_ARN_FORMAT = 'arn:aws:organizations::{0}:root/{1}/{2}' +OU_ARN_FORMAT = 'arn:aws:organizations::{0}:ou/{1}/{2}' + CHARSET = string.ascii_lowercase + string.digits ORG_ID_SIZE = 10 ROOT_ID_SIZE = 4 diff --git a/tests/test_organizations/object_syntax.py b/tests/test_organizations/object_syntax.py deleted file mode 100644 index 3fb86b9d5..000000000 --- a/tests/test_organizations/object_syntax.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Temporary functions for checking object structures while specing out -models. This module will go away. -""" - -import yaml -from moto import organizations as orgs - - -# utils -print(orgs.utils.make_random_org_id()) -root_id = orgs.utils.make_random_root_id() -print(root_id) -print(orgs.utils.make_random_ou_id(root_id)) -print(orgs.utils.make_random_account_id()) -print(orgs.utils.make_random_create_account_status_id()) - -# models -my_org = orgs.models.FakeOrganization(feature_set='ALL') -print(yaml.dump(my_org._describe())) -#assert False - -my_account = orgs.models.FakeAccount(my_org, AccountName='blee01', Email='blee01@moto-example.org') -print(yaml.dump(my_account)) -#assert False diff --git a/tests/test_organizations/organizations_test_utils.py b/tests/test_organizations/organizations_test_utils.py new file mode 100644 index 000000000..6548b1830 --- /dev/null +++ b/tests/test_organizations/organizations_test_utils.py @@ -0,0 +1,136 @@ +from __future__ import unicode_literals + +import six +import sure # noqa +import datetime +from moto.organizations import utils + +EMAIL_REGEX = "^.+@[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}|[0-9]{1,3}$" +ORG_ID_REGEX = r'o-[a-z0-9]{%s}' % utils.ORG_ID_SIZE +ROOT_ID_REGEX = r'r-[a-z0-9]{%s}' % utils.ROOT_ID_SIZE +OU_ID_REGEX = r'ou-[a-z0-9]{%s}-[a-z0-9]{%s}' % (utils.ROOT_ID_SIZE, utils.OU_ID_SUFFIX_SIZE) +ACCOUNT_ID_REGEX = r'[0-9]{%s}' % utils.ACCOUNT_ID_SIZE +CREATE_ACCOUNT_STATUS_ID_REGEX = r'car-[a-z0-9]{%s}' % utils.CREATE_ACCOUNT_STATUS_ID_SIZE + + +def test_make_random_org_id(): + org_id = utils.make_random_org_id() + org_id.should.match(ORG_ID_REGEX) + + +def test_make_random_root_id(): + root_id = utils.make_random_root_id() + root_id.should.match(ROOT_ID_REGEX) + + +def test_make_random_ou_id(): + root_id = utils.make_random_root_id() + ou_id = utils.make_random_ou_id(root_id) + ou_id.should.match(OU_ID_REGEX) + + +def test_make_random_account_id(): + account_id = utils.make_random_account_id() + account_id.should.match(ACCOUNT_ID_REGEX) + + +def test_make_random_create_account_status_id(): + create_account_status_id = utils.make_random_create_account_status_id() + create_account_status_id.should.match(CREATE_ACCOUNT_STATUS_ID_REGEX) + + +def validate_organization(response): + org = response['Organization'] + sorted(org.keys()).should.equal([ + 'Arn', + 'AvailablePolicyTypes', + 'FeatureSet', + 'Id', + 'MasterAccountArn', + 'MasterAccountEmail', + 'MasterAccountId', + ]) + org['Id'].should.match(ORG_ID_REGEX) + org['MasterAccountId'].should.equal(utils.MASTER_ACCOUNT_ID) + org['MasterAccountArn'].should.equal(utils.MASTER_ACCOUNT_ARN_FORMAT.format( + org['MasterAccountId'], + org['Id'], + )) + org['Arn'].should.equal(utils.ORGANIZATION_ARN_FORMAT.format( + org['MasterAccountId'], + org['Id'], + )) + org['MasterAccountEmail'].should.equal(utils.MASTER_ACCOUNT_EMAIL) + org['FeatureSet'].should.be.within(['ALL', 'CONSOLIDATED_BILLING']) + org['AvailablePolicyTypes'].should.equal([{ + 'Type': 'SERVICE_CONTROL_POLICY', + 'Status': 'ENABLED' + }]) + + +def validate_roots(org, response): + response.should.have.key('Roots').should.be.a(list) + response['Roots'].should_not.be.empty + root = response['Roots'][0] + root.should.have.key('Id').should.match(ROOT_ID_REGEX) + root.should.have.key('Arn').should.equal(utils.ROOT_ARN_FORMAT.format( + org['MasterAccountId'], + org['Id'], + root['Id'], + )) + root.should.have.key('Name').should.be.a(six.string_types) + root.should.have.key('PolicyTypes').should.be.a(list) + root['PolicyTypes'][0].should.have.key('Type').should.equal('SERVICE_CONTROL_POLICY') + root['PolicyTypes'][0].should.have.key('Status').should.equal('ENABLED') + + +def validate_organizational_unit(org, response): + response.should.have.key('OrganizationalUnit').should.be.a(dict) + ou = response['OrganizationalUnit'] + ou.should.have.key('Id').should.match(OU_ID_REGEX) + ou.should.have.key('Arn').should.equal(utils.OU_ARN_FORMAT.format( + org['MasterAccountId'], + org['Id'], + ou['Id'], + )) + ou.should.have.key('Name').should.be.a(six.string_types) + + +def validate_account(org, account): + sorted(account.keys()).should.equal([ + 'Arn', + 'Email', + 'Id', + 'JoinedMethod', + 'JoinedTimestamp', + 'Name', + 'Status', + ]) + account['Id'].should.match(ACCOUNT_ID_REGEX) + account['Arn'].should.equal(utils.ACCOUNT_ARN_FORMAT.format( + org['MasterAccountId'], + org['Id'], + account['Id'], + )) + account['Email'].should.match(EMAIL_REGEX) + account['JoinedMethod'].should.be.within(['INVITED', 'CREATED']) + account['Status'].should.be.within(['ACTIVE', 'SUSPENDED']) + account['Name'].should.be.a(six.string_types) + account['JoinedTimestamp'].should.be.a(datetime.datetime) + + +def validate_create_account_status(create_status): + sorted(create_status.keys()).should.equal([ + 'AccountId', + 'AccountName', + 'CompletedTimestamp', + 'Id', + 'RequestedTimestamp', + 'State', + ]) + create_status['Id'].should.match(CREATE_ACCOUNT_STATUS_ID_REGEX) + create_status['AccountId'].should.match(ACCOUNT_ID_REGEX) + create_status['AccountName'].should.be.a(six.string_types) + create_status['State'].should.equal('SUCCEEDED') + create_status['RequestedTimestamp'].should.be.a(datetime.datetime) + create_status['CompletedTimestamp'].should.be.a(datetime.datetime) diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py index 0ac2b61bf..c2f06774a 100644 --- a/tests/test_organizations/test_organizations_boto3.py +++ b/tests/test_organizations/test_organizations_boto3.py @@ -2,120 +2,24 @@ from __future__ import unicode_literals import boto3 import sure # noqa -import datetime import yaml -import six from moto import mock_organizations -from moto.organizations.models import ( - MASTER_ACCOUNT_ID, - MASTER_ACCOUNT_EMAIL, - ORGANIZATION_ARN_FORMAT, - MASTER_ACCOUNT_ARN_FORMAT, - ACCOUNT_ARN_FORMAT, - ROOT_ARN_FORMAT, - OU_ARN_FORMAT, +from .organizations_test_utils import ( + validate_organization, + validate_roots, + validate_organizational_unit, + validate_account, + validate_create_account_status, ) -from .test_organizations_utils import ( - ORG_ID_REGEX, - ROOT_ID_REGEX, - OU_ID_REGEX, - ACCOUNT_ID_REGEX, - CREATE_ACCOUNT_STATUS_ID_REGEX, -) - -EMAIL_REGEX = "^.+@[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}|[0-9]{1,3}$" - - -def validate_organization(response): - org = response['Organization'] - sorted(org.keys()).should.equal([ - 'Arn', - 'AvailablePolicyTypes', - 'FeatureSet', - 'Id', - 'MasterAccountArn', - 'MasterAccountEmail', - 'MasterAccountId', - ]) - org['Id'].should.match(ORG_ID_REGEX) - org['MasterAccountId'].should.equal(MASTER_ACCOUNT_ID) - org['MasterAccountArn'].should.equal(MASTER_ACCOUNT_ARN_FORMAT.format( - org['MasterAccountId'], - org['Id'], - )) - org['Arn'].should.equal(ORGANIZATION_ARN_FORMAT.format( - org['MasterAccountId'], - org['Id'], - )) - org['MasterAccountEmail'].should.equal(MASTER_ACCOUNT_EMAIL) - org['FeatureSet'].should.be.within(['ALL', 'CONSOLIDATED_BILLING']) - org['AvailablePolicyTypes'].should.equal([{ - 'Type': 'SERVICE_CONTROL_POLICY', - 'Status': 'ENABLED' - }]) - - -def validate_organizational_unit(org, response): - response.should.have.key('OrganizationalUnit').should.be.a(dict) - ou = response['OrganizationalUnit'] - ou.should.have.key('Id').should.match(OU_ID_REGEX) - ou.should.have.key('Arn').should.equal(OU_ARN_FORMAT.format( - org['MasterAccountId'], - org['Id'], - ou['Id'], - )) - ou.should.have.key('Name').should.be.a(six.string_types) - - -def validate_account(org, account): - sorted(account.keys()).should.equal([ - 'Arn', - 'Email', - 'Id', - 'JoinedMethod', - 'JoinedTimestamp', - 'Name', - 'Status', - ]) - account['Id'].should.match(ACCOUNT_ID_REGEX) - account['Arn'].should.equal(ACCOUNT_ARN_FORMAT.format( - org['MasterAccountId'], - org['Id'], - account['Id'], - )) - account['Email'].should.match(EMAIL_REGEX) - account['JoinedMethod'].should.be.within(['INVITED', 'CREATED']) - account['Status'].should.be.within(['ACTIVE', 'SUSPENDED']) - account['Name'].should.be.a(six.string_types) - account['JoinedTimestamp'].should.be.a(datetime.datetime) - - -def validate_create_account_status(create_status): - sorted(create_status.keys()).should.equal([ - 'AccountId', - 'AccountName', - 'CompletedTimestamp', - 'Id', - 'RequestedTimestamp', - 'State', - ]) - create_status['Id'].should.match(CREATE_ACCOUNT_STATUS_ID_REGEX) - create_status['AccountId'].should.match(ACCOUNT_ID_REGEX) - create_status['AccountName'].should.be.a(six.string_types) - create_status['State'].should.equal('SUCCEEDED') - create_status['RequestedTimestamp'].should.be.a(datetime.datetime) - create_status['CompletedTimestamp'].should.be.a(datetime.datetime) @mock_organizations def test_create_organization(): client = boto3.client('organizations', region_name='us-east-1') response = client.create_organization(FeatureSet='ALL') - #print(yaml.dump(response)) validate_organization(response) response['Organization']['FeatureSet'].should.equal('ALL') - #assert False @mock_organizations @@ -123,9 +27,7 @@ def test_describe_organization(): client = boto3.client('organizations', region_name='us-east-1') client.create_organization(FeatureSet='ALL') response = client.describe_organization() - #print(yaml.dump(response)) validate_organization(response) - #assert False # Organizational Units @@ -135,21 +37,7 @@ def test_list_roots(): client = boto3.client('organizations', region_name='us-east-1') org = client.create_organization(FeatureSet='ALL')['Organization'] response = client.list_roots() - #print(yaml.dump(response, default_flow_style=False)) - response.should.have.key('Roots').should.be.a(list) - response['Roots'].should_not.be.empty - root = response['Roots'][0] - root.should.have.key('Id').should.match(ROOT_ID_REGEX) - root.should.have.key('Arn').should.equal(ROOT_ARN_FORMAT.format( - org['MasterAccountId'], - org['Id'], - root['Id'], - )) - root.should.have.key('Name').should.be.a(six.string_types) - root.should.have.key('PolicyTypes').should.be.a(list) - root['PolicyTypes'][0].should.have.key('Type').should.equal('SERVICE_CONTROL_POLICY') - root['PolicyTypes'][0].should.have.key('Status').should.equal('ENABLED') - #assert False + validate_roots(org, response) @mock_organizations @@ -162,10 +50,8 @@ def test_create_organizational_unit(): ParentId=root_id, Name=ou_name, ) - #print(yaml.dump(response, default_flow_style=False)) validate_organizational_unit(org, response) response['OrganizationalUnit']['Name'].should.equal(ou_name) - #assert False @mock_organizations @@ -178,9 +64,7 @@ def test_describe_organizational_unit(): Name='ou01', )['OrganizationalUnit']['Id'] response = client.describe_organizational_unit(OrganizationalUnitId=ou_id) - print(yaml.dump(response, default_flow_style=False)) validate_organizational_unit(org, response) - #assert False @mock_organizations @@ -192,11 +76,9 @@ def test_list_organizational_units_for_parent(): client.create_organizational_unit(ParentId=root_id, Name='ou02') client.create_organizational_unit(ParentId=root_id, Name='ou03') response = client.list_organizational_units_for_parent(ParentId=root_id) - print(yaml.dump(response, default_flow_style=False)) response.should.have.key('OrganizationalUnits').should.be.a(list) for ou in response['OrganizationalUnits']: validate_organizational_unit(org, dict(OrganizationalUnit=ou)) - #assert False # Accounts @@ -212,10 +94,8 @@ def test_create_account(): create_status = client.create_account( AccountName=mockname, Email=mockemail )['CreateAccountStatus'] - #print(yaml.dump(create_status, default_flow_style=False)) validate_create_account_status(create_status) create_status['AccountName'].should.equal(mockname) - #assert False @mock_organizations @@ -226,11 +106,9 @@ def test_describe_account(): AccountName=mockname, Email=mockemail )['CreateAccountStatus']['AccountId'] response = client.describe_account(AccountId=account_id) - #print(yaml.dump(response, default_flow_style=False)) validate_account(org, response['Account']) response['Account']['Name'].should.equal(mockname) response['Account']['Email'].should.equal(mockemail) - #assert False @mock_organizations @@ -242,7 +120,6 @@ def test_list_accounts(): email = name + '@' + mockdomain client.create_account(AccountName=name, Email=email) response = client.list_accounts() - #print(yaml.dump(response, default_flow_style=False)) response.should.have.key('Accounts') accounts = response['Accounts'] len(accounts).should.equal(5) @@ -250,7 +127,6 @@ def test_list_accounts(): validate_account(org, account) accounts[3]['Name'].should.equal(mockname + '3') accounts[2]['Email'].should.equal(mockname + '2' + '@' + mockdomain) - #assert False @mock_organizations @@ -263,9 +139,7 @@ def test_list_accounts_for_parent(): Email=mockemail, )['CreateAccountStatus']['AccountId'] response = client.list_accounts_for_parent(ParentId=root_id) - #print(yaml.dump(response, default_flow_style=False)) account_id.should.be.within([account['Id'] for account in response['Accounts']]) - #assert False @mock_organizations @@ -284,9 +158,7 @@ def test_move_account(): DestinationParentId=ou01_id, ) response = client.list_accounts_for_parent(ParentId=ou01_id) - #print(yaml.dump(response, default_flow_style=False)) account_id.should.be.within([account['Id'] for account in response['Accounts']]) - #assert False @mock_organizations @@ -297,18 +169,15 @@ def test_list_parents_for_ou(): ou01 = client.create_organizational_unit(ParentId=root_id, Name='ou01') ou01_id = ou01['OrganizationalUnit']['Id'] response01 = client.list_parents(ChildId=ou01_id) - #print(yaml.dump(response01, default_flow_style=False)) response01.should.have.key('Parents').should.be.a(list) response01['Parents'][0].should.have.key('Id').should.equal(root_id) response01['Parents'][0].should.have.key('Type').should.equal('ROOT') ou02 = client.create_organizational_unit(ParentId=ou01_id, Name='ou02') ou02_id = ou02['OrganizationalUnit']['Id'] response02 = client.list_parents(ChildId=ou02_id) - #print(yaml.dump(response02, default_flow_style=False)) response02.should.have.key('Parents').should.be.a(list) response02['Parents'][0].should.have.key('Id').should.equal(ou01_id) response02['Parents'][0].should.have.key('Type').should.equal('ORGANIZATIONAL_UNIT') - #assert False @mock_organizations @@ -332,16 +201,13 @@ def test_list_parents_for_accounts(): DestinationParentId=ou01_id, ) response01 = client.list_parents(ChildId=account01_id) - #print(yaml.dump(response01, default_flow_style=False)) response01.should.have.key('Parents').should.be.a(list) response01['Parents'][0].should.have.key('Id').should.equal(root_id) response01['Parents'][0].should.have.key('Type').should.equal('ROOT') response02 = client.list_parents(ChildId=account02_id) - #print(yaml.dump(response02, default_flow_style=False)) response02.should.have.key('Parents').should.be.a(list) response02['Parents'][0].should.have.key('Id').should.equal(ou01_id) response02['Parents'][0].should.have.key('Type').should.equal('ORGANIZATIONAL_UNIT') - #assert False @mock_organizations @@ -370,10 +236,6 @@ def test_list_chidlren(): response02 = client.list_children(ParentId=root_id, ChildType='ORGANIZATIONAL_UNIT') response03 = client.list_children(ParentId=ou01_id, ChildType='ACCOUNT') response04 = client.list_children(ParentId=ou01_id, ChildType='ORGANIZATIONAL_UNIT') - #print(yaml.dump(response01, default_flow_style=False)) - #print(yaml.dump(response02, default_flow_style=False)) - #print(yaml.dump(response03, default_flow_style=False)) - #print(yaml.dump(response04, default_flow_style=False)) response01['Children'][0]['Id'].should.equal(account01_id) response01['Children'][0]['Type'].should.equal('ACCOUNT') response02['Children'][0]['Id'].should.equal(ou01_id) @@ -382,4 +244,3 @@ def test_list_chidlren(): response03['Children'][0]['Type'].should.equal('ACCOUNT') response04['Children'][0]['Id'].should.equal(ou02_id) response04['Children'][0]['Type'].should.equal('ORGANIZATIONAL_UNIT') - #assert False diff --git a/tests/test_organizations/test_organizations_utils.py b/tests/test_organizations/test_organizations_utils.py deleted file mode 100644 index d27201446..000000000 --- a/tests/test_organizations/test_organizations_utils.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import unicode_literals - -import sure # noqa -from moto.organizations import utils - -ORG_ID_REGEX = r'o-[a-z0-9]{%s}' % utils.ORG_ID_SIZE -ROOT_ID_REGEX = r'r-[a-z0-9]{%s}' % utils.ROOT_ID_SIZE -OU_ID_REGEX = r'ou-[a-z0-9]{%s}-[a-z0-9]{%s}' % (utils.ROOT_ID_SIZE, utils.OU_ID_SUFFIX_SIZE) -ACCOUNT_ID_REGEX = r'[0-9]{%s}' % utils.ACCOUNT_ID_SIZE -CREATE_ACCOUNT_STATUS_ID_REGEX = r'car-[a-z0-9]{%s}' % utils.CREATE_ACCOUNT_STATUS_ID_SIZE - - -def test_make_random_org_id(): - org_id = utils.make_random_org_id() - org_id.should.match(ORG_ID_REGEX) - - -def test_make_random_root_id(): - root_id = utils.make_random_root_id() - root_id.should.match(ROOT_ID_REGEX) - - -def test_make_random_ou_id(): - root_id = utils.make_random_root_id() - ou_id = utils.make_random_ou_id(root_id) - ou_id.should.match(OU_ID_REGEX) - - -def test_make_random_account_id(): - account_id = utils.make_random_account_id() - account_id.should.match(ACCOUNT_ID_REGEX) - - -def test_make_random_create_account_status_id(): - create_account_status_id = utils.make_random_create_account_status_id() - create_account_status_id.should.match(CREATE_ACCOUNT_STATUS_ID_REGEX)