Skip to content

Commit

Permalink
Allow base tests to use dynamic database
Browse files Browse the repository at this point in the history
These tests are the most unconventional of the all the coding systems
tests that use the dynamic database because:

* they uses fixtures to setup databases, and this needs to happen before
  `setUp()`, so we also have to mangle the `databases` attribute before
  `setUp()`
* one of the tests actually needs to access multiple databases, which
  breaks the pattern for all other tests

After a little bit of trying, it seems overly complicated to try and
generalise the setup and teardown pattern here, partly due to the use of
pytest fixtures and the limitations in `TestCase` classes,
so it's been left as it was in the original code.
  • Loading branch information
StevenMaude committed Jan 30, 2025
1 parent 7888706 commit 340142f
Showing 1 changed file with 210 additions and 149 deletions.
359 changes: 210 additions & 149 deletions coding_systems/base/tests/test_import_data_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,98 @@
from codelists.hierarchy import Hierarchy
from codelists.models import Status
from coding_systems.base.import_data_utils import update_codelist_version_compatibility
from coding_systems.base.tests.helpers import DynamicDatabaseTestCase
from coding_systems.bnf.models import Concept
from coding_systems.conftest import mock_migrate_coding_system
from coding_systems.versioning.models import CodingSystemRelease, ReleaseState


@pytest.fixture
def bnf_csr():
def _csr(valid_from=datetime(2022, 11, 1)):
return CodingSystemRelease.objects.create(
coding_system="bnf",
release_name="import-data",
valid_from=valid_from,
state=ReleaseState.READY,
)

return _csr
class BaseCodingSystemDynamicDatabaseTestCase(DynamicDatabaseTestCase):
@pytest.fixture
@staticmethod
def bnf_csr():
def _csr(valid_from=datetime(2022, 11, 1)):
return CodingSystemRelease.objects.create(
coding_system="bnf",
release_name="import-data",
valid_from=valid_from,
state=ReleaseState.READY,
)

return _csr

@pytest.fixture
@staticmethod
def bnf_review_version_with_search(bnf_version_with_search):
bnf_version_with_search.status = Status.UNDER_REVIEW
bnf_version_with_search.save()
yield bnf_version_with_search

@pytest.fixture
def _get_bnf_release(self, _bnf_release):
self.bnf_release = _bnf_release

@pytest.fixture
def _bnf_release(self, bnf_csr):
# Unusually, this setup needs to be done *before* the regular setUp() runs.
self.add_to_databases(self.db_alias)
# setup the database as a duplicate of the fixture one
csr = bnf_csr()
setup_db(csr)
yield csr
cleanup_db(csr)

@pytest.fixture
def _get_bnf_release_excl_last_concept(self, _bnf_release_excl_last_concept):
self.bnf_release = _bnf_release_excl_last_concept

@pytest.fixture
def _bnf_release_excl_last_concept(self, bnf_csr):
# This addition needs to be done *before* the regular setUp() runs.
self.add_to_databases(self.db_alias)
# setup the database as a duplicate of the fixture one, omitting the last concept
csr = bnf_csr(datetime(2022, 10, 1))
setup_db(csr, exclude_last_concept=True)
yield csr
cleanup_db(csr)

@pytest.fixture
def bnf_review_version_with_search(bnf_version_with_search):
bnf_version_with_search.status = Status.UNDER_REVIEW
bnf_version_with_search.save()
yield bnf_version_with_search
@pytest.fixture
def _bnf_releases(self, bnf_csr):
db_alias_additions = [
"bnf_import-data_20190101",
"bnf_import-data_20220901",
"bnf_import-data_20221201",
]
# This addition needs to be done *before* the regular setUp() runs.
self.add_to_databases(*db_alias_additions)
# setup multiple databases as duplicates of the fixture one, with different dates

# earlier than the release the codelist versions are created with
csr_20190101 = bnf_csr(datetime(2019, 1, 1))
setup_db(csr_20190101)
# earlier than bnf_release_excl_last_concept
csr_20220901 = bnf_csr(datetime(2022, 9, 1))
setup_db(csr_20220901)
# later than bnf_release_excl_last_concept
csr_20221201 = bnf_csr(datetime(2022, 12, 1))
setup_db(csr_20221201)

yield

