Skip to content

Commit

Permalink
Introduce Checkout command handler
Browse files Browse the repository at this point in the history
This commit includes the CheckoutBook command along with its corresponding command handler
and method.
  • Loading branch information
subhashb committed Aug 17, 2024
1 parent 3d88a88 commit 1c436ba
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 2 deletions.
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/lending/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

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


__all__ = [
Expand All @@ -29,6 +30,7 @@
"HoldStatus",
"HoldPlaced",
"Checkout",
"CheckoutBook",
"CheckoutStatus",
"Book",
"BookStatus",
Expand Down
23 changes: 23 additions & 0 deletions src/lending/app/patron/checkout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from protean import current_domain, handle
from protean.fields import Identifier

from lending import Book, Patron, checkout
from lending.domain import lending


@lending.command(part_of="Patron")
class CheckoutBook:
patron_id = Identifier(required=True, identifier=True)
book_id = Identifier(required=True)
branch_id = Identifier(required=True)


@lending.command_handler(part_of="Patron")
class CheckoutCommandHandler:
@handle(CheckoutBook)
def handle_checkout_book(self, command: CheckoutBook) -> None:
patron = current_domain.repository_for(Patron).get(command.patron_id)
book = current_domain.repository_for(Book).get(command.book_id)

checkout(patron, book, command.branch_id)()
current_domain.repository_for(Patron).add(patron)
2 changes: 1 addition & 1 deletion src/lending/app/patron/hold.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class PlaceHold:
@lending.command_handler(part_of="Patron")
class HoldCommandHandler:
@handle(PlaceHold)
def handle_PlaceHold(self, command: PlaceHold) -> None:
def handle_place_hold(self, command: PlaceHold) -> None:
patron = current_domain.repository_for(Patron).get(command.patron_id)
book = current_domain.repository_for(Book).get(command.book_id)

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Check Out a Book

Scenario: Patron checks out a book on hold
Given a circulating book is available
And a patron is logged in
And the patron has a hold on the book
When the patron checks out the book
Then the checkout is successfully completed
And the checkout has a validity of CHECKOUT_PERIOD
And the hold is marked as checked out
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from datetime import date, timedelta

import pytest
from protean import current_domain, g
from pytest_bdd import given, then, when
from pytest_bdd.parsers import cfparse

from lending import CheckoutBook, HoldStatus, HoldType, Patron, PlaceHold


@pytest.fixture(autouse=True)
def reset_globals():
yield

if hasattr(g, "current_user"):
delattr(g, "current_user")
if hasattr(g, "current_book"):
delattr(g, "current_book")
if hasattr(g, "current_exception"):
delattr(g, "current_exception")


@given("a circulating book is available")
def circulating_book_available(book):
g.current_book = book


@given("a patron is logged in")
def patron_logged_in(regular_patron):
g.current_user = regular_patron


@given("the patron has a hold on the book")
def patron_has_hold_on_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)


@when("the patron checks out the book")
def patron_checks_out_book():
command = CheckoutBook(
patron_id=g.current_user.id,
book_id=g.current_book.id,
branch_id="1",
)
current_domain.process(command)


@then("the checkout is successfully completed")
def checkout_completed():
message = current_domain.event_store.store.read_last_message(
f"library::patron-{g.current_user.id}"
)
assert message.metadata.type == "Library.BookCheckedOut.v1"


@then(cfparse("the checkout has a validity of {validity_days_config}"))
def checkout_validity(validity_days_config):
patron = current_domain.repository_for(Patron).get(g.current_user.id)
checkout = patron.checkouts[0]
assert checkout.due_on == date.today() + timedelta(
days=current_domain.config["custom"][validity_days_config]
)


@then("the hold is marked as checked out")
def hold_marked_checked_out():
patron = current_domain.repository_for(Patron).get(g.current_user.id)
hold = patron.holds[0]
assert hold.status == HoldStatus.CHECKED_OUT.value
5 changes: 5 additions & 0 deletions tests/lending/app/bdd/checkout_handlers/test_checkouts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from pytest_bdd import scenarios

from .step_defs.checkout_steps import * # noqa: F403

scenarios("./features")

0 comments on commit 1c436ba

Please sign in to comment.