Properly report initial clin information

Includes fixed up state machine test as well as adds some missing dependencies
This commit is contained in:
tomdds 2020-01-24 11:01:53 -05:00
parent 81054b2ff0
commit ea040a914e
7 changed files with 208 additions and 81 deletions

View File

@ -36,6 +36,7 @@ transitions = "*"
azure-mgmt-consumption = "*" azure-mgmt-consumption = "*"
adal = "*" adal = "*"
azure-identity = "*" azure-identity = "*"
azure-keyvault = "*"
[dev-packages] [dev-packages]
bandit = "*" bandit = "*"

82
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "3760f0b1df1156211d671afa2eb417b7bf980aa33d2f74d390e8eed6a3ce8c8b" "sha256": "4dbb023bcb860eb6dc56e1c201c91f272e1e67ad03e5e5eeb3a7a7fdff350eed"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -26,10 +26,10 @@
}, },
"alembic": { "alembic": {
"hashes": [ "hashes": [
"sha256:3b0cb1948833e062f4048992fbc97ecfaaaac24aaa0d83a1202a99fb58af8c6d" "sha256:d412982920653db6e5a44bfd13b1d0db5685cbaaccaf226195749c706e1e862a"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.3.2" "version": "==1.3.3"
}, },
"amqp": { "amqp": {
"hashes": [ "hashes": [
@ -68,6 +68,28 @@
"index": "pypi", "index": "pypi",
"version": "==1.2.0" "version": "==1.2.0"
}, },
"azure-keyvault": {
"hashes": [
"sha256:76f75cb83929f312a08616d426ad6f597f1beae180131cf445876fb88f2c8ef1",
"sha256:e85f5bd6cb4f10b3248b99bbf02e3acc6371d366846897027d4153f18025a2d7"
],
"index": "pypi",
"version": "==4.0.0"
},
"azure-keyvault-keys": {
"hashes": [
"sha256:2983fa42e20a0e6bf6b87976716129c108e613e0292d34c5b0f0c8dc1d488e89",
"sha256:38c27322637a2c52620a8b96da1942ad6a8d22d09b5a01f6fa257f7a51e52ed0"
],
"version": "==4.0.0"
},
"azure-keyvault-secrets": {
"hashes": [
"sha256:2eae9264a8f6f59277e1a9bfdbc8b0a15969ee5a80d8efe403d7744805b4a481",
"sha256:97a602406a833e8f117c540c66059c818f4321a35168dd17365fab1e4527d718"
],
"version": "==4.0.0"
},
"azure-mgmt-authorization": { "azure-mgmt-authorization": {
"hashes": [ "hashes": [
"sha256:31e875a34ac2c5d6fefe77b4a8079a8b2bdbe9edb957e47e8b44222fb212d6a7", "sha256:31e875a34ac2c5d6fefe77b4a8079a8b2bdbe9edb957e47e8b44222fb212d6a7",
@ -232,6 +254,14 @@
], ],
"version": "==2.8" "version": "==2.8"
}, },
"dataclasses": {
"hashes": [
"sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836",
"sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"
],
"markers": "python_version < '3.7'",
"version": "==0.7"
},
"flask": { "flask": {
"hashes": [ "hashes": [
"sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52",
@ -325,9 +355,9 @@
}, },
"mako": { "mako": {
"hashes": [ "hashes": [
"sha256:a36919599a9b7dc5d86a7a8988f23a9a3a3d083070023bab23d64f7f1d1e0a4b" "sha256:2984a6733e1d472796ceef37ad48c26f4a984bb18119bb2dbc37a44d8f6e75a4"
], ],
"version": "==1.1.0" "version": "==1.1.1"
}, },
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
@ -584,10 +614,10 @@
}, },
"sqlalchemy": { "sqlalchemy": {
"hashes": [ "hashes": [
"sha256:bfb8f464a5000b567ac1d350b9090cf081180ec1ab4aa87e7bca12dab25320ec" "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.3.12" "version": "==1.3.13"
}, },
"sqlalchemy-json": { "sqlalchemy-json": {
"hashes": [ "hashes": [
@ -620,10 +650,10 @@
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
], ],
"version": "==1.25.7" "version": "==1.25.8"
}, },
"vine": { "vine": {
"hashes": [ "hashes": [
@ -657,10 +687,10 @@
}, },
"zipp": { "zipp": {
"hashes": [ "hashes": [
"sha256:57147f6b0403b59f33fd357f169f860e031303415aeb7d04ede4839d23905ab8", "sha256:b338014b9bc7102ca69e0fb96ed07215a8954d2989bc5d83658494ab2ba634af",
"sha256:7ae5ccaca427bafa9760ac3cd8f8c244bfc259794b5b6bb9db4dda2241575d09" "sha256:e013e7800f60ec4dde789ebf4e9f7a54236e4bbf5df2a1a4e20ce9e1d9609d67"
], ],
"version": "==2.0.0" "version": "==2.0.1"
} }
}, },
"develop": { "develop": {
@ -671,6 +701,14 @@
], ],
"version": "==1.4.3" "version": "==1.4.3"
}, },
"appnope": {
"hashes": [
"sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0",
"sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"
],
"markers": "sys_platform == 'darwin'",
"version": "==0.1.0"
},
"argh": { "argh": {
"hashes": [ "hashes": [
"sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3", "sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3",
@ -1062,11 +1100,11 @@
}, },
"pexpect": { "pexpect": {
"hashes": [ "hashes": [
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937",
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb" "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"
], ],
"markers": "sys_platform != 'win32'", "markers": "sys_platform != 'win32'",
"version": "==4.7.0" "version": "==4.8.0"
}, },
"pickleshare": { "pickleshare": {
"hashes": [ "hashes": [
@ -1325,10 +1363,10 @@
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
], ],
"version": "==1.25.7" "version": "==1.25.8"
}, },
"watchdog": { "watchdog": {
"hashes": [ "hashes": [
@ -1359,10 +1397,10 @@
}, },
"zipp": { "zipp": {
"hashes": [ "hashes": [
"sha256:57147f6b0403b59f33fd357f169f860e031303415aeb7d04ede4839d23905ab8", "sha256:b338014b9bc7102ca69e0fb96ed07215a8954d2989bc5d83658494ab2ba634af",
"sha256:7ae5ccaca427bafa9760ac3cd8f8c244bfc259794b5b6bb9db4dda2241575d09" "sha256:e013e7800f60ec4dde789ebf4e9f7a54236e4bbf5df2a1a4e20ce9e1d9609d67"
], ],
"version": "==2.0.0" "version": "==2.0.1"
} }
} }
} }

View File

@ -327,12 +327,12 @@ class TaskOrderBillingCreationCSPPayload(BaseCSPPayload):
class TaskOrderBillingCreationCSPResult(AliasModel): class TaskOrderBillingCreationCSPResult(AliasModel):
task_order_billing_verify_url: str task_order_billing_verify_url: str
retry_after: int task_order_retry_after: int
class Config: class Config:
fields = { fields = {
"task_order_billing_verify_url": "Location", "task_order_billing_verify_url": "Location",
"retry_after": "Retry-After", "task_order_retry_after": "Retry-After",
} }
@ -358,11 +358,11 @@ class TaskOrderBillingVerificationCSPResult(AliasModel):
class BillingInstructionCSPPayload(BaseCSPPayload): class BillingInstructionCSPPayload(BaseCSPPayload):
amount: float initial_clin_amount: float
start_date: str initial_clin_start_date: str
end_date: str initial_clin_end_date: str
clin_type: str initial_clin_type: str
task_order_id: str initial_task_order_id: str
billing_account_name: str billing_account_name: str
billing_profile_name: str billing_profile_name: str
@ -646,7 +646,8 @@ class MockCloudProvider(CloudProviderInterface):
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION) self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION) self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
return { return BillingProfileTenantAccessCSPResult(
**{
"id": "/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/billingProfiles/KQWI-W2SU-BG7-TGB/billingRoleAssignments/40000000-aaaa-bbbb-cccc-100000000000_0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d", "id": "/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/billingProfiles/KQWI-W2SU-BG7-TGB/billingRoleAssignments/40000000-aaaa-bbbb-cccc-100000000000_0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d",
"name": "40000000-aaaa-bbbb-cccc-100000000000_0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d", "name": "40000000-aaaa-bbbb-cccc-100000000000_0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d",
"properties": { "properties": {
@ -659,6 +660,62 @@ class MockCloudProvider(CloudProviderInterface):
}, },
"type": "Microsoft.Billing/billingRoleAssignments", "type": "Microsoft.Billing/billingRoleAssignments",
} }
).dict()
def create_task_order_billing_creation(self, payload: TaskOrderBillingCreationCSPPayload):
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
return TaskOrderBillingCreationCSPResult(**{"Location": "https://somelocation", "Retry-After": "10"}).dict()
def create_task_order_billing_verification(self, payload: TaskOrderBillingVerificationCSPPayload):
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
return TaskOrderBillingVerificationCSPResult(**{
"id": "/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/billingProfiles/XC36-GRNZ-BG7-TGB",
"name": "XC36-GRNZ-BG7-TGB",
"properties": {
"address": {
"addressLine1": "123 S Broad Street, Suite 2400",
"city": "Philadelphia",
"companyName": "Promptworks",
"country": "US",
"postalCode": "19109",
"region": "PA"
},
"currency": "USD",
"displayName": "First Portfolio Billing Profile",
"enabledAzurePlans": [
{
"productId": "DZH318Z0BPS6",
"skuId": "0001",
"skuDescription": "Microsoft Azure Plan"
}
],
"hasReadAccess": True,
"invoiceDay": 5,
"invoiceEmailOptIn": False
},
"type": "Microsoft.Billing/billingAccounts/billingProfiles"
}).dict()
def create_billing_instruction(self, payload: BillingInstructionCSPPayload):
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
return BillingInstructionCSPResult(**{
"name": "TO1:CLIN001",
"properties": {
"amount": 1000.0,
"endDate": "2020-03-01T00:00:00+00:00",
"startDate": "2020-01-01T00:00:00+00:00"
},
"type": "Microsoft.Billing/billingAccounts/billingProfiles/billingInstructions"
}).dict()
def create_or_update_user(self, auth_credentials, user_info, csp_role_id): def create_or_update_user(self, auth_credentials, user_info, csp_role_id):
self._authorize(auth_credentials) self._authorize(auth_credentials)
@ -1150,13 +1207,13 @@ class AzureCloudProvider(CloudProviderInterface):
request_body = { request_body = {
"properties": { "properties": {
"amount": payload.amount, "amount": payload.initial_clin_amount,
"startDate": payload.start_date, "startDate": payload.initial_clin_start_date,
"endDate": payload.end_date, "endDate": payload.initial_clin_end_date,
} }
} }
url = f"https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{payload.billing_account_name}/billingProfiles/{payload.billing_profile_name}/instructions/{payload.task_order_id}:CLIN00{payload.clin_type}?api-version=2019-10-01-preview" url = f"https://management.azure.com/providers/Microsoft.Billing/billingAccounts/{payload.billing_account_name}/billingProfiles/{payload.billing_profile_name}/instructions/{payload.initial_task_order_id}:CLIN00{payload.initial_clin_type}?api-version=2019-10-01-preview"
auth_header = { auth_header = {
"Authorization": f"Bearer {sp_token}", "Authorization": f"Bearer {sp_token}",

View File

@ -67,7 +67,6 @@ class PortfolioStateMachine(
def __repr__(self): def __repr__(self):
return f"<PortfolioStateMachine(state='{self.current_state.name}', portfolio='{self.portfolio.name}'" return f"<PortfolioStateMachine(state='{self.current_state.name}', portfolio='{self.portfolio.name}'"
@reconstructor @reconstructor
def attach_machine(self): def attach_machine(self):
""" """
@ -112,7 +111,9 @@ class PortfolioStateMachine(
if create_trigger: if create_trigger:
self.trigger(create_trigger, **kwargs) self.trigger(create_trigger, **kwargs)
else: else:
app.logger.info(f"could not locate 'create trigger' for {self.__repr__()}") app.logger.info(
f"could not locate 'create trigger' for {self.__repr__()}"
)
self.fail_stage(stage) self.fail_stage(stage)
elif state_obj.is_CREATED: elif state_obj.is_CREATED:
@ -143,8 +144,11 @@ class PortfolioStateMachine(
try: try:
payload_data = payload_data_cls(**payload) payload_data = payload_data_cls(**payload)
except PydanticValidationError as exc: except PydanticValidationError as exc:
app.logger.error(f"Payload Validation Error in {self.__repr__()}:", exc_info=1) app.logger.error(
f"Payload Validation Error in {self.__repr__()}:", exc_info=1
)
app.logger.info(exc.json()) app.logger.info(exc.json())
print(exc.json())
app.logger.info(payload) app.logger.info(payload)
self.fail_stage(stage) self.fail_stage(stage)
@ -161,7 +165,10 @@ class PortfolioStateMachine(
func_name = f"create_{stage}" func_name = f"create_{stage}"
response = getattr(self.csp, func_name)(payload_data) response = getattr(self.csp, func_name)(payload_data)
except (ConnectionException, UnknownServerException) as exc: except (ConnectionException, UnknownServerException) as exc:
app.logger.error(f"CSP api call. Caught exception for {self.__repr__()}. Retry attempt {attempt}", exc_info=1) app.logger.error(
f"CSP api call. Caught exception for {self.__repr__()}. Retry attempt {attempt}",
exc_info=1,
)
continue continue
else: else:
break break
@ -198,12 +205,16 @@ class PortfolioStateMachine(
dc = cls(**stage_data) dc = cls(**stage_data)
if getattr(dc, "get_creds", None) is not None: if getattr(dc, "get_creds", None) is not None:
new_creds = dc.get_creds() new_creds = dc.get_creds()
print("creds to report")
print(new_creds)
# TODO: how/where to store these # TODO: how/where to store these
# TODO: credential schema # TODO: credential schema
# self.store_creds(self.portfolio, new_creds) # self.store_creds(self.portfolio, new_creds)
except PydanticValidationError as exc: except PydanticValidationError as exc:
app.logger.error(f"Payload Validation Error in {self.__repr__()}:", exc_info=1) app.logger.error(
f"Payload Validation Error in {self.__repr__()}:", exc_info=1
)
app.logger.info(exc.json()) app.logger.info(exc.json())
app.logger.info(payload) app.logger.info(payload)

View File

@ -404,11 +404,11 @@ def test_create_billing_instruction(mock_azure: AzureCloudProvider):
payload = BillingInstructionCSPPayload( payload = BillingInstructionCSPPayload(
**dict( **dict(
creds=creds, creds=creds,
amount=1000.00, initial_clin_amount=1000.00,
start_date="2020/1/1", initial_clin_start_date="2020/1/1",
end_date="2020/3/1", initial_clin_end_date="2020/3/1",
clin_type="1", initial_clin_type="1",
task_order_id="TO1", initial_task_order_id="TO1",
billing_account_name="7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31", billing_account_name="7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31",
billing_profile_name="KQWI-W2SU-BG7-TGB", billing_profile_name="KQWI-W2SU-BG7-TGB",
) )

View File

@ -4,16 +4,19 @@ import re
from tests.factories import ( from tests.factories import (
PortfolioFactory, PortfolioFactory,
PortfolioStateMachineFactory, PortfolioStateMachineFactory,
TaskOrderFactory,
CLINFactory,
) )
from atst.models import FSMStates, PortfolioStateMachine from atst.models import FSMStates, PortfolioStateMachine, TaskOrder
from atst.models.mixins.state_machines import AzureStages, StageStates, compose_state from atst.models.mixins.state_machines import AzureStages, StageStates, compose_state
from atst.models.portfolio import Portfolio
from atst.domain.csp import get_stage_csp_class from atst.domain.csp import get_stage_csp_class
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def portfolio(): def portfolio():
portfolio = PortfolioFactory.create() portfolio = CLINFactory.create().task_order.portfolio
return portfolio return portfolio
@ -77,7 +80,7 @@ def test_state_machine_initialization(portfolio):
assert ["reset", "fail", create_trigger] == started_triggers assert ["reset", "fail", create_trigger] == started_triggers
def test_fsm_transition_start(portfolio): def test_fsm_transition_start(portfolio: Portfolio):
sm: PortfolioStateMachine = PortfolioStateMachineFactory.create(portfolio=portfolio) sm: PortfolioStateMachine = PortfolioStateMachineFactory.create(portfolio=portfolio)
assert sm.portfolio assert sm.portfolio
assert sm.state == FSMStates.UNSTARTED assert sm.state == FSMStates.UNSTARTED
@ -88,6 +91,16 @@ def test_fsm_transition_start(portfolio):
sm.start() sm.start()
assert sm.state == FSMStates.STARTED assert sm.state == FSMStates.STARTED
expected_states = [
FSMStates.TENANT_CREATED,
FSMStates.BILLING_PROFILE_CREATION_CREATED,
FSMStates.BILLING_PROFILE_VERIFICATION_CREATED,
FSMStates.BILLING_PROFILE_TENANT_ACCESS_CREATED,
FSMStates.TASK_ORDER_BILLING_CREATION_CREATED,
FSMStates.TASK_ORDER_BILLING_VERIFICATION_CREATED,
FSMStates.BILLING_INSTRUCTION_CREATED,
]
# Should source all creds for portfolio? might be easier to manage than per-step specific ones # Should source all creds for portfolio? might be easier to manage than per-step specific ones
creds = {"username": "mock-cloud", "password": "shh"} creds = {"username": "mock-cloud", "password": "shh"}
if portfolio.csp_data is not None: if portfolio.csp_data is not None:
@ -95,19 +108,21 @@ def test_fsm_transition_start(portfolio):
else: else:
csp_data = {} csp_data = {}
# ppoc = portfolio.owner ppoc = portfolio.owner
# user_id = f"{ppoc.first_name[0]}{ppoc.last_name}".lower() user_id = f"{ppoc.first_name[0]}{ppoc.last_name}".lower()
user_id = "abcdefg"
domain_name = re.sub("[^0-9a-zA-Z]+", "", portfolio.name).lower() domain_name = re.sub("[^0-9a-zA-Z]+", "", portfolio.name).lower()
initial_task_order: TaskOrder = portfolio.task_orders[0]
initial_clin = initial_task_order.sorted_clins[0]
portfolio_data = { portfolio_data = {
"user_id": user_id, "user_id": user_id,
"password": "jklfsdNCVD83nklds2#202", "password": "jklfsdNCVD83nklds2#202",
"domain_name": domain_name, "domain_name": domain_name,
"first_name": "john", # ppoc.first_name, "first_name": ppoc.first_name,
"last_name": "doe", # ppoc.last_name, "last_name": ppoc.last_name,
"country_code": "US", "country_code": "US",
"password_recovery_email_address": "email@example.com", # ppoc.email, "password_recovery_email_address": ppoc.email,
"address": { "address": {
"company_name": "", "company_name": "",
"address_line_1": "", "address_line_1": "",
@ -117,26 +132,24 @@ def test_fsm_transition_start(portfolio):
"postal_code": "", "postal_code": "",
}, },
"billing_profile_display_name": "My Billing Profile", "billing_profile_display_name": "My Billing Profile",
"initial_clin_amount": initial_clin.obligated_amount,
"initial_clin_start_date": initial_clin.start_date.strftime("%Y/%m/%d"),
"initial_clin_end_date": initial_clin.end_date.strftime("%Y/%m/%d"),
"initial_clin_type": initial_clin.number,
"initial_task_order_id": initial_task_order.number,
} }
config = {"billing_account_name": "billing_account_name"} config = {"billing_account_name": "billing_account_name"}
for expected_state in expected_states:
print(expected_state)
collected_data = dict( collected_data = dict(
list(csp_data.items()) + list(portfolio_data.items()) + list(config.items()) list(csp_data.items()) + list(portfolio_data.items()) + list(config.items())
) )
sm.trigger_next_transition(creds=creds, csp_data=collected_data) sm.trigger_next_transition(creds=creds, csp_data=collected_data)
assert sm.state == expected_state
assert sm.state == FSMStates.TENANT_CREATED
assert portfolio.csp_data.get("tenant_id", None) is not None
#print(portfolio.csp_data.keys())
if portfolio.csp_data is not None: if portfolio.csp_data is not None:
csp_data = portfolio.csp_data csp_data = portfolio.csp_data
else: else:
csp_data = {} csp_data = {}
collected_data = dict(
list(csp_data.items()) + list(portfolio_data.items()) + list(config.items())
)
sm.trigger_next_transition(creds=creds, csp_data=collected_data)
assert sm.state == FSMStates.BILLING_PROFILE_CREATION_CREATED
#print(portfolio.csp_data.keys())

View File

@ -66,6 +66,12 @@ def mock_requests():
return Mock(spec=requests) return Mock(spec=requests)
def mock_secrets():
from azure.keyvault import secrets
return Mock(spec=secrets)
class MockAzureSDK(object): class MockAzureSDK(object):
def __init__(self): def __init__(self):
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD
@ -78,6 +84,7 @@ class MockAzureSDK(object):
self.graphrbac = mock_graphrbac() self.graphrbac = mock_graphrbac()
self.credentials = mock_credentials() self.credentials = mock_credentials()
self.policy = mock_policy() self.policy = mock_policy()
self.secrets = mock_secrets()
self.requests = mock_requests() self.requests = mock_requests()
# may change to a JEDI cloud # may change to a JEDI cloud
self.cloud = AZURE_PUBLIC_CLOUD self.cloud = AZURE_PUBLIC_CLOUD