diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..2ec7136 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,109 @@ +"""Unit tests for the helpers in ``depicts.utils``. + +These tests cover common paths and a few edge cases to ensure +stable behavior across refactors. +""" + +from depicts import utils +from flask import Flask +import pytest + + +def test_ordinal() -> None: + """Convert integers to the expected English ordinals.""" + assert utils.ordinal(1) == "1st" + assert utils.ordinal(2) == "2nd" + assert utils.ordinal(3) == "3rd" + assert utils.ordinal(4) == "4th" + + +def test_chunk_basic() -> None: + """Chunk an iterable into fixed-size tuples with a remainder.""" + data = list(range(10)) + chunks = list(utils.chunk(data, 3)) + assert chunks == [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)] + + +def test_drop_start_success_and_failure() -> None: + """Drop a matching prefix and assert on missing prefix.""" + assert utils.drop_start("Category:Painting", "Category:") == "Painting" + with pytest.raises(AssertionError): + utils.drop_start("Painting", "Category:") + + +def test_drop_category_ns() -> None: + """Remove the "Category:" namespace from a string.""" + assert utils.drop_category_ns("Category:Portraits") == "Portraits" + + +def test_parse_sitelink() -> None: + """Decode sitelink by removing prefix, unquoting, and de-underscoring.""" + start = "https://en.wikipedia.org/wiki/" + s = "https://en.wikipedia.org/wiki/Hello_World%21" + assert utils.parse_sitelink(s, start) == "Hello World!" + + +def test_word_contains_letter() -> None: + """Detect whether a token contains at least one letter.""" + assert utils.word_contains_letter("abc123") is True + assert utils.word_contains_letter("12345") is False + assert utils.word_contains_letter("!!!") is False + + +def test_also_singular_main_plural_and_singular() -> None: + """Return both plural and singular when appropriate.""" + # plural should include both plural and singular + assert set(utils.also_singular_main("Dogs")) == {"Dogs", "Dog"} + # singular should return as-is + assert utils.also_singular_main("Dog") == ["Dog"] + + +def test_also_singular_gender_and_skip_names() -> None: + """Add gender-derived forms and honor the skip list.""" + # Adds gender-derived singulars + names = utils.also_singular("Women") + assert "Women" in names + assert "woman" in names + # Skips configured names + assert utils.also_singular("National Gallery") == [] + + +def test_wiki_url_capitalization_and_ns() -> None: + """Build proper MediaWiki URLs with capitalization and namespace.""" + # lowercase first letter should be capitalized, spaces become underscores + url = utils.wiki_url("example page", site="enwiki") + expected = "https://en.wikipedia.org/wiki/Example_page" + assert url == expected + # namespace prefix when provided + url_cat = utils.wiki_url("Portraits", site="commons", ns="Category") + expected_cat = "https://commons.wikimedia.org/wiki/Category:Portraits" + assert url_cat == expected_cat + + +def test_get_int_arg_present_and_missing() -> None: + """Parse integer query args and ignore missing/non-integer values.""" + app = Flask(__name__) + with app.test_request_context("/?page=10&foo=bar"): + assert utils.get_int_arg("page") == 10 + assert utils.get_int_arg("foo") is None + assert utils.get_int_arg("missing") is None + + +def test_format_time_precisions() -> None: + """Format time strings for multiple precision levels.""" + # Full date + assert utils.format_time("+1965-04-23T00:00:00Z", 11) == "23 April 1965" + # Month precision + assert utils.format_time("+1965-04-00T00:00:00Z", 10) == "April 1965" + # Year precision + assert utils.format_time("+1965-00-00T00:00:00Z", 9) == "1965" + # Decade precision + assert utils.format_time("+1960-00-00T00:00:00Z", 8) == "1960s" + # Century precision + assert utils.format_time("+1965-00-00T00:00:00Z", 7) == "20th century" + # Millennium precision + assert utils.format_time("+2001-00-00T00:00:00Z", 6) == "3rd millennium" + # Unparseable falls back to input + assert utils.format_time("not-a-date", 9) == "not-a-date" + # BC year with unknown month/day should still return year for precision 9 + assert utils.format_time("-0123-00-00T00:00:00Z", 9) == "-123"