Adding @nested_transactional for transactions that require more than one

db update/insert.

Using a savepoint for the multiple transactions allows us to rollback if
there is an error when executing the second db transaction.
However, this does add a bit of complexity. Developers need to manage
the db session when calling multiple nested tranactions.

Unit tests have been added to test this functionality and some end to
end tests have been done to make sure all transactions are rollback if
there is an exception while executing the transaction.
This commit is contained in:
Rebecca Law
2021-04-12 13:52:40 +01:00
parent 7806bb5246
commit 6704be4021
10 changed files with 103 additions and 38 deletions

View File

@@ -233,7 +233,7 @@ def test_add_service_to_organisation(sample_service, sample_organisation):
sample_organisation.crown = False
dao_add_service_to_organisation(sample_service, sample_organisation.id)
db.session.commit()
assert len(sample_organisation.services) == 1
assert sample_organisation.services[0].id == sample_service.id

View File

@@ -505,14 +505,54 @@ def test_post_link_service_to_organisation(admin_request, sample_service):
organisation_id=organisation.id,
_expected_status=204
)
assert len(organisation.services) == 1
assert sample_service.organisation_type == 'central'
def test_post_link_service_to_organisation_inserts_annual_billing(admin_request, sample_service):
data = {
'service_id': str(sample_service.id)
}
organisation = create_organisation(organisation_type='central')
assert len(organisation.services) == 0
assert len(AnnualBilling.query.all()) == 0
admin_request.post(
'organisation.link_service_to_organisation',
_data=data,
organisation_id=organisation.id,
_expected_status=204
)
annual_billing = AnnualBilling.query.all()
assert len(annual_billing) == 1
assert annual_billing[0].free_sms_fragment_limit == 150000
def test_post_link_service_to_organisation_rollback_service_if_annual_billing_update_fails(
admin_request, sample_service, mocker
):
mocker.patch('app.dao.annual_billing_dao.dao_create_or_update_annual_billing_for_year',
side_effect=SQLAlchemyError)
data = {
'service_id': str(sample_service.id)
}
assert not sample_service.organisation_type
organisation = create_organisation(organisation_type='central')
assert len(organisation.services) == 0
assert len(AnnualBilling.query.all()) == 0
with pytest.raises(expected_exception=SQLAlchemyError):
admin_request.post(
'organisation.link_service_to_organisation',
_data=data,
organisation_id=organisation.id,
_expected_status=404
)
assert not sample_service.organisation_type
assert len(organisation.services) == 0
assert len(AnnualBilling.query.all()) == 0
def test_post_link_service_to_another_org(
admin_request, sample_service, sample_organisation):
data = {
@@ -582,20 +622,20 @@ def test_post_link_service_to_organisation_missing_payload(
)
def test_link_service_to_organisation_updates_service_if_annual_billing_update_fails(
def test_link_service_to_organisation_does_not_updates_service_if_annual_billing_update_fails(
mocker, admin_request, sample_service, sample_organisation
):
mocker.patch('app.organisation.rest.set_default_free_allowance_for_service', raises=SQLAlchemyError)
mocker.patch('app.organisation.rest.set_default_free_allowance_for_service', side_effect=SQLAlchemyError)
data = {
'service_id': str(sample_service.id)
}
admin_request.post(
'organisation.link_service_to_organisation',
organisation_id=str(sample_organisation.id),
_data=data,
_expected_status=204
)
assert sample_service.organisation_id == sample_organisation.id
with pytest.raises(expected_exception=SQLAlchemyError):
admin_request.post(
'organisation.link_service_to_organisation',
organisation_id=str(sample_organisation.id),
_data=data,
)
assert not sample_service.organisation_id
assert len(AnnualBilling.query.all()) == 0