Skip to content

Commit

Permalink
adding delete unit tests
Browse files Browse the repository at this point in the history
adding delete unit tests

adding delete unit tests
  • Loading branch information
nuwan-samarasinghe committed Feb 7, 2025
1 parent 8a067ec commit 0bf80bd
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 12 deletions.
7 changes: 5 additions & 2 deletions app/db/queries/fund.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from sqlalchemy.orm import joinedload

from app.db import db
from app.db.models import Round, Component, Page, Form
from app.db.models import Round, Component, Page, Form, Lizt
from app.db.models.fund import Fund, Organisation
from app.db.queries.util import delete_all_related_objects

Expand Down Expand Up @@ -48,11 +48,14 @@ def _delete_sections_for_fund_round(fund: Fund):
for round_detail in fund.rounds:
for section in round_detail.sections:
if section:
lizt_ids = [component.list_id for form in section.forms for page in form.pages for component in
page.components]
page_ids = [page.page_id for form in section.forms for page in form.pages]
form_ids = [form.form_id for form in section.forms]
section_ids = [section.section_id]

delete_all_related_objects(db=db, model=Component, column=Component.page_id, ids=page_ids)
delete_all_related_objects(db=db, model=Lizt, column=Lizt.list_id, ids=lizt_ids)
delete_all_related_objects(db=db, model=Page, column=Page.form_id, ids=form_ids)
delete_all_related_objects(db=db, model=Form, column=Form.section_id, ids=section_ids)

Expand All @@ -61,7 +64,7 @@ def _delete_sections_for_fund_round(fund: Fund):


def delete_selected_fund(fund_id):
fund: Fund = db.session.query(Fund).options(joinedload(Fund.rounds).joinedload(Round.sections)).get(fund_id)
fund: Fund = db.session.get(Fund, fund_id, options=[joinedload(Fund.rounds).joinedload(Round.sections)])
if not fund:
raise ValueError(f"Fund with id {fund_id} not found")
try:
Expand Down
7 changes: 5 additions & 2 deletions app/db/queries/round.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from sqlalchemy.orm import joinedload

from app.db import db
from app.db.models import Fund, Component, Page, Form
from app.db.models import Fund, Component, Page, Form, Lizt
from app.db.models.round import Round
from app.db.queries.util import delete_all_related_objects

Expand Down Expand Up @@ -39,11 +39,14 @@ def get_all_rounds() -> list[Round]:

def _delete_sections_for_round(round_detail: Round):
for section_detail in round_detail.sections:
lizt_ids = [component.list_id for form in section_detail.forms for page in form.pages for component in
page.components]
page_ids = [page.page_id for form in section_detail.forms for page in form.pages]
form_ids = [form.form_id for form in section_detail.forms]
section_ids = [section_detail.section_id]

delete_all_related_objects(db=db, model=Component, column=Component.page_id, ids=page_ids)
delete_all_related_objects(db=db, model=Lizt, column=Lizt.list_id, ids=lizt_ids)
delete_all_related_objects(db=db, model=Page, column=Page.form_id, ids=form_ids)
delete_all_related_objects(db=db, model=Form, column=Form.section_id, ids=section_ids)

Expand All @@ -52,7 +55,7 @@ def _delete_sections_for_round(round_detail: Round):


def delete_selected_round(round_id):
round_detail: Round = db.session.query(Round).options(joinedload(Round.sections)).get(round_id)
round_detail: Round = db.session.get(Round, round_id, options=[joinedload(Round.sections)])
if not round_detail:
raise ValueError(f"Round with id {round_id} not found")
try:
Expand Down
41 changes: 38 additions & 3 deletions tests/blueprints/fund/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
from bs4 import BeautifulSoup
from flask import g

from app.db.models import Fund
from app.db.models import Fund, Round, Form, Section, Component, Lizt
from app.db.models.fund import FundingType
from app.db.queries.fund import get_fund_by_id
from tests.helpers import submit_form
from config import Config
from sqlalchemy.orm import joinedload


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
Expand Down Expand Up @@ -329,8 +331,8 @@ def test_view_fund_details(flask_test_client, seed_dynamic_data):
html = response.data.decode("utf-8")
assert f'<h1 class="govuk-heading-l">{test_fund.name_json["en"]}</h1>' in html
assert (
f'<a class="govuk-link govuk-link--no-visited-state" href="/grants/{test_fund.fund_id}/edit#name_en">Change'
f'<span class="govuk-visually-hidden"> Grant name</span></a>' in html # noqa: E501
f'<a class="govuk-link govuk-link--no-visited-state" href="/grants/{test_fund.fund_id}/edit#name_en">Change'
f'<span class="govuk-visually-hidden"> Grant name</span></a>' in html # noqa: E501
)
assert '<a href="/grants/" class="govuk-back-link">Back</a>' in html

