Files
bullpen/tests/test_slugify.py
Markus Fritsche 7aae3dd996 add slugify spec (18 tests) — shakedown v2 contract
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 09:48:04 +02:00

120 lines
3.5 KiB
Python

"""Executable spec for slugify(s) — to live at src/slugify.py.
Contract (this suite IS the contract):
1. Lowercase the input (via str.lower semantics — unicode letters lowercase too).
2. "Alphanumeric" is defined per Python's str.isalnum: unicode letters and
digits are KEPT; everything else (space, punctuation, underscore, emoji,
control chars) is a separator.
3. Every run of one-or-more non-alphanumeric characters collapses to a
SINGLE hyphen.
4. Leading/trailing hyphens are stripped. All-separator input -> ''.
Tests load src/slugify.py by file path (SourceFileLoader), so they fail red
while the module doesn't exist yet and need no installed package.
"""
import importlib.machinery
import importlib.util
from pathlib import Path
import pytest
REPO = Path(__file__).resolve().parents[1]
MODULE_PATH = REPO / "src" / "slugify.py"
@pytest.fixture(scope="module")
def slugify():
if not MODULE_PATH.is_file():
pytest.fail(f"expected module at {MODULE_PATH} — not created yet")
loader = importlib.machinery.SourceFileLoader("slugify_under_test", str(MODULE_PATH))
spec = importlib.util.spec_from_loader(loader.name, loader)
mod = importlib.util.module_from_spec(spec)
loader.exec_module(mod)
assert hasattr(mod, "slugify"), "src/slugify.py must define slugify(s)"
return mod.slugify
# --- the foreman's worked examples ------------------------------------------
def test_basic_sentence(slugify):
assert slugify("Hello, World!") == "hello-world"
def test_already_slugged_with_padding(slugify):
assert slugify(" --Already--Slugged-- ") == "already-slugged"
def test_alnum_passthrough_lowercased(slugify):
assert slugify("ABC123") == "abc123"
def test_all_punctuation_is_empty(slugify):
assert slugify("!!!") == ""
# --- boundaries --------------------------------------------------------------
def test_empty_string(slugify):
assert slugify("") == ""
def test_single_separator_char(slugify):
assert slugify("-") == ""
def test_single_letter(slugify):
assert slugify("a") == "a"
def test_digits_only_keep_leading_zeros(slugify):
assert slugify("007") == "007"
def test_whitespace_only(slugify):
assert slugify(" \t\n ") == ""
# --- run collapsing & stripping ----------------------------------------------
def test_internal_hyphen_run_collapses(slugify):
assert slugify("foo--bar") == "foo-bar"
def test_mixed_separator_run_is_one_hyphen(slugify):
assert slugify("foo -_.\t\n- bar") == "foo-bar"
def test_every_gap_gets_exactly_one_hyphen(slugify):
assert slugify("A!B?C") == "a-b-c"
def test_strip_after_collapse(slugify):
assert slugify("--a--b--") == "a-b"
# --- the cases a cheap model gets wrong --------------------------------------
def test_underscore_is_a_separator_not_word_char(slugify):
# regex \w keeps '_'; str.isalnum does not. '_' must become a hyphen.
assert slugify("snake_case_name") == "snake-case-name"
def test_unicode_letters_are_kept_and_lowercased(slugify):
# 'Ü', 'é' are alnum per str.isalnum -> kept, lowercased.
assert slugify("Über Café") == "über-café"
def test_unicode_digits_are_kept(slugify):
# Fullwidth digits: '123'.isalnum() is True.
assert slugify("x123y") == "x123y"
def test_emoji_is_a_separator(slugify):
assert slugify("héllo🌍wörld") == "héllo-wörld"
def test_idempotent_on_own_output(slugify):
out = slugify(" Some -- Messy__Input!! 42 ")
assert out == "some-messy-input-42"
assert slugify(out) == out