Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion samples/book-app-project/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*(This README is intentionally rough so you can improve it with GitHub Copilot CLI)*

A Python app for managing books you have or want to read.
It can add, remove, and list books. Also mark them as read.
It can add, remove, and list books, including only unread books. Also mark them as read.

---

Expand All @@ -29,6 +29,7 @@ It can add, remove, and list books. Also mark them as read.

```bash
python book_app.py list
python book_app.py list unread
python book_app.py add
python book_app.py find
python book_app.py remove
Expand Down
10 changes: 9 additions & 1 deletion samples/book-app-project/book_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def handle_list():
show_books(books)


def handle_list_unread():
books = collection.get_unread_books()
show_books(books)


def handle_add():
print("\nAdd a New Book\n")

Expand Down Expand Up @@ -65,6 +70,7 @@ def show_help():

Commands:
list - Show all books
list unread - Show unread books
add - Add a new book
remove - Remove a book by title
find - Find books by author
Expand All @@ -79,7 +85,9 @@ def main():

command = sys.argv[1].lower()

if command == "list":
if command == "list" and len(sys.argv) > 2 and sys.argv[2].lower() == "unread":
handle_list_unread()
elif command == "list":
handle_list()
elif command == "add":
handle_add()
Expand Down
3 changes: 3 additions & 0 deletions samples/book-app-project/books.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def add_book(self, title: str, author: str, year: int) -> Book:
def list_books(self) -> List[Book]:
return self.books

def get_unread_books(self) -> List[Book]:
return [book for book in self.books if not book.read]

def find_book_by_title(self, title: str) -> Optional[Book]:
for book in self.books:
if book.title.lower() == title.lower():
Expand Down
60 changes: 60 additions & 0 deletions samples/book-app-project/tests/test_book_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import os
import sys
from pathlib import Path

import pytest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import book_app
import books
from books import BookCollection


@pytest.fixture
def collection(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> BookCollection:
data_file = tmp_path / "data.json"
data_file.write_text("[]")
monkeypatch.setattr(books, "DATA_FILE", str(data_file))
test_collection = BookCollection()
monkeypatch.setattr(book_app, "collection", test_collection)
return test_collection


def test_list_unread_command_displays_unread_books_only(
collection: BookCollection,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
collection.add_book("Dune", "Frank Herbert", 1965)
collection.add_book("1984", "George Orwell", 1949)
collection.mark_as_read("1984")
monkeypatch.setattr(sys, "argv", ["book_app.py", "list", "unread"])

book_app.main()

output = capsys.readouterr().out
assert "Dune by Frank Herbert (1965)" in output
assert "1984 by George Orwell" not in output


def test_list_unread_command_reports_no_books_when_none_are_unread(
collection: BookCollection,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
monkeypatch.setattr(sys, "argv", ["book_app.py", "list", "unread"])

book_app.main()

assert "No books found." in capsys.readouterr().out


def test_help_includes_list_unread_command(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
monkeypatch.setattr(sys, "argv", ["book_app.py", "help"])

book_app.main()

assert "list unread - Show unread books" in capsys.readouterr().out
47 changes: 47 additions & 0 deletions samples/book-app-project/tests/test_books.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,53 @@ def test_add_book():
assert book.year == 1949
assert book.read is False


def test_get_unread_books_returns_only_unread_books():
collection = BookCollection()
collection.add_book("Dune", "Frank Herbert", 1965)
collection.add_book("1984", "George Orwell", 1949)
collection.mark_as_read("1984")

assert [book.title for book in collection.get_unread_books()] == ["Dune"]


def test_get_unread_books_preserves_order():
collection = BookCollection()
collection.add_book("Dune", "Frank Herbert", 1965)
collection.add_book("1984", "George Orwell", 1949)
collection.add_book("The Hobbit", "J.R.R. Tolkien", 1937)
collection.mark_as_read("1984")

assert [book.title for book in collection.get_unread_books()] == [
"Dune",
"The Hobbit",
]


def test_get_unread_books_returns_empty_list_for_empty_collection():
collection = BookCollection()

assert collection.get_unread_books() == []


def test_get_unread_books_returns_empty_list_when_all_books_are_read():
collection = BookCollection()
collection.add_book("1984", "George Orwell", 1949)
collection.mark_as_read("1984")

assert collection.get_unread_books() == []


def test_get_unread_books_returns_separate_list():
collection = BookCollection()
collection.add_book("Dune", "Frank Herbert", 1965)

unread_books = collection.get_unread_books()
unread_books.clear()

assert len(collection.list_books()) == 1


def test_mark_book_as_read():
collection = BookCollection()
collection.add_book("Dune", "Frank Herbert", 1965)
Expand Down