mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Combines all uncommitted D&D work into one checkpoint: Phases 1-8 (per gogobee_dnd_session_summary.md): - Race/class/stats system, !setup flow, !sheet, !respec - d20-vs-AC combat with race/class passives, auto-migration - XP/level-up, skill checks, NPC refund hooks - Equipment slot mapping, rarity, !rest short/long - Pre-arm active abilities, resource pools - Bestiary, !roll/!stats/!level, onboarding DM - Audit fixes A-I, flavor wiring under internal/flavor/ - Phase 8 weapon dice + armor AC tables (gogobee_equipment_appendix) Phase 9 SP1 — spell system foundations: - 79 SpellDefinitions (76 in-scope + 3 reaction-deferred) - dnd_known_spells, dnd_spell_slots tables - pending_cast / concentration_* columns on dnd_character - Slot tables for Mage/Cleric/Ranger, DC + attack-bonus math - Long-rest refreshes slots; respec wipes spell state - Auto-grant default known list on !setup confirm and auto-migration Phase 9 SP2 — !cast / !spells / !prepare commands: - Out-of-combat HEAL and UTILITY resolve immediately - Damage/control/buff queue as pending_cast for next combat - Audit-style save-then-debit, slot refund on save failure - !cast --drop, concentration supersession, prep-cap enforcement - Reaction spells refused with Phase 11 note SP3 (combat-time resolution of pending_cast) and SP4 (full prep/learn tests) are next. Build clean, full test suite green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
package plugin
|
|
|
|
import "testing"
|
|
|
|
func TestMapLegacySlot(t *testing.T) {
|
|
cases := []struct {
|
|
legacy EquipmentSlot
|
|
want DnDSlot
|
|
}{
|
|
{SlotWeapon, DnDSlotMainHand},
|
|
{SlotArmor, DnDSlotChest},
|
|
{SlotHelmet, DnDSlotHead},
|
|
{SlotBoots, DnDSlotFeet},
|
|
{SlotTool, DnDSlotOffHand},
|
|
}
|
|
for _, c := range cases {
|
|
if got := mapLegacySlot(c.legacy); got != c.want {
|
|
t.Errorf("mapLegacySlot(%s) = %s, want %s", c.legacy, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInferRarity(t *testing.T) {
|
|
cases := []struct {
|
|
tier int
|
|
masterwork bool
|
|
arenaTier int
|
|
want DnDRarity
|
|
}{
|
|
{1, false, 0, RarityCommon},
|
|
{2, false, 0, RarityCommon},
|
|
{3, false, 0, RarityUncommon},
|
|
{4, false, 0, RarityUncommon},
|
|
{5, false, 0, RarityRare},
|
|
{6, false, 0, RarityRare},
|
|
{7, false, 0, RarityEpic},
|
|
{9, false, 0, RarityLegendary},
|
|
// Masterwork bumps to Rare/Epic
|
|
{2, true, 0, RarityRare},
|
|
{6, true, 0, RarityEpic},
|
|
// Arena set
|
|
{3, false, 1, RarityUncommon},
|
|
{3, false, 3, RarityRare},
|
|
{3, false, 4, RarityEpic},
|
|
{3, false, 5, RarityLegendary},
|
|
}
|
|
for _, c := range cases {
|
|
got := inferRarity(c.tier, c.masterwork, c.arenaTier)
|
|
if got != c.want {
|
|
t.Errorf("inferRarity(t=%d mw=%v arena=%d) = %s, want %s",
|
|
c.tier, c.masterwork, c.arenaTier, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRarityIcon(t *testing.T) {
|
|
for _, r := range []DnDRarity{RarityCommon, RarityUncommon, RarityRare, RarityEpic, RarityLegendary} {
|
|
if rarityIcon(r) == "" {
|
|
t.Errorf("rarityIcon(%s) returned empty string", r)
|
|
}
|
|
}
|
|
if rarityIcon("Bogus") != "" {
|
|
t.Error("rarityIcon for unknown rarity should be empty")
|
|
}
|
|
}
|
|
|
|
func TestEquipmentRarity_NilSafety(t *testing.T) {
|
|
if got := equipmentRarity(nil); got != RarityCommon {
|
|
t.Errorf("equipmentRarity(nil) = %s, want Common", got)
|
|
}
|
|
}
|
|
|
|
func TestDnDSlotOrderComplete(t *testing.T) {
|
|
// Every D&D slot is in the order list exactly once.
|
|
seen := map[DnDSlot]bool{}
|
|
for _, s := range dndSlotOrder {
|
|
if seen[s] {
|
|
t.Errorf("dndSlotOrder has duplicate %s", s)
|
|
}
|
|
seen[s] = true
|
|
}
|
|
expected := []DnDSlot{
|
|
DnDSlotHead, DnDSlotChest, DnDSlotLegs, DnDSlotHands, DnDSlotFeet,
|
|
DnDSlotMainHand, DnDSlotOffHand,
|
|
DnDSlotRing1, DnDSlotRing2, DnDSlotAmulet,
|
|
}
|
|
if len(seen) != len(expected) {
|
|
t.Errorf("dndSlotOrder size = %d, want %d", len(seen), len(expected))
|
|
}
|
|
}
|