package pzconfig import ( "strings" "testing" ) const sampleINI = `# Comment line PublicName=My Server Public=true MaxPlayers=32 Mods=modA;modB WorkshopItems=111;222 ` func TestINIPreservesLayout(t *testing.T) { ini := ParseINI([]byte(sampleINI)) if got := ini.Get("PublicName"); got != "My Server" { t.Fatalf("PublicName = %q, ожидалось %q", got, "My Server") } // Правка одного ключа не должна трогать комментарии и пустые строки. ini.Set("MaxPlayers", "64") got := string(ini.Render()) if !strings.HasPrefix(got, "# Comment line\n") { t.Errorf("комментарий потерян:\n%s", got) } if !strings.Contains(got, "\n\nMaxPlayers=64\n") { t.Errorf("пустая строка или новое значение потеряны:\n%s", got) } if strings.Contains(got, "MaxPlayers=32") { t.Errorf("старое значение осталось:\n%s", got) } } func TestINIAddsMissingKey(t *testing.T) { ini := ParseINI([]byte("A=1")) ini.Set("B", "2") if got := ini.Get("B"); got != "2" { t.Fatalf("B = %q", got) } if got := string(ini.Render()); got != "A=1\nB=2" { t.Fatalf("render = %q", got) } } func TestINIRejectsNewlineInValue(t *testing.T) { ini := ParseINI([]byte("A=1")) ini.Set("A", "нет\nпереносам") if strings.Contains(string(ini.Render()), "\nп") { t.Fatalf("перенос строки попал в значение: %q", ini.Get("A")) } } func TestModsRoundTrip(t *testing.T) { ini := ParseINI([]byte(sampleINI)) mods, workshop := ini.Mods() if len(mods) != 2 || mods[0] != "modA" { t.Fatalf("mods = %v", mods) } if len(workshop) != 2 || workshop[1] != "222" { t.Fatalf("workshop = %v", workshop) } // Дубликаты отбрасываются, порядок сохраняется. ini.SetMods([]string{"modB", "modA", "modB"}, []string{"222"}) if got := ini.Get(KeyMods); got != "modB;modA" { t.Fatalf("Mods = %q", got) } if got := ini.Get(KeyWorkshopItems); got != "222" { t.Fatalf("WorkshopItems = %q", got) } } func TestSplitListIgnoresBlanks(t *testing.T) { if got := SplitList(" a ; ; b;"); len(got) != 2 || got[0] != "a" || got[1] != "b" { t.Fatalf("SplitList = %v", got) } if got := SplitList(" "); len(got) != 0 { t.Fatalf("пустая строка дала %v", got) } } const sampleSandbox = `SandboxVars = { VERSION = 5, Zombies = 3, Speed = 2, ZombieLore = { Speed = 2, Strength = 2, }, WaterShutModifier = 14, } ` func TestSandboxParsesNestedPaths(t *testing.T) { sb := ParseSandbox([]byte(sampleSandbox)) if v, ok := sb.Get("Zombies"); !ok || v != "3" { t.Fatalf("Zombies = %q (найдено: %v)", v, ok) } // Одноимённые ключи на разных уровнях не должны перетирать друг друга. if v, ok := sb.Get("ZombieLore.Speed"); !ok || v != "2" { t.Fatalf("ZombieLore.Speed = %q (найдено: %v)", v, ok) } if v, ok := sb.Get("Speed"); !ok || v != "2" { t.Fatalf("Speed верхнего уровня = %q (найдено: %v)", v, ok) } if v, ok := sb.Get("WaterShutModifier"); !ok || v != "14" { t.Fatalf("WaterShutModifier после закрытия таблицы = %q (найдено: %v)", v, ok) } } func TestSandboxSetKeepsFormatting(t *testing.T) { sb := ParseSandbox([]byte(sampleSandbox)) if err := sb.Set("ZombieLore.Speed", "1"); err != nil { t.Fatal(err) } got := string(sb.Render()) if !strings.Contains(got, " Speed = 1,") { t.Errorf("отступ или запятая потеряны:\n%s", got) } if !strings.Contains(got, " Speed = 2,") { t.Errorf("параметр верхнего уровня изменён по ошибке:\n%s", got) } if !strings.HasSuffix(got, "}\n") { t.Errorf("хвост файла изменён:\n%s", got) } } func TestSandboxRejectsUnknownAndBrokenValues(t *testing.T) { sb := ParseSandbox([]byte(sampleSandbox)) if err := sb.Set("НетТакого", "1"); err == nil { t.Error("ожидалась ошибка для неизвестного параметра") } if err := sb.Set("Zombies", "{ 1, 2 }"); err == nil { t.Error("ожидалась ошибка для значения с фигурными скобками") } } func TestSandboxEntriesFollowFileOrder(t *testing.T) { entries := ParseSandbox([]byte(sampleSandbox)).Entries() want := []string{"VERSION", "Zombies", "Speed", "ZombieLore.Speed", "ZombieLore.Strength", "WaterShutModifier"} if len(entries) != len(want) { t.Fatalf("получено %d параметров, ожидалось %d: %v", len(entries), len(want), entries) } for i, path := range want { if entries[i].Path != path { t.Errorf("entries[%d] = %q, ожидалось %q", i, entries[i].Path, path) } } }