658bda1d24
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
155 lines
4.5 KiB
Go
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")
|
|
}
|
|
}
|