Expand All @@ -356,3 +358,36 @@ def test_create_fund_welsh_error_messages(flask_test_client, seed_dynamic_data):
assert b"Enter the Welsh grant name" in response.data # Validation error message
assert b"Enter the Welsh application name" in response.data # Validation error message
assert b"Enter the Welsh grant description" in response.data # Validation error message


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
def test_delete_fund_feature_disabled(flask_test_client, monkeypatch, seed_fund_without_assessment):
"""Test that the delete endpoint returns 403 when a feature flag is disabled."""
test_fund: Fund = seed_fund_without_assessment["funds"][0]
monkeypatch.setattr(Config, "FEATURE_FLAGS", {"feature_delete": False})
response = flask_test_client.get(f"/grants/{test_fund.fund_id}/delete", follow_redirects=True)
assert response.status_code == 403
assert b"Delete Feature Disabled" in response.data


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
def test_delete_fund_feature_enabled(_db, flask_test_client, monkeypatch, seed_fund_without_assessment):
"""Test that the delete endpoint redirects when a feature flag is enabled."""
test_fund: Fund = seed_fund_without_assessment["funds"][0]
monkeypatch.setattr(Config, "FEATURE_FLAGS", {"feature_delete": True})
output: Fund = _db.session.get(Fund, test_fund.fund_id,
options=[joinedload(Fund.rounds).joinedload(Round.sections)])
assert output is not None, "No values present in the db"
response = flask_test_client.get(f"/grants/{test_fund.fund_id}/delete", follow_redirects=True)
assert response.status_code == 200 # Assuming redirection to a valid page
_db.session.commit()
output_f = _db.session.get(Fund, test_fund.fund_id, options=[joinedload(Fund.rounds).joinedload(Round.sections)])
assert output_f is None, "Grant delete did not happened"
output_r = _db.session.query(Round).all()
assert not output_r, "Round delete did not happened"
output_s = _db.session.query(Section).all()
assert not output_s, "Section delete did not happened"
output_c = _db.session.query(Component).all()
assert not output_c, "Component delete did not happened"
output_l = _db.session.query(Lizt).all()
assert not output_l, "Lizt delete did not happened"
37 changes: 36 additions & 1 deletion tests/blueprints/round/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from bs4 import BeautifulSoup
from flask import g, url_for

from app.db.models import Round
from app.db.models import Round, Fund, Section, Component, Lizt
from app.db.queries.round import get_round_by_id
from tests.helpers import submit_form
from config import Config
from sqlalchemy.orm import joinedload

