fix(models): require a positive range_increment_ft (reject 0 and negatives)

This commit is contained in:
2026-09-20 17:36:45 +02:00
parent e9c2a63122
commit 10f811e80e
3 changed files with 22 additions and 2 deletions
+2 -1
View File
@@ -285,7 +285,8 @@ passe par les conditions (`miss_chance`), pas par la carte.
```
Champ `range_increment_ft` (optionnel, uniquement pour `kind: ranged` ou
`kind: touch`) : incrément de portée en pieds. La pénalité de portée vaut −2
`kind: touch`) : incrément de portée en pieds, entier **strictement positif**
(zéro ou négatif = erreur de données, rejetée au chargement). La pénalité de portée vaut −2
par incrément complet au-delà du premier ; une arme `ranged` reste utilisable
jusqu'à 10 incréments, une arme `touch` jusqu'à 5 (jet/firearm), au-delà elle
n'est pas sélectionnable. `damage_bonus` et `dr` sont optionnels.
+5 -1
View File
@@ -278,7 +278,11 @@ class AttackSpec(_FrozenModel):
crit_mult: int = Field(default=2, ge=2, le=4)
reach_ft: int = 5
reach_weapon: bool = False
range_increment_ft: int | None = None
# Positive or None (None = no range increments: melee, or touch at natural
# reach). A zero increment would crash the engine's range_penalty with a
# ZeroDivisionError (dist_ft // 0); a negative one silently makes the
# weapon unusable -- both are data errors, rejected at the schema.
range_increment_ft: int | None = Field(default=None, ge=1)
count: int = Field(default=1, ge=1)
firearm: bool = False
two_handed: bool = False
+15
View File
@@ -116,6 +116,21 @@ class TestAttackSpec:
with pytest.raises(ValidationError):
AttackSpec.model_validate(_attack_dict(damage=[]))
@pytest.mark.parametrize("increment", [0, -30])
def test_rejects_non_positive_range_increment(self, increment: int) -> None:
# Given a range_increment_ft of 0 (ZeroDivisionError in range_penalty)
# or negative (silently unusable weapon)
# When/Then validation rejects it
with pytest.raises(ValidationError):
AttackSpec.model_validate(_attack_dict(kind="ranged", range_increment_ft=increment))
def test_accepts_positive_range_increment(self) -> None:
# Given a legal range increment
# When validated
attack = AttackSpec.model_validate(_attack_dict(kind="ranged", range_increment_ft=30))
# Then it is kept
assert attack.range_increment_ft == 30
def test_rejects_crit_mult_below_2(self) -> None:
# Given crit_mult=1 (a weapon that never multiplies is not PF1e)
# When/Then validation rejects it