Files
pzmanager/internal/pzconfig/installed_test.go
T
mrleo1nidandClaude Opus 5 6d27fd049d
CI / test (push) Successful in 23s
Release / release (push) Successful in 29s
Панель видит моды Build 42 и названия больших сборок
В Build 42 mod.info лежит не в корне мода, а в папке версии: common, 42,
42.16. Сканер смотрел только в корень, и такие моды выглядели нескачанными
с неизвестным Mod ID — включить их через панель было нельзя. На реальной
мастерской находилось 237 модов из 399.

Названия из мастерской фронтенд запрашивал одним списком, а эндпоинт не
принимает больше двухсот идентификаторов за раз. У сборки на 284 пакета
запрос падал целиком, ошибка молча проглатывалась, и вместо названий
оставались одни номера. Теперь запрос идёт партиями, а полная неудача
больше не остаётся незамеченной.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 17:38:23 +03:00

138 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package pzconfig
import (
"os"
"path/filepath"
"testing"
)
// TestScanInstalledMods проверяет обе раскладки мастерской и локальную папку
// модов, а также то, что дубликаты схлопываются.
func TestScanInstalledMods(t *testing.T) {
root := t.TempDir()
content := filepath.Join(root, "server", "steamapps", "workshop", "content", pzAppID)
// Обычная раскладка: <workshop id>/mods/<папка>/mod.info
writeModInfo(t, filepath.Join(content, "111", "mods", "BetterSorting"),
"name=Better Sorting\nid=BetterSorting\ndescription=Сортировка\n")
// Второй мод в том же пакете мастерской.
writeModInfo(t, filepath.Join(content, "111", "mods", "BetterSortingCore"),
"name=Better Sorting Core\nid=BetterSortingCore\n")
// Раскладка без промежуточной папки mods.
writeModInfo(t, filepath.Join(content, "222", "ModOptions"),
"name=Mod Options\nid=ModOptions\n")
// Локальный мод, положенный руками.
local := filepath.Join(root, "Zomboid", "mods")
writeModInfo(t, filepath.Join(local, "MyTweak"), "name=My Tweak\nid=MyTweak\n")
// Тот же мод, что и в мастерской: должен попасть в список один раз.
writeModInfo(t, filepath.Join(local, "BetterSortingCopy"), "name=Better Sorting\nid=BetterSorting\n")
// Папка без mod.info игнорируется.
if err := os.MkdirAll(filepath.Join(content, "333", "mods", "empty"), 0o755); err != nil {
t.Fatal(err)
}
mods := ScanInstalledMods([]string{filepath.Join(root, "server")}, local)
byID := make(map[string]InstalledMod, len(mods))
for _, m := range mods {
if _, dup := byID[m.ModID]; dup {
t.Errorf("мод %s встретился дважды", m.ModID)
}
byID[m.ModID] = m
}
if len(byID) != 4 {
t.Fatalf("найдено %d модов, ожидалось 4: %v", len(byID), byID)
}
if got := byID["BetterSorting"]; got.WorkshopID != "111" || got.Name != "Better Sorting" {
t.Errorf("BetterSorting разобран неверно: %+v", got)
}
if got := byID["ModOptions"]; got.WorkshopID != "222" {
t.Errorf("раскладка без папки mods не распознана: %+v", got)
}
if got := byID["MyTweak"]; got.WorkshopID != "" {
t.Errorf("локальному моду проставлен Workshop ID: %+v", got)
}
}
func TestReadModInfoRequiresID(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "mod.info")
if err := os.WriteFile(path, []byte("name=Без идентификатора\n"), 0o644); err != nil {
t.Fatal(err)
}
if _, ok := readModInfo(path); ok {
t.Error("мод без id принят")
}
}
func writeModInfo(t *testing.T, dir, content string) {
t.Helper()
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "mod.info"), []byte(content), 0o644); err != nil {
t.Fatal(err)
}
}
// В Build 42 mod.info лежит не в корне мода, а в папке версии. Мод из такой
// раскладки должен находиться, иначе панель считает его нескачанным.
func TestScanInstalledModsBuild42Layout(t *testing.T) {
root := t.TempDir()
content := filepath.Join(root, "server", "steamapps", "workshop", "content", pzAppID)
writeModInfo(t, filepath.Join(content, "111", "mods", "Wipes", "common"),
"name=Cleaning Wipes\nid=dustinguished_cleaning_wipes\n")
// Мод с несколькими версиями: берём самую свежую, а не common.
mod := filepath.Join(content, "222", "mods", "NewMusicG90s")
writeModInfo(t, filepath.Join(mod, "common"), "name=Mixtape (common)\nid=NewMusicG90s\n")
writeModInfo(t, filepath.Join(mod, "42"), "name=Mixtape (42)\nid=NewMusicG90s\n")
writeModInfo(t, filepath.Join(mod, "42.16"), "name=Mixtape (42.16)\nid=NewMusicG90s\n")
mods := ScanInstalledMods([]string{filepath.Join(root, "server")}, "")
byID := make(map[string]InstalledMod, len(mods))
for _, m := range mods {
byID[m.ModID] = m
}
if len(byID) != 2 {
t.Fatalf("найдено %d модов, ожидалось 2: %v", len(byID), byID)
}
if got := byID["dustinguished_cleaning_wipes"]; got.Name != "Cleaning Wipes" || got.WorkshopID != "111" {
t.Errorf("мод из common разобран неверно: %+v", got)
}
if got := byID["NewMusicG90s"]; got.Name != "Mixtape (42.16)" {
t.Errorf("взята не самая свежая версия: %+v", got)
}
// Dir указывает на папку мода, а не на папку версии: так его удобнее найти.
if got := byID["NewMusicG90s"]; filepath.Base(got.Dir) != "NewMusicG90s" {
t.Errorf("Dir указывает не на папку мода: %+v", got)
}
}
// Корневой mod.info главнее версий: так раскладывают моды Build 41.
func TestModInfoPathPrefersRoot(t *testing.T) {
mod := t.TempDir()
writeModInfo(t, mod, "name=Root\nid=Root\n")
writeModInfo(t, filepath.Join(mod, "42"), "name=Version\nid=Version\n")
path, ok := modInfoPath(mod)
if !ok {
t.Fatal("mod.info не найден")
}
if filepath.Dir(path) != mod {
t.Errorf("взят mod.info из папки версии: %s", path)
}
}
func TestModInfoPathWithoutModInfo(t *testing.T) {
mod := t.TempDir()
if err := os.MkdirAll(filepath.Join(mod, "media"), 0o755); err != nil {
t.Fatal(err)
}
if _, ok := modInfoPath(mod); ok {
t.Error("mod.info найден там, где его нет")
}
}