for csr in [csr_20190101, csr_20220901, csr_20221201]:
cleanup_db(csr)

@pytest.fixture
def _get_bnf_version_with_search(self, bnf_version_with_search):
self.bnf_version_with_search = bnf_version_with_search

@pytest.fixture
def _get_bnf_review_version_with_search(self, bnf_review_version_with_search):
self.bnf_review_version_with_search = bnf_review_version_with_search

@pytest.fixture
def _get_bnf_version_asthma(self, bnf_version_asthma):
self.bnf_version_asthma = bnf_version_asthma


def setup_db(csr, exclude_last_concept=False):
Expand Down Expand Up @@ -66,159 +135,151 @@ def cleanup_db(csr):
cursor.execute("DROP TABLE bnf_concept")


@pytest.fixture
def bnf_release(bnf_csr):
# setup the database as a duplicate of the fixture one
csr = bnf_csr()
setup_db(csr)
yield csr
cleanup_db(csr)


@pytest.fixture
def bnf_release_excl_last_concept(bnf_csr):
# setup the database as a duplicate of the fixture one, omitting the last concept
csr = bnf_csr(datetime(2022, 10, 1))
setup_db(csr, exclude_last_concept=True)
yield csr
cleanup_db(csr)


@pytest.fixture
def bnf_releases(bnf_csr):
# setup multiple databases as duplicates of the fixture one, with different dates

# earlier than the release the codelist versions are created with
csr_20190101 = bnf_csr(datetime(2019, 1, 1))
setup_db(csr_20190101)
# earlier than bnf_release_excl_last_concept
csr_20220901 = bnf_csr(datetime(2022, 9, 1))
setup_db(csr_20220901)
# later than bnf_release_excl_last_concept
csr_20221201 = bnf_csr(datetime(2022, 12, 1))
setup_db(csr_20221201)

yield

for csr in [csr_20190101, csr_20220901, csr_20221201]:
cleanup_db(csr)