round_data_info = {
"opens": ["01", "10", "2024", "09", "00"],
Expand Down Expand Up @@ -330,3 +332,36 @@ def test_clone_round(flask_test_client, seed_dynamic_data):
soup = BeautifulSoup(response.data, "html.parser")
notification = soup.find("div", {"class": "govuk-notification-banner__content"})
assert notification.text.strip() == "Error copying application"


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
def test_delete_fund_feature_disabled(flask_test_client, monkeypatch, seed_fund_without_assessment):
"""Test that the delete endpoint returns 403 when a feature flag is disabled."""
test_round: Round = seed_fund_without_assessment["rounds"][0]
monkeypatch.setattr(Config, "FEATURE_FLAGS", {"feature_delete": False})
response = flask_test_client.get(f"/rounds/{test_round.round_id}/delete", follow_redirects=True)
assert response.status_code == 403
assert b"Delete Feature Disabled" in response.data


@pytest.mark.usefixtures("set_auth_cookie", "patch_validate_token_rs256_internal_user")
def test_delete_fund_feature_enabled(_db, flask_test_client, monkeypatch, seed_fund_without_assessment):
"""Test that the delete endpoint redirects when a feature flag is enabled."""
test_round: Round = seed_fund_without_assessment["rounds"][0]
monkeypatch.setattr(Config, "FEATURE_FLAGS", {"feature_delete": True})
output: Fund = _db.session.get(Fund, test_round.fund_id,
options=[joinedload(Fund.rounds).joinedload(Round.sections)])
assert output is not None, "No values present in the db"
response = flask_test_client.get(f"/rounds/{test_round.round_id}/delete", follow_redirects=True)
assert response.status_code == 200 # Assuming redirection to a valid page
_db.session.commit()
output_f = _db.session.get(Fund, test_round.fund_id, options=[joinedload(Fund.rounds).joinedload(Round.sections)])
assert output_f is not None, "Grant deleted"
output_r = _db.session.query(Round).all()
assert not output_r, "Round delete did not happened"
output_s = _db.session.query(Section).all()
assert not output_s, "Section delete did not happened"
output_c = _db.session.query(Component).all()
assert not output_c, "Component delete did not happened"
output_l = _db.session.query(Lizt).all()
assert not output_l, "Lizt delete did not happened"
25 changes: 24 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from app.create_app import create_app
from app.import_config.load_form_json import load_form_jsons
from config import Config
from tests.seed_test_data import init_unit_test_data, insert_test_data
from tests.seed_test_data import init_unit_test_data, insert_test_data, fund_without_assessment

pytest_plugins = ["fsd_test_utils.fixtures.db_fixtures"]

Expand Down Expand Up @@ -47,6 +47,29 @@ def seed_dynamic_data(request, app, clear_test_data, _db, enable_preserve_test_d
_db.session.commit()


@pytest.fixture(scope="function")
def seed_fund_without_assessment(request, app, clear_test_data, _db, enable_preserve_test_data):
marker = request.node.get_closest_marker("seed_config")

if marker is None:
fab_seed_data = fund_without_assessment()
else:
fab_seed_data = marker.args[0]
insert_test_data(db=_db, test_data=fab_seed_data)
yield fab_seed_data
# cleanup data after test
# rollback incase of any errors during test session
_db.session.rollback()
# disable foreign key checks
_db.session.execute(text("SET session_replication_role = replica"))
# delete all data from tables
for table in reversed(_db.metadata.sorted_tables):
_db.session.execute(table.delete())
# reset foreign key checks
_db.session.execute(text("SET session_replication_role = DEFAULT"))
_db.session.commit()


@pytest.fixture(scope="function")
def db_with_templates(app, _db):
"""Ensures a clean database but with templates already loaded"""
Expand Down
142 changes: 139 additions & 3 deletions tests/seed_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"project_name_field_id": 1,
"prospectus_link": "https://www.gov.uk/government/organisations/ministry-of-housing-communities-local-government",
"privacy_notice_link": "https://www.gov.uk/government/organisations/"
"ministry-of-housing-communities-local-government",
"ministry-of-housing-communities-local-government",
"contact_email": "help@fab.gov.uk",
"instructions_json": {},
"feedback_link": "https://www.gov.uk/government/organisations/ministry-of-housing-communities-local-government",
Expand Down Expand Up @@ -71,7 +71,7 @@
page_five_id = uuid4()
alt_page_id = uuid4()


#NOSONAR Ignore since this data is related to unit tests
def init_salmon_fishing_fund():
organisation_uuid = uuid4()
o: Organisation = Organisation(
Expand Down Expand Up @@ -355,7 +355,7 @@ def init_salmon_fishing_fund():
"organisations": [o],
}


#NOSONAR Ignore since this data is related to unit tests
def init_unit_test_data() -> dict:
organisation_uuid = uuid4()
o: Organisation = Organisation(
Expand Down Expand Up @@ -481,6 +481,142 @@ def init_unit_test_data() -> dict:
"themes": [t1],
}

#NOSONAR Ignore since this data is related to unit tests
def fund_without_assessment() -> dict:
organisation_uuid = uuid4()
o: Organisation = Organisation(
organisation_id=organisation_uuid,
name=f"Ministry of Testing - {str(organisation_uuid)[:5]}",
short_name=f"MoT-{str(organisation_uuid)[:5]}",
logo_uri="https://www.google.com",
audit_info={"user": "dummy_user", "timestamp": datetime.now().isoformat(), "action": "create"},
)

f2: Fund = Fund(
fund_id=uuid4(),
name_json={"en": "Unit Test Fund 2"},
title_json={"en": "funding to improve testing"},
description_json={"en": "A £10m fund to improve testing across the devolved nations."},
welsh_available=False,
short_name=f"UTF{randint(0, 999)}",
owner_organisation_id=o.organisation_id,
funding_type=FundingType.COMPETITIVE,
ggis_scheme_reference_number="G3-SCH-0000092414",
)

f2_r1: Round = Round(
round_id=uuid4(),
fund_id=f2.fund_id,
audit_info={"user": "dummy_user", "timestamp": datetime.now().isoformat(), "action": "create"},
title_json={"en": "round the first"},
short_name=f"UTR{randint(0, 999)}",
opens=datetime.now(),
deadline=datetime.now(),
assessment_start=datetime.now(),
reminder_date=datetime.now(),
assessment_deadline=datetime.now(),
prospectus_link="https://www.google.com",
privacy_notice_link="https://www.google.com",
contact_email="test@test.com",
feedback_link="https://www.google.com",
project_name_field_id="12312312312",
guidance_url="https://www.google.com",
feedback_survey_config={
"has_feedback_survey": False,
"has_section_feedback": False,
"has_research_survey": False,
"is_feedback_survey_optional": False,
"is_section_feedback_optional": False,
"is_research_survey_optional": False,
},
eligibility_config={"has_eligibility": False},
eoi_decision_schema={"en": {"valid": True}, "cy": {"valid": False}},
)

f2_r1_s1: Section = Section(
section_id=uuid4(), index=1, round_id=f2_r1.round_id, name_in_apply_json={"en": "Organisation Information 2"}
)

f2_r1_s2: Section = Section(
section_id=uuid4(), index=1, round_id=f2_r1.round_id, name_in_apply_json={"en": "Organisation Information 3"}
)

f2_r1_s1_f1: Form = Form(
form_id=uuid4(),
section_id=f2_r1_s1.section_id,
name_in_apply_json={"en": "About your organisation"},
section_index=1,
runner_publish_name="about-your-org",
template_name="About your organization template",
)

f2_r1_s1_f2: Form = Form(
form_id=uuid4(),
section_id=f2_r1_s2.section_id,
name_in_apply_json={"en": "About your organisation 2"},
section_index=1,
runner_publish_name="about-your-org",
template_name="About your organization template",
)

f2_r1_s1_f1_p1: Page = Page(
page_id=uuid4(),
form_id=f2_r1_s1_f1.form_id,
display_path="organisation-name",
name_in_apply_json={"en": "Organisation Name"},
form_index=1,
default_next_page_id=None,
)

f2_r1_s1_f1_p2: Page = Page(
page_id=uuid4(),
form_id=f2_r1_s1_f2.form_id,
display_path="organisation-name",
name_in_apply_json={"en": "Organisation Name"},
form_index=1,
default_next_page_id=None,
)

f2_r1_s1_f1_p1_c1: Component = Component(
component_id=uuid4(),
page_id=f2_r1_s1_f1_p1.page_id,
title="What is your organisation's name?",
hint_text="This must match the registered legal organisation name",
type=ComponentType.TEXT_FIELD,
page_index=1,
options={"hideTitle": False, "classes": ""},
runner_component_name="organisation_name",
)

l1: Lizt = Lizt(
list_id=uuid4(),
name="classifications_list",
type="string",
items=[{"text": "Charity", "value": "charity"}, {"text": "Public Limited Company", "value": "plc"}],
is_template=True,
)

f2_r1_s1_f1_p1_c2_with_list: Component = Component(
component_id=uuid4(),
page_id=f2_r1_s1_f1_p2.page_id,
title="How is your organisation classified?",
type=ComponentType.RADIOS_FIELD,
page_index=2,
options={"hideTitle": False, "classes": ""},
runner_component_name="organisation_classification",
list_id=l1.list_id,
)
return {
"lists": [l1],
"funds": [f2],
"organisations": [o],
"rounds": [f2_r1],
"sections": [f2_r1_s1, f2_r1_s2],
"forms": [f2_r1_s1_f1, f2_r1_s1_f2],
"pages": [f2_r1_s1_f1_p1, f2_r1_s1_f1_p2],
"components": [f2_r1_s1_f1_p1_c1, f2_r1_s1_f1_p1_c2_with_list]
}


def add_default_page_paths(db, default_next_page_config):
# set up the default paths
Expand Down

0 comments on commit 0bf80bd

Please sign in to comment.