diff --git a/Review b/Review new file mode 100644 index 00000000..e69de29b diff --git a/What b/What new file mode 100644 index 00000000..e69de29b diff --git a/samples/book-app-project/README.md b/samples/book-app-project/README.md index d3dd580a..8b401d70 100644 --- a/samples/book-app-project/README.md +++ b/samples/book-app-project/README.md @@ -30,6 +30,7 @@ It can add, remove, and list books. Also mark them as read. ```bash python book_app.py list python book_app.py add +python book_app.py read python book_app.py find python book_app.py remove python book_app.py help diff --git a/samples/book-app-project/book_app.py b/samples/book-app-project/book_app.py index f0100c2d..a94ae998 100644 --- a/samples/book-app-project/book_app.py +++ b/samples/book-app-project/book_app.py @@ -59,6 +59,18 @@ def handle_find(): show_books(books) +def handle_read(): + print("\nMark a Book as Read\n") + + title = input("Enter the title of the book to mark as read: ").strip() + result = collection.mark_as_read(title) + + if result: + print("\nBook marked as read.\n") + else: + print("\nBook not found.\n") + + def show_help(): print(""" Book Collection Helper @@ -66,6 +78,7 @@ def show_help(): Commands: list - Show all books add - Add a new book + read - Mark a book as read remove - Remove a book by title find - Find books by author help - Show this help message @@ -83,6 +96,8 @@ def main(): handle_list() elif command == "add": handle_add() + elif command == "read": + handle_read() elif command == "remove": handle_remove() elif command == "find": diff --git a/samples/book-app-project/tests/test_books.py b/samples/book-app-project/tests/test_books.py index 061149c5..b898247c 100644 --- a/samples/book-app-project/tests/test_books.py +++ b/samples/book-app-project/tests/test_books.py @@ -1,5 +1,6 @@ import sys import os +import importlib sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pytest @@ -51,3 +52,31 @@ def test_remove_book_invalid(): collection = BookCollection() result = collection.remove_book("Nonexistent Book") assert result is False + + +def test_read_command_marks_book_as_read(monkeypatch, capsys): + book_app = importlib.import_module("book_app") + monkeypatch.setattr(book_app, "collection", BookCollection()) + book_app.collection.add_book("Dune", "Frank Herbert", 1965) + + monkeypatch.setattr("builtins.input", lambda _prompt: "Dune") + monkeypatch.setattr(sys, "argv", ["book_app.py", "read"]) + + book_app.main() + + captured = capsys.readouterr() + assert "Book marked as read." in captured.out + assert book_app.collection.find_book_by_title("Dune").read is True + + +def test_read_command_book_not_found(monkeypatch, capsys): + book_app = importlib.import_module("book_app") + monkeypatch.setattr(book_app, "collection", BookCollection()) + + monkeypatch.setattr("builtins.input", lambda _prompt: "Missing Book") + monkeypatch.setattr(sys, "argv", ["book_app.py", "read"]) + + book_app.main() + + captured = capsys.readouterr() + assert "Book not found." in captured.out diff --git a/samples/book-app-project/tests/test_utils.py b/samples/book-app-project/tests/test_utils.py new file mode 100644 index 00000000..ba749f85 --- /dev/null +++ b/samples/book-app-project/tests/test_utils.py @@ -0,0 +1,28 @@ +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from utils import get_user_choice + + +def test_get_user_choice_rejects_empty_input(monkeypatch, capsys): + responses = iter(["", "2"]) + monkeypatch.setattr("builtins.input", lambda _prompt: next(responses)) + + choice = get_user_choice() + + captured = capsys.readouterr() + assert "Input cannot be empty" in captured.out + assert choice == "2" + + +def test_get_user_choice_rejects_non_numeric_input(monkeypatch, capsys): + responses = iter(["abc", "4"]) + monkeypatch.setattr("builtins.input", lambda _prompt: next(responses)) + + choice = get_user_choice() + + captured = capsys.readouterr() + assert "Invalid input. Please enter a number." in captured.out + assert choice == "4" diff --git a/samples/book-app-project/utils.py b/samples/book-app-project/utils.py index 4151dcda..321810db 100644 --- a/samples/book-app-project/utils.py +++ b/samples/book-app-project/utils.py @@ -8,10 +8,31 @@ def print_menu(): def get_user_choice() -> str: - return input("Choose an option (1-5): ").strip() + while True: + choice = input("Choose an option (1-5): ").strip() + if not choice: + print("Input cannot be empty. Please enter a number.") + continue + if not choice.isdigit(): + print("Invalid input. Please enter a number.") + continue + return choice def get_book_details(): + """Collect book details from user input. + + Parameters: + None: This function does not accept parameters. It reads values from + standard input prompts. + + Returns: + tuple[str, str, int]: A 3-item tuple containing: + - title (str): The entered book title (whitespace-trimmed). + - author (str): The entered author name (whitespace-trimmed). + - year (int): The publication year parsed as an integer. If the + year input is invalid, this value defaults to 0. + """ title = input("Enter book title: ").strip() author = input("Enter author: ").strip() diff --git a/what b/what new file mode 100644 index 00000000..e69de29b