def test_update_codelist_version_compatibility_no_searches(
bnf_version_asthma, coding_systems_tmp_path, bnf_release
class TestUpdateCodelistVersionCompatibilityNoSearches(
BaseCodingSystemDynamicDatabaseTestCase
):
# Draft versions are not checked for compatibility; this version is under review
assert bnf_version_asthma.status == Status.PUBLISHED
update_codelist_version_compatibility("bnf", bnf_release.database_alias)
# this version has no searches, but its hierarchy is identical
assert bnf_version_asthma.compatible_releases.exists()

db_alias = "bnf_import-data_20221101"

def test_update_codelist_draft_version_excluded(
bnf_version_with_search, coding_systems_tmp_path, bnf_release
):
assert bnf_version_with_search.status == Status.DRAFT
update_codelist_version_compatibility("bnf", bnf_release.database_alias)
# this version has an identical hierarchy, and its search will return the same
# results with the new release, so it is compatible
assert not bnf_version_with_search.compatible_releases.exists()
@pytest.mark.usefixtures("_get_bnf_release", "_get_bnf_version_asthma")
def test_update_codelist_version_compatibility_no_searches(self):
# Draft versions are not checked for compatibility; this version is under review
assert self.bnf_version_asthma.status == Status.PUBLISHED
update_codelist_version_compatibility("bnf", self.bnf_release.database_alias)
# this version has no searches, but its hierarchy is identical
assert self.bnf_version_asthma.compatible_releases.exists()


def test_update_codelist_version_with_search(
bnf_review_version_with_search, coding_systems_tmp_path, bnf_release
class TestUpdateCodelistVersionDraftVersionExcluded(
BaseCodingSystemDynamicDatabaseTestCase
):
assert bnf_review_version_with_search.status == Status.UNDER_REVIEW
update_codelist_version_compatibility("bnf", bnf_release.database_alias)
# this version has an identical hierarchy, and its search will return the same
# results with the new release, so it is compatible
assert bnf_review_version_with_search.compatible_releases.first() == bnf_release
db_alias = "bnf_import-data_20221101"

@pytest.mark.usefixtures("_get_bnf_release", "_get_bnf_version_with_search")
def test_update_codelist_draft_version_excluded(self):
assert self.bnf_version_with_search.status == Status.DRAFT
update_codelist_version_compatibility("bnf", self.bnf_release.database_alias)
# this version has an identical hierarchy, and its search will return the same
# results with the new release, so it is compatible
assert not self.bnf_version_with_search.compatible_releases.exists()


class TestUpdateCodelistVersionWithSearch(BaseCodingSystemDynamicDatabaseTestCase):
db_alias = "bnf_import-data_20221101"

@pytest.mark.usefixtures("_get_bnf_release", "_get_bnf_review_version_with_search")
def test_update_codelist_version_with_search(self):
assert self.bnf_review_version_with_search.status == Status.UNDER_REVIEW
update_codelist_version_compatibility("bnf", self.bnf_release.database_alias)
# this version has an identical hierarchy, and its search will return the same
# results with the new release, so it is compatible
assert (
self.bnf_review_version_with_search.compatible_releases.first()
== self.bnf_release
)


def test_update_codelist_version_compatibility_with_mismatched_search(
bnf_review_version_with_search,
coding_systems_tmp_path,
bnf_release_excl_last_concept,
class TestUpdateCodelistVersionCompatibilityWithMismatchedSearch(
BaseCodingSystemDynamicDatabaseTestCase
):
# setup the db, but omit the last Concept from the existing db, so the search
# will return different results
update_codelist_version_compatibility(
"bnf", bnf_release_excl_last_concept.database_alias
db_alias = "bnf_import-data_20221001"

@pytest.mark.usefixtures(
"_get_bnf_release_excl_last_concept", "_get_bnf_review_version_with_search"
)
# this version has a search, but the new release returns different results,
# so it is not compatible
assert not bnf_review_version_with_search.compatible_releases.exists()
def test_update_codelist_version_compatibility_with_mismatched_search(self):
# setup the db, but omit the last Concept from the existing db, so the search
# will return different results
update_codelist_version_compatibility("bnf", self.bnf_release.database_alias)
# this version has a search, but the new release returns different results,
# so it is not compatible
assert not self.bnf_review_version_with_search.compatible_releases.exists()


def test_update_codelist_version_compatibility_with_search_but_mismatched_hierarchy(
bnf_review_version_with_search,
coding_systems_tmp_path,
bnf_release_excl_last_concept,
class TestUpdateCodelistVersionCompatibilityWithSearchButMismatchedHierarchy(
BaseCodingSystemDynamicDatabaseTestCase
):
# setup the db, but omit the last Concept from the existing db
# Modify the search so that it returns just one code; the hierarchy will differ but
# search results will remain the same
bnf_review_version_with_search.searches.all().delete()
create_search(
draft=bnf_review_version_with_search,
code="0301012A0AAABAB",
codes=["0301012A0AAABAB"],
)
db_alias = "bnf_import-data_20221001"

existing_hierarchy = bnf_review_version_with_search.hierarchy
hierarchy_with_new_release = Hierarchy.from_codes(
coding_system=CODING_SYSTEMS["bnf"](
database_alias=bnf_release_excl_last_concept.database_alias
),
codes=["0301012A0AAABAB"],
@pytest.mark.usefixtures(
"_get_bnf_release_excl_last_concept", "_get_bnf_review_version_with_search"
)
assert existing_hierarchy.nodes != hierarchy_with_new_release.nodes
def test_update_codelist_version_compatibility_with_search_but_mismatched_hierarchy(
self,
):
# setup the db, but omit the last Concept from the existing db
# Modify the search so that it returns just one code; the hierarchy will differ but
# search results will remain the same
self.bnf_review_version_with_search.searches.all().delete()
create_search(
draft=self.bnf_review_version_with_search,
code="0301012A0AAABAB",
codes=["0301012A0AAABAB"],
)

update_codelist_version_compatibility(
"bnf", bnf_release_excl_last_concept.database_alias
)
# this version has a search that returns identical results in the new release
# but the hierarchies differ, so it is not compatible
assert not bnf_review_version_with_search.compatible_releases.exists()
existing_hierarchy = self.bnf_review_version_with_search.hierarchy
hierarchy_with_new_release = Hierarchy.from_codes(
coding_system=CODING_SYSTEMS["bnf"](
database_alias=self.bnf_release.database_alias
),
codes=["0301012A0AAABAB"],
)
assert existing_hierarchy.nodes != hierarchy_with_new_release.nodes

update_codelist_version_compatibility("bnf", self.bnf_release.database_alias)
# this version has a search that returns identical results in the new release
# but the hierarchies differ, so it is not compatible
assert not self.bnf_review_version_with_search.compatible_releases.exists()


def test_save_codelist_draft_updates_compatibility(
bnf_version_with_search, coding_systems_tmp_path, bnf_release
class TestSaveCodelistDraftUpdatesCompatibility(
BaseCodingSystemDynamicDatabaseTestCase
):
assert bnf_version_with_search.status == Status.DRAFT
assert not bnf_version_with_search.compatible_releases.exists()
save_draft_for_review(draft=bnf_version_with_search)
db_alias = "bnf_import-data_20221001"

assert bnf_version_with_search.compatible_releases.exists()
@pytest.mark.usefixtures("_get_bnf_release", "_get_bnf_version_with_search")
def test_save_codelist_draft_updates_compatibility(self):
assert self.bnf_version_with_search.status == Status.DRAFT
assert not self.bnf_version_with_search.compatible_releases.exists()
save_draft_for_review(draft=self.bnf_version_with_search)

assert self.bnf_version_with_search.compatible_releases.exists()

def test_save_codelist_draft_updates_compatibility_multiple_releases(
bnf_version_with_search,
coding_systems_tmp_path,
bnf_releases,
bnf_release_excl_last_concept,

# This test is particularly unusual compared with the other coding systems
# tests that use dynamic databases, in that multiple databases are used.
class TestSaveCodelistDraftUpdatesCompatibilityMultipleReleases(
BaseCodingSystemDynamicDatabaseTestCase
):
# In this test, we have a draft created with release `bnf_test_20200101`
# The `bnf_releases` fixture gives us 3 other releases, which are duplicates
# of the data in `bnf_test_20200101`, so would all be compatible
# These have dates 20190901, 20221001, 20221201
# bnf_release_excl_last_concept is not compatible, and has date 20221101

# When the draft version is saved for review, releases with more recent valid_from
# dates are checked for compatibility in order from oldest to newest. If a release
# is found to be incompatible, no later releases are checked.
#
# i.e. for this draft, releases are checked in this order:
# (bnf_import-data_20190901 is skipped because it's earlier than the draft's cs release)
# 1) bnf_import-data_20220901: compatible
# 2) bnf_import-data_20221101: incompatible
# 3) bnf_import-data_20221201: compatible (but later than an incompatible release, so not checked)

assert bnf_version_with_search.status == Status.DRAFT
assert not bnf_version_with_search.compatible_releases.exists()
save_draft_for_review(draft=bnf_version_with_search)

assert bnf_version_with_search.compatible_releases.count() == 1
assert (
bnf_version_with_search.compatible_releases.first().database_alias
== "bnf_import-data_20220901"
db_alias = "bnf_import-data_20190101"

@pytest.mark.usefixtures(
"_bnf_releases",
"_get_bnf_release_excl_last_concept",
"_get_bnf_version_with_search",
)
def test_save_codelist_draft_updates_compatibility_multiple_releases(self):
# In this test, we have a draft created with release `bnf_test_20200101`
# The `bnf_releases` fixture gives us 3 other releases, which are duplicates
# of the data in `bnf_test_20200101`, so would all be compatible
# These have dates 20190901, 20221001, 20221201
# bnf_release_excl_last_concept is not compatible, and has date 20221101

# When the draft version is saved for review, releases with more recent valid_from
# dates are checked for compatibility in order from oldest to newest. If a release
# is found to be incompatible, no later releases are checked.
#
# i.e. for this draft, releases are checked in this order:
# (bnf_import-data_20190901 is skipped because it's earlier than the draft's cs release)
# 1) bnf_import-data_20220901: compatible
# 2) bnf_import-data_20221101: incompatible
# 3) bnf_import-data_20221201: compatible (but later than an incompatible release, so not checked)

assert self.bnf_version_with_search.status == Status.DRAFT
assert not self.bnf_version_with_search.compatible_releases.exists()
save_draft_for_review(draft=self.bnf_version_with_search)

assert self.bnf_version_with_search.compatible_releases.count() == 1
assert (
self.bnf_version_with_search.compatible_releases.first().database_alias
== "bnf_import-data_20220901"
)

0 comments on commit 340142f

Please sign in to comment.