Skip to content

Commit a0a9924

Browse files
ishizuka-nkcCopilot
andcommitted
fix(books): improve find_by_author to use casefold and partial matching
- Replace .lower() with .casefold() for correct Unicode handling (e.g., Straße now matches strasse) - Change exact match (==) to substring match (in) so partial author names like 'Orwell' match 'George Orwell' - Pre-compute normalized query outside loop to avoid redundant calls - Add 12 new tests covering partial name, case variations, and edge cases (empty string, single character, multiple matches) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1b44c0b commit a0a9924

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

samples/book-app-project/books.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ def remove_book(self, title: str) -> bool:
7070
return False
7171

7272
def find_by_author(self, author: str) -> List[Book]:
73-
"""Find all books by a given author."""
74-
return [b for b in self.books if b.author.lower() == author.lower()]
73+
"""Find all books where author contains the given string (case-insensitive partial match)."""
74+
normalized = author.casefold()
75+
return [b for b in self.books if normalized in b.author.casefold()]
7576

7677
def search_books(self, query: str) -> List[Book]:
7778
"""Find books where query matches (partial, case-insensitive) title or author."""

samples/book-app-project/tests/test_books.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,61 @@ def test_find_returns_all_books_by_author(self, collection):
197197
results = collection.find_by_author("George Orwell")
198198
assert len(results) == 2
199199

200+
# --- Partial match ---
201+
202+
def test_partial_last_name_returns_match(self, populated_collection):
203+
results = populated_collection.find_by_author("Orwell")
204+
assert len(results) == 1
205+
assert results[0].title == "1984"
206+
207+
def test_partial_first_name_returns_match(self, populated_collection):
208+
results = populated_collection.find_by_author("George")
209+
assert len(results) == 1
210+
assert results[0].author == "George Orwell"
211+
212+
def test_partial_name_matches_multiple_authors(self, collection):
213+
collection.add_book("Book A", "George Orwell", 1945)
214+
collection.add_book("Book B", "George Bernard Shaw", 1900)
215+
collection.add_book("Book C", "Frank Herbert", 1965)
216+
results = collection.find_by_author("George")
217+
assert len(results) == 2
218+
219+
def test_partial_name_no_match_returns_empty(self, populated_collection):
220+
results = populated_collection.find_by_author("Asimov")
221+
assert results == []
222+
223+
# --- Case variations ---
224+
225+
@pytest.mark.parametrize("query", [
226+
"orwell",
227+
"ORWELL",
228+
"Orwell",
229+
"oRwElL",
230+
])
231+
def test_partial_match_is_case_insensitive(self, populated_collection, query):
232+
results = populated_collection.find_by_author(query)
233+
assert len(results) == 1
234+
assert results[0].title == "1984"
235+
236+
def test_uppercase_full_name_matches(self, populated_collection):
237+
results = populated_collection.find_by_author("GEORGE ORWELL")
238+
assert len(results) == 1
239+
240+
# --- Edge cases ---
241+
242+
def test_empty_string_matches_all(self, populated_collection):
243+
"""Empty string is a substring of every author name."""
244+
results = populated_collection.find_by_author("")
245+
assert len(results) == len(populated_collection.books)
246+
247+
def test_single_character_matches_correct_books(self, populated_collection):
248+
results = populated_collection.find_by_author("J")
249+
titles = [b.title for b in results]
250+
assert "The Hobbit" in titles # J.R.R. Tolkien
251+
252+
def test_returns_list_type(self, collection):
253+
assert isinstance(collection.find_by_author("Anyone"), list)
254+
200255

201256
# ---------------------------------------------------------------------------
202257
# TestMarkAsRead

0 commit comments

Comments
 (0)