Files
gogobee/internal/plugin/dnd_spells_prose_test.go
prosolis 20647eeafc Phase B1 follow-up: regex catches bare-die notation; blink overlay shim
The A1 regex banned \b\d+d\d+\b (e.g. '1d20') but missed bare die
notation ('d20', 'd6') with no leading count. SRD's 'blink' Description
slipped through with 'Roll a d20 at the end of each of your turns...'.

- Add \bd(4|6|8|10|12|20|100)\b ban to the prose regex sweep.
- Add hand-authored overlay shim for blink in dnd_spells_data.go.
  Classes union with SRD preserves Cleric/Sorcerer/Warlock availability.

Test now passes the wider sweep; no other reachable spells leak.
2026-05-15 07:18:16 -07:00

47 lines
1.6 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`)},
}
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)
}
}
}
}