-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9336637
commit 2cd8e6d
Showing
4 changed files
with
205 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
from unittest.mock import create_autospec, patch | ||
|
||
import pytest | ||
|
||
from palace.manager.celery.task import Task | ||
from palace.manager.celery.tasks.patron_activity import sync_patron_activity | ||
from palace.manager.service.integration_registry.license_providers import ( | ||
LicenseProvidersRegistry, | ||
) | ||
from palace.manager.service.logging.configuration import LogLevel | ||
from palace.manager.service.redis.models.patron_activity import ( | ||
PatronActivity, | ||
PatronActivityStatus, | ||
) | ||
from tests.fixtures.celery import CeleryFixture | ||
from tests.fixtures.database import DatabaseTransactionFixture | ||
from tests.fixtures.redis import RedisFixture | ||
from tests.fixtures.services import ServicesFixture | ||
from tests.mocks.circulation import MockPatronActivityCirculationAPI | ||
|
||
|
||
class SyncTaskFixture: | ||
def __init__( | ||
self, | ||
db: DatabaseTransactionFixture, | ||
redis_fixture: RedisFixture, | ||
celery_fixture: CeleryFixture, | ||
services_fixture: ServicesFixture, | ||
): | ||
self.db = db | ||
self.redis_fixture = redis_fixture | ||
self.celery_fixture = celery_fixture | ||
self.services_fixture = services_fixture | ||
|
||
self.library = db.library() | ||
self.patron = db.patron(library=self.library) | ||
self.collection = db.collection(library=self.library) | ||
|
||
self.redis_record = self.patron_activity(self.patron.id, self.collection.id) | ||
self.mock_registry = create_autospec(LicenseProvidersRegistry) | ||
self.services_fixture.services.integration_registry.license_providers.override( | ||
self.mock_registry | ||
) | ||
|
||
def patron_activity(self, patron_id: int, collection_id: int) -> PatronActivity: | ||
return PatronActivity( | ||
self.redis_fixture.client, patron_id, collection_id, "test-fixture-task-id" | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def sync_task_fixture( | ||
db: DatabaseTransactionFixture, | ||
redis_fixture: RedisFixture, | ||
celery_fixture: CeleryFixture, | ||
services_fixture: ServicesFixture, | ||
): | ||
return SyncTaskFixture(db, redis_fixture, celery_fixture, services_fixture) | ||
|
||
|
||
class TestSyncPatronActivity: | ||
def test_unable_to_lock( | ||
self, sync_task_fixture: SyncTaskFixture, caplog: pytest.LogCaptureFixture | ||
): | ||
caplog.set_level(LogLevel.info) | ||
|
||
# We lock the patron activity record in redis, so the task cannot acquire it. | ||
sync_task_fixture.redis_record.lock() | ||
|
||
# We patch the task to raise an exception if the db is accessed. If we don't acquire the lock | ||
# we should never go out to the database. | ||
with patch.object( | ||
Task, "_session_maker", side_effect=Exception() | ||
) as mock_session: | ||
sync_patron_activity.apply_async( | ||
(sync_task_fixture.collection.id, sync_task_fixture.patron.id, "pin") | ||
).wait() | ||
|
||
assert "Patron activity sync not needed (state: LOCKED)" in caplog.text | ||
assert mock_session.call_count == 0 | ||
|
||
def test_patron_not_found( | ||
self, | ||
sync_task_fixture: SyncTaskFixture, | ||
db: DatabaseTransactionFixture, | ||
caplog: pytest.LogCaptureFixture, | ||
): | ||
patron_id = sync_task_fixture.patron.id | ||
db.session.delete(sync_task_fixture.patron) | ||
task = sync_patron_activity.apply_async( | ||
(sync_task_fixture.collection.id, patron_id, "pin") | ||
) | ||
task.wait() | ||
|
||
assert f"Patron (id: {patron_id}) not found." in caplog.text | ||
|
||
task_status = sync_task_fixture.redis_record.status() | ||
assert task_status.state == PatronActivityStatus.State.FAILED | ||
assert task_status.task_id == task.id | ||
|
||
def test_collection_not_found( | ||
self, | ||
sync_task_fixture: SyncTaskFixture, | ||
db: DatabaseTransactionFixture, | ||
caplog: pytest.LogCaptureFixture, | ||
): | ||
collection_id = sync_task_fixture.collection.id | ||
db.session.delete(sync_task_fixture.collection) | ||
task = sync_patron_activity.apply_async( | ||
(collection_id, sync_task_fixture.patron.id, "pin") | ||
) | ||
task.wait() | ||
|
||
assert f"Collection (id: {collection_id}) not found." in caplog.text | ||
|
||
task_status = sync_task_fixture.redis_record.status() | ||
assert task_status.state == PatronActivityStatus.State.FAILED | ||
assert task_status.task_id == task.id | ||
|
||
def test_exception( | ||
self, sync_task_fixture: SyncTaskFixture, caplog: pytest.LogCaptureFixture | ||
): | ||
sync_task_fixture.collection.protocol = "unknown" | ||
|
||
with pytest.raises(KeyError): | ||
sync_patron_activity.apply_async( | ||
(sync_task_fixture.collection.id, sync_task_fixture.patron.id, "pin") | ||
).wait() | ||
|
||
task_status = sync_task_fixture.redis_record.status() | ||
assert task_status.state == PatronActivityStatus.State.FAILED | ||
|
||
assert "An exception occurred during the patron activity sync" in caplog.text | ||
|
||
def test_not_supported( | ||
self, sync_task_fixture: SyncTaskFixture, caplog: pytest.LogCaptureFixture | ||
): | ||
caplog.set_level(LogLevel.info) | ||
|
||
sync_patron_activity.apply_async( | ||
(sync_task_fixture.collection.id, sync_task_fixture.patron.id, "pin") | ||
).wait() | ||
|
||
task_status = sync_task_fixture.redis_record.status() | ||
assert task_status.state == PatronActivityStatus.State.NOT_SUPPORTED | ||
|
||
assert "does not support patron activity sync" in caplog.text | ||
sync_task_fixture.mock_registry.from_collection.assert_called_once_with( | ||
sync_task_fixture.db.session, sync_task_fixture.collection | ||
) | ||
|
||
def test_success(self, sync_task_fixture: SyncTaskFixture): | ||
mock_collection_api = MockPatronActivityCirculationAPI( | ||
sync_task_fixture.db.session, sync_task_fixture.collection | ||
) | ||
sync_task_fixture.mock_registry.from_collection.return_value = ( | ||
mock_collection_api | ||
) | ||
|
||
sync_patron_activity.apply_async( | ||
(sync_task_fixture.collection.id, sync_task_fixture.patron.id, "pin") | ||
).wait() | ||
|
||
task_status = sync_task_fixture.redis_record.status() | ||
assert task_status.state == PatronActivityStatus.State.SUCCESS | ||
|
||
sync_task_fixture.mock_registry.from_collection.assert_called_once_with( | ||
sync_task_fixture.db.session, sync_task_fixture.collection | ||
) | ||
assert len(mock_collection_api.patron_activity_calls) == 1 | ||
assert mock_collection_api.patron_activity_calls[0] == ( | ||
sync_task_fixture.patron, | ||
"pin", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters