mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Two tests had been failing on -short for several phases; the
comfortable "pre-existing failures" framing was masking that both
were stale, not flaky — each test predated a deliberate UX rewire
that nobody updated the assertion against.
TestMageSpellbookLineInRender — commit 8bf7d35 (UX S7 jargon sweep)
reshaped the spellbook line to "**Spellbook:** N / M leveled spells
learned" and intentionally dropped the redundant "(can learn N more)"
trailer. Test was still asserting "2/10" (no spaces) and the dropped
hint. Updated to track the live "N / M leveled spells learned"
format; comment cites the sweep commit.
TestAdv2Scenario_ZoneRunGoblinWarrens — commit 886eb5a (turn-based
elite/boss combat) moved Elite/Boss rooms off the auto-resolve
SimulateCombat path onto the manual !fight + !attack engine; !zone
advance now stops at the doorway and only progresses past a won
CombatSession. The scenario test was only calling !advance, so it
spun in place at room 4 (the elite). Loop now detects Elite/Boss
prev-rooms, opens with !fight if the session doesn't exist, drains
!attack rounds until the session terminates, then falls through to
!advance to clear the room (won → walk graph; lost/fled → terminate
next pass). maxSteps doubled to absorb the fight-then-advance pairs;
inner !attack cap at 60 rounds covers RNG tail.
Both -short tests now pass; full -short suite green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
189 lines
5.6 KiB
Go
189 lines
5.6 KiB
Go
package plugin
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
// Phase 9 SP4 — Mage `!spells learn` cap + level-up nudge.
|
|
|
|
// TestMageKnownSpellsCapPerLevel — formula: 6 + 2*(level-1).
|
|
func TestMageKnownSpellsCapPerLevel(t *testing.T) {
|
|
cases := []struct {
|
|
level, cap int
|
|
}{
|
|
{1, 6},
|
|
{2, 8},
|
|
{3, 10},
|
|
{5, 14},
|
|
{9, 22},
|
|
{20, 44},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := mageKnownSpellsCap(tc.level); got != tc.cap {
|
|
t.Errorf("mageKnownSpellsCap(L%d)=%d, want %d", tc.level, got, tc.cap)
|
|
}
|
|
}
|
|
}
|
|
|
|
func setupMageForLearn(t *testing.T, uid id.UserID, level int) *DnDCharacter {
|
|
t.Helper()
|
|
if err := createAdvCharacter(uid, "mage_learn"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c := &DnDCharacter{
|
|
UserID: uid, Race: RaceElf, Class: ClassMage, Level: level,
|
|
STR: 8, DEX: 14, CON: 12, INT: 17, WIS: 12, CHA: 10,
|
|
HPMax: 20, HPCurrent: 20, ArmorClass: 12,
|
|
}
|
|
if err := SaveDnDCharacter(c); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := setSpellSlotsForLevel(uid, ClassMage, level); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
// TestMageLearnCapEnforced — at L1 the cap is 6 leveled spells. Pre-fill 6;
|
|
// the 7th attempt is refused without writing to the spellbook.
|
|
func TestMageLearnCapEnforced(t *testing.T) {
|
|
setupAbilitiesTestDB(t)
|
|
uid := id.UserID("@mage_cap:example")
|
|
c := setupMageForLearn(t, uid, 1)
|
|
|
|
// Six leveled mage spells (all L1-eligible).
|
|
pre := []string{
|
|
"magic_missile", "thunderwave", "mage_armor", "burning_hands",
|
|
"grease", "sleep",
|
|
}
|
|
for _, sid := range pre {
|
|
if err := addKnownSpell(uid, sid, "class", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
// Cantrips: don't count.
|
|
if err := addKnownSpell(uid, "fire_bolt", "class", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
count, _ := mageLeveledKnownCount(uid)
|
|
if count != 6 {
|
|
t.Fatalf("setup: leveled count=%d, want 6", count)
|
|
}
|
|
|
|
p := &AdventurePlugin{}
|
|
// 7th leveled spell — must be refused.
|
|
if err := p.handleSpellsLearn(MessageContext{Sender: uid}, c, "chromatic_orb"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if known, _, _ := playerKnowsSpell(uid, "chromatic_orb"); known {
|
|
t.Errorf("over-cap learn slipped through")
|
|
}
|
|
|
|
// A cantrip should still be learnable beyond the cap.
|
|
if err := p.handleSpellsLearn(MessageContext{Sender: uid}, c, "minor_illusion"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if known, _, _ := playerKnowsSpell(uid, "minor_illusion"); !known {
|
|
t.Errorf("cantrip blocked by leveled-spell cap")
|
|
}
|
|
}
|
|
|
|
// TestMageLearnAcceptsUpToCap — fresh Mage at L1 can learn 6 leveled spells
|
|
// before the cap kicks in.
|
|
func TestMageLearnAcceptsUpToCap(t *testing.T) {
|
|
setupAbilitiesTestDB(t)
|
|
uid := id.UserID("@mage_fill:example")
|
|
c := setupMageForLearn(t, uid, 1)
|
|
|
|
wantList := []string{
|
|
"magic_missile", "thunderwave", "mage_armor", "burning_hands",
|
|
"grease", "sleep",
|
|
}
|
|
p := &AdventurePlugin{}
|
|
for _, sid := range wantList {
|
|
if err := p.handleSpellsLearn(MessageContext{Sender: uid}, c, sid); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if known, _, _ := playerKnowsSpell(uid, sid); !known {
|
|
t.Errorf("under-cap learn rejected: %s", sid)
|
|
}
|
|
}
|
|
if got, _ := mageLeveledKnownCount(uid); got != 6 {
|
|
t.Errorf("after 6 learns, count=%d, want 6", got)
|
|
}
|
|
}
|
|
|
|
// TestMageLevelUpDMNudge — buildLevelUpMessage surfaces the spellbook headroom
|
|
// after a Mage levels up.
|
|
func TestMageLevelUpDMNudge(t *testing.T) {
|
|
setupAbilitiesTestDB(t)
|
|
uid := id.UserID("@mage_nudge:example")
|
|
c := setupMageForLearn(t, uid, 2) // post-level-up state
|
|
|
|
// 5 leveled known + cantrips. At L2 cap = 8 → headroom = 3.
|
|
for _, sid := range []string{
|
|
"magic_missile", "mage_armor", "shield", "burning_hands", "grease",
|
|
} {
|
|
_ = addKnownSpell(uid, sid, "class", true)
|
|
}
|
|
_ = addKnownSpell(uid, "fire_bolt", "class", true)
|
|
|
|
events := []LevelUpEvent{{NewLevel: 2, HPGain: 5}}
|
|
msg := buildLevelUpMessage(c, events, "")
|
|
|
|
if !strings.Contains(msg, "Spells available to learn: **3**") {
|
|
t.Errorf("nudge missing or wrong count; msg=\n%s", msg)
|
|
}
|
|
if !strings.Contains(msg, "!spells learn") {
|
|
t.Errorf("nudge missing command hint; msg=\n%s", msg)
|
|
}
|
|
}
|
|
|
|
// TestNonMageLevelUpNoSpellNudge — Fighter levelling never sees the nudge.
|
|
func TestNonMageLevelUpNoSpellNudge(t *testing.T) {
|
|
c := &DnDCharacter{
|
|
Race: RaceHuman, Class: ClassFighter, Level: 5,
|
|
HPMax: 50, HPCurrent: 50,
|
|
}
|
|
msg := buildLevelUpMessage(c, []LevelUpEvent{{NewLevel: 5, HPGain: 8}}, "")
|
|
if strings.Contains(msg, "Spells available to learn") {
|
|
t.Errorf("Fighter level-up shouldn't show spell nudge; msg=\n%s", msg)
|
|
}
|
|
}
|
|
|
|
// TestMageLevelUpDeltaIsTwo — leveling from L1 → L2 increases the cap by 2.
|
|
func TestMageLevelUpDeltaIsTwo(t *testing.T) {
|
|
for lvl := 1; lvl < 20; lvl++ {
|
|
got := mageKnownSpellsCap(lvl+1) - mageKnownSpellsCap(lvl)
|
|
if got != 2 {
|
|
t.Errorf("delta L%d→L%d = %d, want 2", lvl, lvl+1, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestMageSpellbookLineInRender — renderSpellsList shows the budget for Mage.
|
|
func TestMageSpellbookLineInRender(t *testing.T) {
|
|
setupAbilitiesTestDB(t)
|
|
uid := id.UserID("@mage_render:example")
|
|
c := setupMageForLearn(t, uid, 3)
|
|
for _, sid := range []string{"magic_missile", "mage_armor"} {
|
|
_ = addKnownSpell(uid, sid, "class", true)
|
|
}
|
|
|
|
out := renderSpellsList(c)
|
|
// L3 cap = 10, count = 2. UX S7 jargon sweep (8bf7d35) reshaped
|
|
// the line to "**Spellbook:** N / M leveled spells learned" and
|
|
// dropped the redundant "(can learn N more)" trailer — the
|
|
// "N / M" already conveys remaining capacity. Assertion tracks
|
|
// the live format; if the line ever loses the count or the cap
|
|
// the substrings still pin the regression.
|
|
if !strings.Contains(out, "**Spellbook:**") ||
|
|
!strings.Contains(out, "2 / 10 leveled spells learned") {
|
|
t.Errorf("renderSpellsList missing/wrong Spellbook line; got\n%s", out)
|
|
}
|
|
}
|