Files
gogobee/internal/plugin/dnd_spells_prose_test.go
prosolis f424300d87 Ship-audit P0/P1 sweep on open5e: prose, combat math, UX hygiene
P0 — actual bugs:
- eldritch_blast: SRD-imported "Whatever" placeholder leaked to players;
  overlay added in dnd_spells_data.go.
- Battle Master disarming_attack + parry: math.Max on multiplicative
  DamageReduct was a no-op vs the 1.0 default. Switched to *= and added
  regression tests covering the stacking case too.
- Extended TestSpellDescriptionsAreJargonFree to catch "Whatever",
  trailing ellipses, mid-word truncation, and empty-size phrases. This
  surfaced 4 more genuinely-broken SRD descriptions (find_familiar,
  clairvoyance, glyph_of_warding, teleportation_circle); all now
  overlayed in dnd_spells_data.go.

P1 — UX hygiene:
- Setup completion now nudges new casters toward !spells/!cast.
- Magic-item attunement vocab unified to "bond" everywhere user-facing;
  renamed the Treasures sheet section to avoid clashing with the bond
  vocab.
- Ambient DM footers stripped of "Threat +N" / "Supplies −N SU" hidden-
  meter leakage; verb-form footers instead.
- !expedition status defaults to a days-left + threat-label summary;
  raw SU / threat-out-of-100 / zone-stack / roll-modifier moved behind
  `!expedition status --debug`.
- !zone taunt/compliment help text dropped the "(mood −10)" / "(mood +5)"
  numerics that turned the hidden TwinBee-mood mechanic into a min/max
  knob.
- TwinBee pronoun pass: switched she/her to singular they on the two
  surfaces I touched plus achievements.bj_beat_twinbee.

P1 — correctness/scale:
- adventure_shop curio purchase now guards the euro.Debit bool return
  before inventory grant + pot cut; the GetBalance preflight isn't
  enough since a concurrent blackjack/lottery debit can slip the balance
  across BLACKJACK_DEBT_LIMIT between read and write.
- equipMagicItem reordered: remove inventory item BEFORE equip, with a
  best-effort rollback if equip fails. The prior order left a dup
  window if the inventory delete tripped on a transient DB error.
- magicItemsByRarityCache promoted to sync.Once — concurrent cold-start
  loot rolls otherwise raced on the lazy assignment.

Stale-audit verifications (no code change needed):
- light / druidcraft / water_walk overlays already exist and win the
  SRD merge — the broken raw text never reaches players.
- bootstrapPhase5BHPRefresh is already wired at adventure.go:228.

Deferred:
- combatantsForSession per-turn 6-load DB chatter (perf P1-A): touches
  the hot combat path; needs its own test pass before shipping. Tracked
  in memory at project_combat_session_cache_deferred.

go vet clean; go test -race ./... green (229s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:43:11 -07:00

58 lines
2.4 KiB
Go

package plugin
import (
"regexp"
"testing"
)
// TestSpellDescriptionsAreJargonFree scans every reachable spell Description
// in the merged registry (SRD + hand-authored overlay) for D&D jargon that
// leaks into player-facing chat via dnd_cast.go's !cast confirmation and the
// !spellbook list view. Per feedback_accessibility_over_dnd_crunch, the
// player surface should be verb/outcome-led — no math, no dice, no saves.
//
// Failures usually mean either (a) a new SRD entry was added without an
// overlay shim in dnd_spells_data.go, or (b) an overlay Description was
// edited back into crunch. Fix in dnd_spells_data.go; do NOT touch the
// generated dnd_spells_srd_data.go directly (it's regenerated from Open5e).
//
// Upcast strings are NOT tested — they're never displayed (grep
// `\.Upcast` across the codebase to confirm; only the import generator
// reads them as of 2026-05-15).
func TestSpellDescriptionsAreJargonFree(t *testing.T) {
bans := []struct {
name string
re *regexp.Regexp
}{
{"saving throw", regexp.MustCompile(`(?i)saving throw`)},
{"spell slot of", regexp.MustCompile(`(?i)spell slot of`)},
{"ability modifier", regexp.MustCompile(`(?i)ability modifier`)},
{"hit points equal to", regexp.MustCompile(`(?i)hit points equal to`)},
{"NdN dice notation", regexp.MustCompile(`\b\d+d\d+\b`)},
{"bare die notation", regexp.MustCompile(`\bd(4|6|8|10|12|20|100)\b`)},
// Catch SRD-import placeholders that have leaked through past
// overlays. "Whatever" was the eldritch_blast tell; the truncation
// patterns caught water_walk/druidcraft/light in earlier audits.
// "Whatever" placeholder used by the SRD importer when a field
// went missing. Allow mid-prose "Whatever's"/"Whatever it"
// (legit contraction / sentence-start) by requiring a following
// space-then-lowercase, which is the signature of the bug.
{"Whatever placeholder", regexp.MustCompile(`\bWhatever [a-z]`)},
{"trailing ellipsis", regexp.MustCompile(`(?:\.\.\.|…)\s*$`)},
{"trailing truncated word", regexp.MustCompile(`\b[a-z]{1,3}(?:\.\.\.|…)\s*$`)},
{"empty size phrase", regexp.MustCompile(`(?i)\bno larger than in any\b`)},
}
for id, s := range dndSpellRegistry {
if s.Description == "" {
continue
}
for _, b := range bans {
if b.re.MatchString(s.Description) {
t.Errorf("spell %q Description leaks %q jargon: %q",
id, b.name, s.Description)
}
}
}
}