Files
panel/pkg/sandbox/sandbox_test.go
T
2026-07-14 19:19:43 -07:00

155 lines
4.5 KiB
Go

package sandbox
import (
"testing"
)
// TestInitConsistency: every option resolves an enum + a default index, and no
// two options share an enum (the 2-char code must be unique per option).
func TestInitConsistency(t *testing.T) {
if len(options) != 150 {
t.Fatalf("expected 150 sandbox options, got %d", len(options))
}
seen := map[int]string{}
for i := range options {
o := &options[i]
if o.enum == 0 && o.Name != "RangedDamage" {
t.Errorf("%s: enum unresolved (0)", o.Name)
}
if prev, ok := seen[o.enum]; ok {
t.Errorf("enum %d shared by %s and %s", o.enum, prev, o.Name)
}
seen[o.enum] = o.Name
if _, ok := defaultIdx[o.Name]; !ok {
t.Errorf("%s: no default index", o.Name)
}
}
}
// TestGameDefaultCode decodes the game's own default difficulty (Adventurer)
// code from serverconfig.xml and checks the documented meaning, then re-encodes
// and confirms it round-trips byte-for-byte.
func TestGameDefaultCode(t *testing.T) {
const code = "AAAJABJACJADJARFBNC" // shipped in 3.0 serverconfig.xml comment
c := V3Codec{}
vals, err := c.Decode(code)
if err != nil {
t.Fatalf("decode: %v", err)
}
// Triples: AA J / AB J / AC J / AD J / AR F / BN C
// AA(0)=RangedDamage idx J=9 -> DamageValues[9]=1.5
// AB(1)=MeleeDamage idx 9 -> 1.5
// AC(2)=BlockDamage idx 9 -> 1.5
// AD(3)=TerrainDamage idx 9 -> 1.5
// AR(17)=IncomingDamage idx F=5 -> DamageValues[5]=0.75
// BN(39)=AISmellMode idx C=2 -> AISmellMode set Ints[2]=2
checks := map[string]string{
"RangedDamage": "1.5",
"MeleeDamage": "1.5",
"BlockDamage": "1.5",
"TerrainDamage": "1.5",
"IncomingDamage": "0.75",
"AISmellMode": "2",
}
for name, want := range checks {
if got := vals[name]; got != want {
t.Errorf("%s = %q, want %q", name, got, want)
}
}
// Re-encode: only the 6 non-default options should appear, in enum order →
// must reproduce the original code exactly.
round, err := c.Encode(vals)
if err != nil {
t.Fatalf("encode: %v", err)
}
if round != code {
t.Errorf("round-trip mismatch:\n have %q\n want %q", round, code)
}
}
// TestEmptyAndDefaultsEncodeEmpty: the all-defaults map encodes to just the
// version char (no triples), and an empty code decodes to all defaults.
func TestEmptyAndDefaultsEncodeEmpty(t *testing.T) {
c := V3Codec{}
def := DefaultsMap()
code, err := c.Encode(def)
if err != nil {
t.Fatalf("encode defaults: %v", err)
}
if code != "A" {
t.Errorf("defaults should encode to bare version char %q, got %q", "A", code)
}
vals, err := c.Decode("")
if err != nil {
t.Fatalf("decode empty: %v", err)
}
if len(vals) != 150 {
t.Errorf("empty decode should yield 150 defaults, got %d", len(vals))
}
for name, v := range vals {
if def[name] != v {
t.Errorf("%s: empty-decode %q != defaults %q", name, v, def[name])
}
}
}
// TestRoundTripNonDefault sets a spread of options across categories/types and
// confirms encode→decode preserves them.
func TestRoundTripNonDefault(t *testing.T) {
c := V3Codec{}
in := DefaultsMap()
in["BloodMoonFrequency"] = "14" // int set, non-default
in["XPMultiplier"] = "2" // float set
in["AllowZombieDigging"] = "false"
in["TraderResetInterval"] = "7"
in["SillyBigHeads"] = "true"
code, err := c.Encode(in)
if err != nil {
t.Fatalf("encode: %v", err)
}
out, err := c.Decode(code)
if err != nil {
t.Fatalf("decode: %v", err)
}
for _, k := range []string{"BloodMoonFrequency", "XPMultiplier", "AllowZombieDigging", "TraderResetInterval", "SillyBigHeads"} {
if out[k] != in[k] {
t.Errorf("%s: round-trip %q != %q", k, out[k], in[k])
}
}
}
// TestAlphaHelpers spot-checks the index<->alpha conversions against the C#.
func TestAlphaHelpers(t *testing.T) {
if v, _ := alpha2ToIndex("BN"); v != 39 {
t.Errorf("BN -> %d, want 39", v)
}
if v, _ := alpha2ToIndex("AA"); v != 0 {
t.Errorf("AA -> %d, want 0", v)
}
if s, _ := indexToAlpha2(39); s != "BN" {
t.Errorf("39 -> %q, want BN", s)
}
if v, _ := alphaToIndex('J'); v != 9 {
t.Errorf("J -> %d, want 9", v)
}
if b, _ := indexToAlpha(9); b != 'J' {
t.Errorf("9 -> %c, want J", b)
}
}
// TestValidate rejects malformed codes and accepts good ones.
func TestValidate(t *testing.T) {
if err := Validate(""); err != nil {
t.Errorf("empty should be valid: %v", err)
}
if err := Validate("AAAJABJACJADJARFBNC"); err != nil {
t.Errorf("game default should be valid: %v", err)
}
if err := Validate("ZZZ"); err == nil {
t.Error("wrong version char should fail")
}
if err := Validate("AAB"); err == nil {
t.Error("non-multiple-of-3 body should fail")
}
}