Skip to content

Commit

Permalink
Add app layer for cancelling holds
Browse files Browse the repository at this point in the history
  • Loading branch information
subhashb committed Aug 17, 2024
1 parent 7e74f82 commit 9d4a3d9
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/lending/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
)

from lending.app.dailysheet import DailySheet # isort:skip
from lending.app.patron.hold import PlaceHold # isort:skip
from lending.app.patron.hold import CancelHold, PlaceHold # isort:skip
from lending.app.patron.checkout import CheckoutBook # isort:skip


Expand All @@ -37,6 +37,7 @@
"BookType",
"place_hold",
"PlaceHold",
"CancelHold",
"DailySheetService",
"checkout",
"DailySheet",
Expand Down
12 changes: 12 additions & 0 deletions src/lending/app/patron/hold.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ class PlaceHold:
hold_type = String(required=True)


@lending.command(part_of="Patron")
class CancelHold:
patron_id = Identifier(required=True)
hold_id = Identifier(required=True)


@lending.command_handler(part_of="Patron")
class HoldCommandHandler:
@handle(PlaceHold)
Expand All @@ -22,3 +28,9 @@ def handle_place_hold(self, command: PlaceHold) -> None:

place_hold(patron, book, command.branch_id, command.hold_type)()
current_domain.repository_for(Patron).add(patron)

@handle(CancelHold)
def handle_cancel_hold(self, command: CancelHold) -> None:
patron = current_domain.repository_for(Patron).get(command.patron_id)
patron.cancel_hold(command.hold_id)
current_domain.repository_for(Patron).add(patron)
90 changes: 88 additions & 2 deletions tests/lending/app/bdd/hold_handlers/step_defs/hold_steps.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
from datetime import date, timedelta

import pytest
from protean import current_domain, g
from protean.exceptions import ValidationError
from pytest_bdd import given, then, when

from lending import Book, BookStatus, HoldType, PlaceHold
from lending import (
Book,
BookStatus,
CancelHold,
CheckoutBook,
HoldStatus,
HoldType,
Patron,
PlaceHold,
)


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -52,6 +63,59 @@ def a_book_is_already_on_hold_by_another_patron(book):
g.current_book = book


@given("a patron has an active hold")
def patron_with_active_hold(patron, book):
g.current_user = patron
g.current_book = book

command = PlaceHold(
patron_id=g.current_user.id,
book_id=g.current_book.id,
branch_id="1",
hold_type=HoldType.CLOSED_ENDED.value,
)
current_domain.process(command)


@given("a patron has an expired hold")
def patron_with_expired_hold(patron, book):
g.current_user = patron
g.current_book = book

command = PlaceHold(
patron_id=g.current_user.id,
book_id=g.current_book.id,
branch_id="1",
hold_type=HoldType.CLOSED_ENDED.value,
)
current_domain.process(command)

refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
refreshed_patron.holds[0].expires_on = date.today() - timedelta(days=1)
current_domain.repository_for(Patron).add(refreshed_patron)


@given("a patron has a hold that has been checked out")
def patron_with_hold_that_has_been_checked_out(patron, book):
g.current_user = patron
g.current_book = book

command = PlaceHold(
patron_id=g.current_user.id,
book_id=g.current_book.id,
branch_id="1",
hold_type=HoldType.CLOSED_ENDED.value,
)
current_domain.process(command)

command = CheckoutBook(
patron_id=g.current_user.id,
book_id=g.current_book.id,
branch_id="1",
)
current_domain.process(command)


@when("the patron places a hold on the book")
@when("the patron tries to place a hold on the book")
@when("the patron tries to place a hold on a book")
Expand All @@ -68,6 +132,21 @@ def the_patron_places_a_hold_on_the_book():
g.current_exception = exc


@when("the patron cancels the hold")
@when("the patron tries to cancel the hold")
def cancel_hold():
try:
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
command = CancelHold(
patron_id=refreshed_patron.id,
hold_id=refreshed_patron.holds[0].id,
)

current_domain.process(command)
except ValidationError as exc:
g.current_exception = exc


@then("the hold is successfully placed")
def the_hold_is_successfully_placed():
message = current_domain.event_store.store.read_last_message(
Expand All @@ -83,7 +162,8 @@ def the_book_is_marked_as_held():


@then("the hold placement is rejected")
def the_hold_placement_is_rejected():
@then("the cancellation is rejected")
def action_is_rejected():
assert hasattr(g, "current_exception")
assert isinstance(g.current_exception, ValidationError)

Expand All @@ -92,3 +172,9 @@ def the_hold_placement_is_rejected():
def the_book_is_not_marked_as_held():
book = current_domain.repository_for(Book).get(g.current_book.id)
assert book.status != BookStatus.ON_HOLD.value


@then("the hold is successfully canceled")
def check_hold_canceled():
refreshed_patron = current_domain.repository_for(Patron).get(g.current_user.id)
assert refreshed_patron.holds[0].status == HoldStatus.CANCELLED.value

0 comments on commit 9d4a3d9

Please sign in to comment.