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") } }