diff --git a/src/palace/manager/api/admin/controller/custom_lists.py b/src/palace/manager/api/admin/controller/custom_lists.py index 5f9e530e6e..9ae59a7903 100644 --- a/src/palace/manager/api/admin/controller/custom_lists.py +++ b/src/palace/manager/api/admin/controller/custom_lists.py @@ -291,9 +291,9 @@ def _create_or_update_list( # so the upstream counts can be calculated correctly. documents = Work.to_search_documents( self._db, - [w.id for w in works_to_update_in_search], + [w.id for w in works_to_update_in_search if w.id is not None], ) - # TODO: Does this need to be done here, or can this be done in the task? + # TODO: Does this need to be done here, or can this be done asynchronously? self.search_engine.add_documents(documents) self.search_engine.search_service().refresh() diff --git a/src/palace/manager/scripts/initialization.py b/src/palace/manager/scripts/initialization.py index 87e8b30c48..49d30972b2 100644 --- a/src/palace/manager/scripts/initialization.py +++ b/src/palace/manager/scripts/initialization.py @@ -11,7 +11,7 @@ from palace.manager.celery.tasks.search import do_migration from palace.manager.search.revision import SearchSchemaRevision -from palace.manager.search.service import SearchService +from palace.manager.search.service import SearchService, SearchWritePointer from palace.manager.service.container import container_instance from palace.manager.sqlalchemy.session import SessionManager from palace.manager.sqlalchemy.util import LOCK_ID_DB_INIT, pg_advisory_lock @@ -112,9 +112,11 @@ def create_search_index( @classmethod def migrate_search( - cls, service: SearchService, revision: SearchSchemaRevision + cls, + service: SearchService, + revision: SearchSchemaRevision, + write_pointer: SearchWritePointer, ) -> None: - write_pointer = service.write_pointer() if write_pointer.version != revision.version: # The revision is not the most recent. We need to create a new index. # and start reindexing our data into it asynchronously. When the reindex @@ -137,15 +139,16 @@ def initialize_search(self) -> None: service = self._container.search.service() revision_directory = self._container.search.revision_directory() revision = revision_directory.highest() + write_pointer = service.write_pointer() - if not service.write_pointer(): + if write_pointer is None: # A write pointer does not exist. This is a fresh index. self.log.info("Search index does not exist. Creating a new index.") self.create_search_index(service, revision) else: # The index already exists. We need to check if the revision is the most recent. self.log.info("Search index exists. Checking for search migrations.") - self.migrate_search(service, revision) + self.migrate_search(service, revision, write_pointer) self.log.info("Search initialization complete.") def run(self) -> None: diff --git a/src/palace/manager/sqlalchemy/model/work.py b/src/palace/manager/sqlalchemy/model/work.py index ebe52c4034..b73a0ae237 100644 --- a/src/palace/manager/sqlalchemy/model/work.py +++ b/src/palace/manager/sqlalchemy/model/work.py @@ -1741,7 +1741,10 @@ def target_age_query(self, foreign_work_id_field): def to_search_document(self) -> dict[str, Any]: """Generate a search document for this Work.""" db = Session.object_session(self) - return Work.to_search_documents(db, [self.id])[0] + if self.id is not None: + return Work.to_search_documents(db, [self.id])[0] + else: + return {} def mark_licensepools_as_superceded(self): """Make sure that all but the single best open-access LicensePool for diff --git a/tests/fixtures/search.py b/tests/fixtures/search.py index 547a009d16..54194468a0 100644 --- a/tests/fixtures/search.py +++ b/tests/fixtures/search.py @@ -319,7 +319,7 @@ def clear(self): def disable_fixture(self): self.patch.stop() - def is_queued(self, work: int | Work, clear: bool = False) -> bool: + def is_queued(self, work: int | Work, *, clear: bool = False) -> bool: if isinstance(work, Work): work_id = work.id else: @@ -343,6 +343,6 @@ def fixture(cls): @pytest.fixture(scope="function") -def work_external_indexing() -> WorkExternalIndexingFixture: +def work_external_indexing() -> Generator[WorkExternalIndexingFixture, None, None]: with WorkExternalIndexingFixture.fixture() as fixture: yield fixture diff --git a/tests/manager/scripts/test_initialization.py b/tests/manager/scripts/test_initialization.py index 475cb250c7..bc6ab9b4dd 100644 --- a/tests/manager/scripts/test_initialization.py +++ b/tests/manager/scripts/test_initialization.py @@ -124,19 +124,38 @@ def test__get_alembic_config(self, db: DatabaseTransactionFixture): ) assert conf.config_file_name == str(test_ini.resolve()) - def test_initialize_search_indexes( - self, external_search_fixture: ExternalSearchFixture - ): + def test_initialize_search(self, external_search_fixture: ExternalSearchFixture): service = external_search_fixture.service base_name = service.base_revision_name script = InstanceInitializationScript() + script.create_search_index = MagicMock(wraps=script.create_search_index) + script.migrate_search = MagicMock(wraps=script.migrate_search) # Initially this should not exist, if the search service hasn't been initialized assert service.read_pointer() is None + assert service.write_pointer() is None # Do the initial search index creation script.initialize_search() + # We should have created the search index, and not tried to migrate it, + # since we know its a new index. + script.create_search_index.assert_called_once() + script.migrate_search.assert_not_called() + # Then we have the latest version index - assert base_name in service.read_pointer() + read_pointer = service.read_pointer() + assert read_pointer is not None + assert base_name in read_pointer + + write_pointer = service.write_pointer() + assert write_pointer is not None + assert base_name in write_pointer.name + + # Now we try to initialize the search index again, and we should not create a new index + script.create_search_index.reset_mock() + script.initialize_search() + + script.create_search_index.assert_not_called() + script.migrate_search.assert_called_once() diff --git a/tests/manager/scripts/test_search.py b/tests/manager/scripts/test_search.py index cb5b167122..3fc11fe75b 100644 --- a/tests/manager/scripts/test_search.py +++ b/tests/manager/scripts/test_search.py @@ -1,57 +1,23 @@ from __future__ import annotations -import random +from unittest.mock import patch from palace.manager.scripts.search import RebuildSearchIndexScript -from palace.manager.sqlalchemy.model.coverage import WorkCoverageRecord from tests.fixtures.database import DatabaseTransactionFixture -from tests.fixtures.search import ExternalSearchFixtureFake class TestRebuildSearchIndexScript: - def test_do_run( - self, - db: DatabaseTransactionFixture, - external_search_fake_fixture: ExternalSearchFixtureFake, - ): - index = external_search_fake_fixture.external_search - work = db.work(with_license_pool=True) - work2 = db.work(with_license_pool=True) - wcr = WorkCoverageRecord - decoys = [wcr.QUALITY_OPERATION, wcr.SUMMARY_OPERATION] - - # Set up some coverage records. - for operation in decoys + [wcr.UPDATE_SEARCH_INDEX_OPERATION]: - for w in (work, work2): - wcr.add_for(w, operation, status=random.choice(wcr.ALL_STATUSES)) - - coverage_qu = db.session.query(wcr).filter( - wcr.operation == wcr.UPDATE_SEARCH_INDEX_OPERATION - ) - original_coverage = [x.id for x in coverage_qu] - - # Run the script. - script = RebuildSearchIndexScript(db.session, search_index_client=index) - [progress] = script.do_run() - - # The mock methods were called with the values we expect. - assert {work.id, work2.id} == set( - map( - lambda d: d["_id"], external_search_fake_fixture.service.documents_all() - ) - ) - - # The script returned a list containing a single - # CoverageProviderProgress object containing accurate - # information about what happened (from the CoverageProvider's - # point of view). - assert ( - "Items processed: 2. Successes: 2, transient failures: 0, persistent failures: 0" - == progress.achievements - ) - - # The old WorkCoverageRecords for the works were deleted. Then - # the CoverageProvider did its job and new ones were added. - new_coverage = [x.id for x in coverage_qu] - assert 2 == len(new_coverage) - assert set(new_coverage) != set(original_coverage) + def test_do_run(self, db: DatabaseTransactionFixture): + # If we are called with no arguments, we default to asynchronously rebuilding the search index. + with patch( + "palace.manager.scripts.search.search_reindex" + ) as mock_search_reindex: + RebuildSearchIndexScript(db.session).do_run() + mock_search_reindex.delay.assert_called_once_with() + + # If we are called with the --blocking argument, we rebuild the search index synchronously. + with patch( + "palace.manager.scripts.search.search_reindex" + ) as mock_search_reindex: + RebuildSearchIndexScript(db.session, cmd_args=["--blocking"]).do_run() + mock_search_reindex.assert_called_once_with() diff --git a/tests/manager/search/test_migration_states.py b/tests/manager/search/test_migration_states.py deleted file mode 100644 index 10aefa63c4..0000000000 --- a/tests/manager/search/test_migration_states.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Explicitly test the different states of migration, and ensure we are adhering to the principles set out. -These tests do have some overlap with the unit tests for the search migration, but these are specific to the migration use cases. -Initial Case -- No pointers or indices are available -- The System comes online for the first time and some prep work must be done -- The initial versioned indices and pointers should be prepped by the init_instance script -- The ExternalSearchIndex should not be hindered by this -Migration Case -- Pointers exist, indices exist -- The migration contains a new version for the index -- The search_index_refresh script, when run, should create and populate the indices, and move the red/write pointers -- The ExternalSearchIndex should not be hindered by this, and should continue to work with the pointers, regardless of where they point -""" - -import pytest - -from palace.manager.scripts.coverage_provider import RunWorkCoverageProviderScript -from palace.manager.scripts.initialization import InstanceInitializationScript -from palace.manager.search.document import SearchMappingDocument -from palace.manager.search.external_search import ExternalSearchIndex -from palace.manager.search.revision import SearchSchemaRevision -from palace.manager.search.revision_directory import SearchRevisionDirectory -from tests.fixtures.search import ExternalSearchFixture - - -class TestMigrationStates: - def test_initial_migration_case( - self, external_search_fixture: ExternalSearchFixture - ): - fx = external_search_fixture - index = fx.index - service = fx.service - - # We cannot make any requests before we initialize - with pytest.raises(Exception) as raised: - index.query_works("") - assert "index_not_found" in str(raised.value) - - # When a new sytem comes up the first code to run is the InstanceInitialization script - # This preps the DB and the search indices/pointers - InstanceInitializationScript().initialize_search_indexes() - - # Ensure we have created the index and pointers - new_index_name = index._revision.name_for_index(fx.index_prefix) - empty_index_name = service._empty(fx.index_prefix) - all_indices = fx.client.indices.get("*") - - assert fx.index_prefix in new_index_name - assert new_index_name in all_indices.keys() - assert empty_index_name in all_indices.keys() - assert fx.client.indices.exists_alias( - index._search_read_pointer, index=new_index_name - ) - assert fx.client.indices.exists_alias( - index._search_write_pointer, index=new_index_name - ) - - # The same client should work without issue once the pointers are setup - assert index.query_works("").hits == [] - - def test_migration_case(self, external_search_fixture: ExternalSearchFixture): - fx = external_search_fixture - db = fx.db - - # The initial indices setup - InstanceInitializationScript().initialize_search_indexes() - - MOCK_VERSION = 1000001 - - class MockSchema(SearchSchemaRevision): - def __init__(self, v: int): - self.SEARCH_VERSION = v - super().__init__() - - def mapping_document(self) -> SearchMappingDocument: - return SearchMappingDocument() - - client = ExternalSearchIndex( - fx.search_container.service(), - revision_directory=SearchRevisionDirectory( - {MOCK_VERSION: MockSchema(MOCK_VERSION)} - ), - ) - - # The search client works just fine - assert client.query_works("") is not None - receiver = client.start_updating_search_documents() - receiver.add_documents([{"work_id": 123}]) - receiver.finish() - - mock_index_name = client._revision.name_for_index(fx.service.base_revision_name) - assert str(MOCK_VERSION) in mock_index_name - - # The mock index does not exist yet - with pytest.raises(Exception) as raised: - fx.client.indices.get(mock_index_name) - assert "index_not_found" in str(raised.value) - - # This should run the migration - RunWorkCoverageProviderScript( - SearchIndexCoverageProvider, db.session, search_index_client=client - ).run() - - # The new version is created, and the aliases point to the right index - assert fx.client.indices.get(mock_index_name) is not None - assert mock_index_name in fx.client.indices.get_alias( - name=client._search_read_pointer - ) - assert mock_index_name in fx.client.indices.get_alias( - name=client._search_write_pointer - ) diff --git a/tests/manager/search/test_migrator.py b/tests/manager/search/test_migrator.py deleted file mode 100644 index 56660a4dd9..0000000000 --- a/tests/manager/search/test_migrator.py +++ /dev/null @@ -1,249 +0,0 @@ -from unittest.mock import MagicMock, Mock, call - -import pytest - -from palace.manager.search.document import SearchMappingDocument -from palace.manager.search.revision import SearchSchemaRevision -from palace.manager.search.revision_directory import SearchRevisionDirectory -from palace.manager.search.service import SearchWritePointer - - -class EmptyRevision(SearchSchemaRevision): - SEARCH_VERSION = 0 - - def __init__(self, version: int): - self.SEARCH_VERSION = version - super().__init__() - - def mapping_document(self) -> SearchMappingDocument: - return SearchMappingDocument() - - -class TestMigrator: - def test_migrate_no_revisions(self): - """If a revision isn't available, the migration fails fast.""" - service = Mock() - revisions = SearchRevisionDirectory.empty() - migrator = SearchMigrator(revisions, service) - with pytest.raises(SearchMigrationException): - migrator.migrate(base_name="any", version=23) - - def test_migrate_from_empty(self): - """With an empty search state, migrating to a supported version works.""" - service = Mock() - service.read_pointer = MagicMock(return_value=None) - service.write_pointer = MagicMock(return_value=None) - service.index_is_populated = MagicMock(return_value=False) - service.index_set_populated = MagicMock() - - revision = EmptyRevision(3) - revisions = SearchRevisionDirectory({revision.version: revision}) - migrator = SearchMigrator(revisions, service) - - migration = migrator.migrate(base_name="works", version=revision.version) - migration.finish() - - # The sequence of expected calls. - service.create_empty_index.assert_called_with() - service.read_pointer.assert_called_with() - # The read pointer didn't exist, so it's set to the empty index - service.read_pointer_set_empty.assert_called_with() - service.write_pointer.assert_called_with() - # The new index is created and populated. - service.index_create.assert_called_with(revision) - service.populate_index.assert_not_called() - # Both the read and write pointers are set. - service.write_pointer_set.assert_called_with(revision) - service.read_pointer_set.assert_called_with(revision) - service.index_set_populated.assert_called_with(revision) - - def test_migrate_upgrade(self): - """Index 2 exists, and we can migrate to 3.""" - service = Mock() - service.read_pointer = MagicMock(return_value="works-v2") - service.write_pointer = MagicMock(return_value=None) - service.index_is_populated = MagicMock(return_value=False) - service.index_set_mapping = MagicMock() - service.index_submit_documents = MagicMock() - service.index_set_populated = MagicMock() - - revision = EmptyRevision(3) - revisions = SearchRevisionDirectory({revision.version: revision}) - migrator = SearchMigrator(revisions, service) - - docs = migrator.migrate(base_name="works", version=revision.version) - docs.add_documents([{"_id": "1"}, {"_id": "2"}, {"_id": "3"}]) - docs.add_documents([{"_id": "4"}, {"_id": "5"}, {"_id": "6"}]) - docs.add_documents([{"_id": "7"}, {"_id": "8"}]) - docs.finish() - - # The sequence of expected calls. - service.create_empty_index.assert_called_with() - # The read pointer existed, so it's left alone for now. - service.read_pointer.assert_called_with() - service.write_pointer.assert_called_with() - # The index for version 3 is created and populated. - service.index_create.assert_called_with(revision) - service.index_set_mapping.assert_called_with(revision) - service.index_submit_documents.assert_has_calls( - [ - call( - pointer="works-v3", - documents=[{"_id": "1"}, {"_id": "2"}, {"_id": "3"}], - ), - call( - pointer="works-v3", - documents=[{"_id": "4"}, {"_id": "5"}, {"_id": "6"}], - ), - call( - pointer="works-v3", - documents=[{"_id": "7"}, {"_id": "8"}], - ), - ] - ) - # Both the read and write pointers are set. - service.write_pointer_set.assert_called_with(revision) - service.read_pointer_set.assert_called_with(revision) - service.index_set_populated.assert_called_with(revision) - - def test_migrate_upgrade_cancel(self): - """Cancelling a migration leaves the pointers untouched.""" - service = Mock() - service.read_pointer = MagicMock(return_value="works-v2") - service.write_pointer = MagicMock(return_value=None) - service.index_is_populated = MagicMock(return_value=False) - service.index_set_mapping = MagicMock() - service.index_submit_documents = MagicMock() - service.index_set_populated = MagicMock() - - revision = EmptyRevision(3) - revisions = SearchRevisionDirectory({revision.version: revision}) - migrator = SearchMigrator(revisions, service) - - docs = migrator.migrate(base_name="works", version=revision.version) - docs.add_documents([{"_id": "1"}, {"_id": "2"}, {"_id": "3"}]) - docs.add_documents([{"_id": "4"}, {"_id": "5"}, {"_id": "6"}]) - docs.add_documents([{"_id": "7"}, {"_id": "8"}]) - docs.cancel() - - # The sequence of expected calls. - service.create_empty_index.assert_called_with() - # The read pointer existed, so it's left alone for now. - service.read_pointer.assert_called_with() - service.write_pointer.assert_called_with() - # The index for version 3 is created and populated. - service.index_create.assert_called_with(revision) - service.index_set_mapping.assert_called_with(revision) - service.index_submit_documents.assert_has_calls( - [ - call( - pointer="works-v3", - documents=[{"_id": "1"}, {"_id": "2"}, {"_id": "3"}], - ), - call( - pointer="works-v3", - documents=[{"_id": "4"}, {"_id": "5"}, {"_id": "6"}], - ), - call( - pointer="works-v3", - documents=[{"_id": "7"}, {"_id": "8"}], - ), - ] - ) - # Both the read and write pointers are left untouched. - service.write_pointer_set.assert_not_called() - service.read_pointer_set.assert_not_called() - service.index_set_populated.assert_not_called() - - def test_migrate_no_op(self): - """Index 3 already exists, so migrating to 3 is a no-op.""" - service = Mock() - service.read_pointer = MagicMock(return_value="works-v3") - service.write_pointer = MagicMock(return_value=SearchWritePointer("works", 3)) - service.index_is_populated = MagicMock(return_value=True) - service.index_set_populated = MagicMock() - - revision = EmptyRevision(3) - revisions = SearchRevisionDirectory({revision.version: revision}) - migrator = SearchMigrator(revisions, service) - docs = migrator.migrate("works", revision.version) - assert docs is None - - # The sequence of expected calls. - service.create_empty_index.assert_called_with() - # The read pointer existed, so it's left alone for now. - service.read_pointer.assert_called_with() - service.write_pointer.assert_called_with() - # The index for version 3 already exists and is populated, so nothing happens. - service.index_create.assert_not_called() - service.index_set_mapping.assert_not_called() - # The write pointer is set redundantly. - service.write_pointer_set.assert_called_with(revision) - # The read pointer is set redundantly. - service.read_pointer_set.assert_called_with(revision) - # The "indexed" flag is set redundantly. - service.index_set_populated.assert_called_with(revision) - - def test_migrate_from_indexed_2_to_3_unpopulated(self): - """Index 3 exists but is not populated. Migrating involves populating it.""" - service = Mock() - service.read_pointer = MagicMock(return_value="works-v2") - service.write_pointer = MagicMock(return_value=SearchWritePointer("works", 2)) - service.index_is_populated = MagicMock(return_value=False) - service.index_set_populated = MagicMock() - - revision = EmptyRevision(3) - revisions = SearchRevisionDirectory({revision.version: revision}) - migrator = SearchMigrator(revisions, service) - migration = migrator.migrate("works", revision.version) - migration.add_documents([]) - migration.finish() - - # The sequence of expected calls. - service.create_empty_index.assert_called_with() - # The read pointer existed, so it's left alone for now. - service.read_pointer.assert_called_with() - service.write_pointer.assert_called_with() - # The index for version 3 exists but isn't populated, so it is populated. - service.index_create.assert_called_with(revision) - service.index_set_mapping.assert_called_with(revision) - service.index_submit_documents.assert_has_calls( - [ - call( - pointer="works-v3", - documents=[], - ) - ] - ) - # Both the read and write pointers are updated. - service.write_pointer_set.assert_called_with(revision) - service.read_pointer_set.assert_called_with(revision) - service.index_set_populated.assert_called_with(revision) - - def test_migrate_from_indexed_2_to_3_write_unset(self): - """Index 3 exists and is populated, but the write pointer is unset.""" - service = Mock() - service.read_pointer = MagicMock(return_value="works-v2") - service.write_pointer = MagicMock(return_value=None) - service.index_is_populated = MagicMock(return_value=True) - service.index_set_populated = MagicMock() - - revision = EmptyRevision(3) - revisions = SearchRevisionDirectory({revision.version: revision}) - migrator = SearchMigrator(revisions, service) - docs = migrator.migrate("works", revision.version) - assert docs is None - - # The sequence of expected calls. - service.create_empty_index.assert_called_with() - # The read pointer existed, so it's left alone for now. - service.read_pointer.assert_called_with() - # The write pointer is completely unset. - service.write_pointer.assert_called_with() - # The index for version 3 exists and is populated. The create call is redundant but harmless. - service.index_create.assert_called_with(revision) - service.populate_index.assert_not_called() - # Both the read and write pointers are updated. - service.write_pointer_set.assert_called_with(revision) - service.read_pointer_set.assert_called_with(revision) - service.index_set_populated.assert_called_with(revision) diff --git a/tests/manager/sqlalchemy/test_listeners.py b/tests/manager/sqlalchemy/test_listeners.py index a5efce084e..0b63fb05ce 100644 --- a/tests/manager/sqlalchemy/test_listeners.py +++ b/tests/manager/sqlalchemy/test_listeners.py @@ -1,15 +1,17 @@ from __future__ import annotations import functools +from collections.abc import Callable from unittest.mock import patch import pytest from palace.manager.core.config import Configuration from palace.manager.sqlalchemy.listeners import site_configuration_has_changed -from palace.manager.sqlalchemy.model.coverage import Timestamp, WorkCoverageRecord +from palace.manager.sqlalchemy.model.coverage import Timestamp from palace.manager.util.datetime_helpers import utc_now from tests.fixtures.database import DatabaseTransactionFixture +from tests.fixtures.search import WorkExternalIndexingFixture class TestSiteConfigurationHasChanged: @@ -122,59 +124,43 @@ def _set_property(object, value, property_name): class TestListeners: @pytest.mark.parametrize( - "name,status_property_setter", + "status_property_setter", [ - ( - "works_when_open_access_property_changes", + pytest.param( functools.partial(_set_property, property_name="open_access"), + id="works_when_open_access_property_changes", ), ], ) - def test_licensepool_storage_status_change( + def test_licensepool_status_change( self, - db, - name, - status_property_setter, + db: DatabaseTransactionFixture, + work_external_indexing: WorkExternalIndexingFixture, + status_property_setter: Callable[..., None], ): - # Arrange work = db.work(with_license_pool=True) [pool] = work.license_pools - # Clear out any WorkCoverageRecords created as the work was initialized. - work.coverage_records = [] - - # Act # Change the field status_property_setter(pool, True) + assert work_external_indexing.is_queued(work, clear=True) # Then verify that if the field is 'set' to its existing value, this doesn't happen. - # pool.self_hosted = True status_property_setter(pool, True) + assert not work_external_indexing.is_queued(work, clear=True) - # Assert - assert 1 == len(work.coverage_records) - assert work.id == work.coverage_records[0].work_id - assert ( - WorkCoverageRecord.UPDATE_SEARCH_INDEX_OPERATION - == work.coverage_records[0].operation - ) - assert WorkCoverageRecord.REGISTERED == work.coverage_records[0].status - - def test_work_suppressed_for_library(self, db: DatabaseTransactionFixture): + def test_work_suppressed_for_library( + self, + db: DatabaseTransactionFixture, + work_external_indexing: WorkExternalIndexingFixture, + ): work = db.work(with_license_pool=True) library = db.library() - # Clear out any WorkCoverageRecords created as the work was initialized. - work.coverage_records = [] - - # Act + # Suppress the work for the library work.suppressed_for.append(library) + assert work_external_indexing.is_queued(work, clear=True) - # Assert - assert 1 == len(work.coverage_records) - assert work.id == work.coverage_records[0].work_id - assert ( - WorkCoverageRecord.UPDATE_SEARCH_INDEX_OPERATION - == work.coverage_records[0].operation - ) - assert WorkCoverageRecord.REGISTERED == work.coverage_records[0].status + # Unsuppress the work for the library + work.suppressed_for.remove(library) + assert work_external_indexing.is_queued(work, clear=True) diff --git a/tests/migration/test_instance_init_script.py b/tests/migration/test_instance_init_script.py index ec7f083873..ba423db381 100644 --- a/tests/migration/test_instance_init_script.py +++ b/tests/migration/test_instance_init_script.py @@ -41,7 +41,7 @@ def __init__( self.migrate_database_mock = Mock(wraps=self.script.migrate_database) self.script.migrate_database = self.migrate_database_mock - def initialize_database(self): + def initialize_database(self) -> None: self.script.initialize_database(self.database.connection) @classmethod