mud/tests/test_pov.py
Jared Miller 2a546a3171
Fix operator precedence in POV smart conjugation
The {s} conjugation check had incorrect operator precedence that
evaluated the ch/sh suffix check independently of the prev_text
existence check. This could lead to confusing logic flow even
though it didn't crash due to len() handling empty strings safely.

Fixed by wrapping both suffix conditions in parentheses so they're
both guarded by the prev_text truthiness check.
2026-02-14 01:00:37 -05:00

222 lines
8.2 KiB
Python

"""Tests for POV template engine."""
from dataclasses import dataclass
import pytest
from mudlib.render.pov import render_pov
@dataclass
class MockEntity:
"""Simple entity for testing."""
name: str
@pytest.fixture
def jared():
"""Attacker entity."""
return MockEntity(name="Jared")
@pytest.fixture
def goku():
"""Defender entity."""
return MockEntity(name="Goku")
@pytest.fixture
def vegeta():
"""Bystander entity."""
return MockEntity(name="Vegeta")
def test_attacker_pov_basic(jared, goku):
"""Attacker sees 'You' for self, defender name for target."""
template = "{attacker} hit{s} {defender}"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You hit Goku"
def test_defender_pov_basic(jared, goku):
"""Defender sees attacker name, 'You' for self."""
template = "{attacker} hit{s} {defender}"
result = render_pov(template, viewer=goku, attacker=jared, defender=goku)
assert result == "Jared hits You"
def test_bystander_pov_basic(jared, goku, vegeta):
"""Bystander sees both entity names."""
template = "{attacker} hits {defender}"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared hits Goku"
def test_verb_s_conjugation_you(jared, goku):
"""Verb with {s} after You gets no suffix."""
template = "{attacker} punch{s} {defender}"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You punch Goku"
def test_verb_s_conjugation_third_person(jared, goku, vegeta):
"""Verb with {s} after name gets 's' suffix."""
template = "{attacker} punch{s} {defender}"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared punches Goku"
def test_verb_es_conjugation_you(jared, goku):
"""Verb with {es} after You gets no suffix."""
template = "{attacker} lurch{es} forward"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You lurch forward"
def test_verb_es_conjugation_third_person(jared, goku, vegeta):
"""Verb with {es} after name gets 'es' suffix."""
template = "{attacker} lurch{es} forward"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared lurches forward"
def test_irregular_verb_y_ies_you(jared, goku):
"""Irregular verb {y|ies} after You uses left form."""
template = "{attacker} parr{y|ies} the blow"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You parry the blow"
def test_irregular_verb_y_ies_third_person(jared, goku, vegeta):
"""Irregular verb {y|ies} after name uses right form."""
template = "{attacker} parr{y|ies} the blow"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared parries the blow"
def test_pronoun_his_you(jared, goku):
"""{his} after You becomes 'your'."""
template = "{attacker} raise{s} {his} fist"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You raise your fist"
def test_pronoun_his_third_person(jared, goku, vegeta):
"""{his} after name becomes 'his'."""
template = "{attacker} raise{s} {his} fist"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared raises his fist"
def test_pronoun_him_you(jared, goku):
"""{him} after You becomes 'you'."""
template = "{attacker} brace{s} {him}self"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You brace yourself"
def test_pronoun_him_third_person(jared, goku, vegeta):
"""{him} after name becomes 'him'."""
template = "{attacker} brace{s} {him}self"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared braces himself"
def test_mixed_template_attacker_pov(jared, goku):
"""Complex template with multiple substitutions, attacker POV."""
template = "{attacker} slam{s} {his} fist into {defender}"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You slam your fist into Goku"
def test_mixed_template_defender_pov(jared, goku):
"""Complex template with multiple substitutions, defender POV."""
template = "{attacker} slam{s} {his} fist into {defender}"
result = render_pov(template, viewer=goku, attacker=jared, defender=goku)
assert result == "Jared slams his fist into You"
def test_mixed_template_bystander_pov(jared, goku, vegeta):
"""Complex template with multiple substitutions, bystander POV."""
template = "{attacker} slam{s} {his} fist into {defender}"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared slams his fist into Goku"
def test_defender_contextual_tags(jared, goku):
"""Contextual tags apply to defender when they precede them."""
template = "{defender} brace{s} {him}self against {attacker}"
result = render_pov(template, viewer=goku, attacker=jared, defender=goku)
assert result == "You brace yourself against Jared"
def test_defender_contextual_third_person(jared, goku, vegeta):
"""Contextual tags apply to defender in third person."""
template = "{defender} brace{s} {him}self against {attacker}"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Goku braces himself against Jared"
def test_plain_text_passthrough():
"""Template with no POV tags passes through unchanged."""
template = "The battle rages on!"
result = render_pov(template, viewer=None, attacker=None, defender=None)
assert result == "The battle rages on!"
def test_you_capitalization_mid_sentence(jared, goku):
"""'You' is capitalized even when not at start of sentence."""
template = "The blow strikes {defender} hard"
result = render_pov(template, viewer=goku, attacker=jared, defender=goku)
assert result == "The blow strikes You hard"
def test_multiple_entity_references(jared, goku):
"""Context switches between entities correctly."""
template = "{attacker} throw{s} a punch at {defender}, but {defender} dodge{s} it"
result = render_pov(template, viewer=goku, attacker=jared, defender=goku)
assert result == "Jared throws a punch at You, but You dodge it"
def test_multiple_entity_references_attacker_pov(jared, goku):
"""Context switches correctly from attacker POV."""
template = "{attacker} throw{s} a punch at {defender}, but {defender} dodg{es} it"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You throw a punch at Goku, but Goku dodges it"
def test_you_capitalized_after_comma(jared, goku):
"""'You' stays capitalized after comma/conjunction."""
template = "{attacker} strikes, and {defender} fall{s}"
result = render_pov(template, viewer=goku, attacker=jared, defender=goku)
assert result == "Jared strikes, and You fall"
def test_s_conjugation_after_ch(jared, goku, vegeta):
"""{s} after 'ch' should add 'es' for third person."""
template = "{attacker} punch{s} {defender}"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared punches Goku"
def test_s_conjugation_after_sh(jared, goku, vegeta):
"""{s} after 'sh' should add 'es' for third person."""
template = "{attacker} smash{s} into {defender}"
result = render_pov(template, viewer=vegeta, attacker=jared, defender=goku)
assert result == "Jared smashes into Goku"
def test_s_conjugation_you_after_ch(jared, goku):
"""{s} should not add suffix for 'You' regardless of word ending."""
template = "{attacker} punch{s} {defender}"
result = render_pov(template, viewer=jared, attacker=jared, defender=goku)
assert result == "You punch Goku"
def test_s_conjugation_at_template_start():
"""{s} at start of template should not crash when checking prev_text."""
template = "{s}test"
result = render_pov(template, viewer=None, attacker=None, defender=None)
# Without entity, last_was_you defaults to False, so should add 's'
assert result == "stest"