- Fix CSRF token handling for production: generate tokens with HMAC, set cookie via afterware, inject into HTMX requests via JS - Improve registry page: filter at top with better proportions, compact horizontal pill layout for facets - Add phonetic ID encoding (e.g., "tobi-kafu-meli") for animal display instead of truncated ULIDs - Remove "subadult" life stage (migration converts to juvenile) - Change "Death (natural)" outcome label to just "Death" - Show sex/life stage in animal picker alongside species/location 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
# ABOUTME: Tests for ULID generation and phonetic encoding utilities.
|
|
# ABOUTME: Verifies ULID format, uniqueness, and phonetic display encoding.
|
|
|
|
from animaltrack.id_gen import format_animal_id, generate_id, ulid_to_phonetic
|
|
|
|
|
|
class TestGenerateId:
|
|
"""Tests for the generate_id function."""
|
|
|
|
def test_returns_string(self):
|
|
"""generate_id returns a string."""
|
|
result = generate_id()
|
|
assert isinstance(result, str)
|
|
|
|
def test_returns_26_characters(self):
|
|
"""generate_id returns exactly 26 characters (ULID format)."""
|
|
result = generate_id()
|
|
assert len(result) == 26
|
|
|
|
def test_returns_unique_ids(self):
|
|
"""generate_id returns unique IDs on each call."""
|
|
ids = [generate_id() for _ in range(100)]
|
|
assert len(set(ids)) == 100
|
|
|
|
def test_ulid_is_uppercase_alphanumeric(self):
|
|
"""ULID contains only uppercase letters and digits (Crockford base32)."""
|
|
result = generate_id()
|
|
# Crockford base32 excludes I, L, O, U
|
|
valid_chars = set("0123456789ABCDEFGHJKMNPQRSTVWXYZ")
|
|
assert all(c in valid_chars for c in result)
|
|
|
|
|
|
class TestUlidToPhonetic:
|
|
"""Tests for the ulid_to_phonetic function."""
|
|
|
|
def test_returns_hyphenated_syllables(self):
|
|
"""Result is hyphen-separated syllables."""
|
|
ulid = generate_id()
|
|
result = ulid_to_phonetic(ulid)
|
|
parts = result.split("-")
|
|
assert len(parts) == 4 # Default syllable count
|
|
|
|
def test_syllables_are_cv_format(self):
|
|
"""Each syllable is consonant-vowel format."""
|
|
ulid = generate_id()
|
|
result = ulid_to_phonetic(ulid)
|
|
consonants = set("bdfgklmnprstvwxz")
|
|
vowels = set("aeiou")
|
|
for syllable in result.split("-"):
|
|
assert len(syllable) == 2
|
|
assert syllable[0] in consonants
|
|
assert syllable[1] in vowels
|
|
|
|
def test_same_ulid_produces_same_phonetic(self):
|
|
"""Same ULID always produces the same phonetic."""
|
|
ulid = "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
result1 = ulid_to_phonetic(ulid)
|
|
result2 = ulid_to_phonetic(ulid)
|
|
assert result1 == result2
|
|
|
|
def test_different_ulids_produce_different_phonetics(self):
|
|
"""Different ULIDs produce different phonetics (with high probability)."""
|
|
ulids = [generate_id() for _ in range(100)]
|
|
phonetics = [ulid_to_phonetic(u) for u in ulids]
|
|
# All should be unique
|
|
assert len(set(phonetics)) == 100
|
|
|
|
def test_custom_syllable_count(self):
|
|
"""Can specify custom number of syllables."""
|
|
ulid = generate_id()
|
|
result = ulid_to_phonetic(ulid, syllable_count=3)
|
|
assert len(result.split("-")) == 3
|
|
|
|
result = ulid_to_phonetic(ulid, syllable_count=5)
|
|
assert len(result.split("-")) == 5
|
|
|
|
|
|
class TestFormatAnimalId:
|
|
"""Tests for the format_animal_id function."""
|
|
|
|
def test_returns_nickname_when_provided(self):
|
|
"""Returns nickname if provided."""
|
|
ulid = generate_id()
|
|
result = format_animal_id(ulid, nickname="Daisy")
|
|
assert result == "Daisy"
|
|
|
|
def test_returns_phonetic_when_no_nickname(self):
|
|
"""Returns phonetic encoding when nickname is None."""
|
|
ulid = generate_id()
|
|
result = format_animal_id(ulid, nickname=None)
|
|
# Should be phonetic format (4 syllables, hyphen-separated)
|
|
parts = result.split("-")
|
|
assert len(parts) == 4
|
|
|
|
def test_returns_phonetic_when_nickname_empty(self):
|
|
"""Returns phonetic encoding when nickname is empty string."""
|
|
ulid = generate_id()
|
|
# Empty string is falsy, should use phonetic
|
|
result = format_animal_id(ulid, nickname="")
|
|
parts = result.split("-")
|
|
assert len(parts) == 4
|