MosswartOverlord/go-services/inventory-go/od_test.go
Erik cbc8785c47 feat(inventory-go): recompute OD from VTank loot-profile ladder (0-10 / casters 0-7)
Replaces the UtilityBelt over-retail float with the user's actual in-game OD:
per-weapon-type MaxDamage/elemVsMon thresholds from Loot.utl. Bows/thrown
deferred (null) pending a computed-stat calibration. (L) Dagger MS bucket
folded into (L) Dagger (indistinguishable by captured fields).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 19:09:22 +02:00

78 lines
3 KiB
Go

package main
import "testing"
func TestOD_MeleeTetsubo(t *testing.T) {
// 2H Cleaving baseline 45, name "Tetsubo", maxdmg 85 → clamp(85-45,1,10)=10.
raw := map[string]any{"ObjectClass": 1.0, "Name": "Tetsubo",
"IntValues": map[string]any{"218103840": 41.0, "353": 11.0, "218103842": 85.0}}
if od, ok := computeOD(raw); !ok || od != 10 {
t.Fatalf("Tetsubo OD=%v ok=%v want 10", od, ok)
}
}
func TestOD_MeleeMidTier(t *testing.T) {
// Heavy Axe baseline 74, name "War Axe", maxdmg 79 → clamp(79-74,1,10)=5.
raw := map[string]any{"ObjectClass": 1.0, "Name": "Bronze War Axe",
"IntValues": map[string]any{"218103840": 44.0, "353": 3.0, "218103842": 79.0}}
if od, ok := computeOD(raw); !ok || od != 5 {
t.Fatalf("War Axe OD=%v ok=%v want 5", od, ok)
}
}
func TestOD_MeleeBelowBaselineNull(t *testing.T) {
// Heavy Axe baseline 74, maxdmg 74 → stat-baseline=0 (<1) → null.
raw := map[string]any{"ObjectClass": 1.0, "Name": "Bronze War Axe",
"IntValues": map[string]any{"218103840": 44.0, "353": 3.0, "218103842": 74.0}}
if _, ok := computeOD(raw); ok {
t.Fatal("maxdmg == baseline must be null")
}
}
func TestOD_MeleeNameGateNull(t *testing.T) {
// Heavy Axe bucket requires name in {Silifi,Lugian,War Axe,Battle}. A skill-44
// mastery-3 item NOT matching any name and matching no other bucket → null.
raw := map[string]any{"ObjectClass": 1.0, "Name": "Bronze Frobnicator",
"IntValues": map[string]any{"218103840": 44.0, "353": 3.0, "218103842": 84.0}}
if _, ok := computeOD(raw); ok {
t.Fatal("name not matching the Heavy Axe filter must be null")
}
}
func TestOD_Crossbow(t *testing.T) {
// Xbow baseline 77, k839=89 → clamp(89-77,1,10)=10.
raw := map[string]any{"ObjectClass": 9.0, "Name": "Fire Compound Crossbow",
"IntValues": map[string]any{"218103840": 47.0, "353": 9.0, "218103839": 89.0}}
if od, ok := computeOD(raw); !ok || od != 10 {
t.Fatalf("Xbow OD=%v ok=%v want 10", od, ok)
}
}
func TestOD_WarCaster(t *testing.T) {
// War caster: elemVsMon 1.40 → clamp(round((1.40-1.18)*100),1,7)=clamp(22,1,7)=7.
raw := map[string]any{"ObjectClass": 31.0, "Name": "Diamond Frost Baton",
"IntValues": map[string]any{"159": 34.0},
"DoubleValues": map[string]any{"152": 1.40}}
if od, ok := computeOD(raw); !ok || od != 7 {
t.Fatalf("War caster OD=%v ok=%v want 7", od, ok)
}
}
func TestOD_VoidCasterTier(t *testing.T) {
// Void caster: name must contain "Nether"; elemVsMon 1.22 → round((1.22-1.18)*100)=4.
raw := map[string]any{"ObjectClass": 31.0, "Name": "Silver Nether Staff",
"IntValues": map[string]any{"159": 43.0},
"DoubleValues": map[string]any{"152": 1.22}}
if od, ok := computeOD(raw); !ok || od != 4 {
t.Fatalf("Void caster OD=%v ok=%v want 4", od, ok)
}
}
func TestOD_BowDeferredNull(t *testing.T) {
// Bows (mastery 8) are deferred → always null in this pass.
raw := map[string]any{"ObjectClass": 9.0, "Name": "Corsair's Arc",
"IntValues": map[string]any{"218103840": 47.0, "353": 8.0, "218103839": 70.0}}
if _, ok := computeOD(raw); ok {
t.Fatal("bows are deferred → null")
}
}