172 lines
6.4 KiB
Go
172 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
// withTempConfigDir подменяет XDG_CONFIG_HOME на временную — так
|
|
// os.UserConfigDir() на Linux/macOS будет смотреть в неё, и тесты
|
|
// не трогают реальный конфиг пользователя.
|
|
func withTempConfigDir(t *testing.T) {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
oldXDG, hadXDG := os.LookupEnv("XDG_CONFIG_HOME")
|
|
os.Setenv("XDG_CONFIG_HOME", dir)
|
|
t.Cleanup(func() {
|
|
if hadXDG {
|
|
os.Setenv("XDG_CONFIG_HOME", oldXDG)
|
|
} else {
|
|
os.Unsetenv("XDG_CONFIG_HOME")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLoadCorpseStarchGameWithNoSaveFile(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
_, err := LoadCorpseStarchGame()
|
|
if err != ErrCorpseStarchNoSave {
|
|
t.Fatalf("ожидалась ErrCorpseStarchNoSave при отсутствии файла, получено: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSaveAndLoadRoundTrip(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
g := NewCorpseStarchGame(csT(0))
|
|
g.Starch = 123.5
|
|
g.TotalEarned = 999
|
|
g.ClickYield = 4
|
|
g.ClickUpgrades = 3
|
|
g.ServitorCounts[0] = 5
|
|
g.ServitorCounts[1] = 2
|
|
g.Corruption = 2
|
|
g.MissionsDone = 1
|
|
g.Log = []string{"a", "b", "c"}
|
|
|
|
if err := SaveCorpseStarchGame(g); err != nil {
|
|
t.Fatalf("неожиданная ошибка сохранения: %v", err)
|
|
}
|
|
|
|
loaded, err := LoadCorpseStarchGame()
|
|
if err != nil {
|
|
t.Fatalf("неожиданная ошибка загрузки: %v", err)
|
|
}
|
|
if loaded.Starch != g.Starch || loaded.TotalEarned != g.TotalEarned {
|
|
t.Errorf("запас/накопленное не совпадают после загрузки: %+v vs %+v", loaded, g)
|
|
}
|
|
if loaded.ClickYield != g.ClickYield || loaded.ClickUpgrades != g.ClickUpgrades {
|
|
t.Errorf("параметры клика не совпадают после загрузки")
|
|
}
|
|
if loaded.ServitorCounts[0] != 5 || loaded.ServitorCounts[1] != 2 {
|
|
t.Errorf("счётчики сервиторов не совпадают после загрузки: %v", loaded.ServitorCounts)
|
|
}
|
|
if loaded.Corruption != 2 || loaded.MissionsDone != 1 {
|
|
t.Errorf("скверна/завершённые миссии не совпадают после загрузки")
|
|
}
|
|
if len(loaded.Log) != 3 {
|
|
t.Errorf("журнал должен был сохраниться полностью, получено %v", loaded.Log)
|
|
}
|
|
}
|
|
|
|
func TestSaveAndLoadPreservesActiveMission(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
g := NewCorpseStarchGame(csT(0))
|
|
g.TotalEarned = corpseStarchUnlockMissions
|
|
g.StartMission(csT(100), 1)
|
|
|
|
if err := SaveCorpseStarchGame(g); err != nil {
|
|
t.Fatalf("неожиданная ошибка сохранения: %v", err)
|
|
}
|
|
loaded, err := LoadCorpseStarchGame()
|
|
if err != nil {
|
|
t.Fatalf("неожиданная ошибка загрузки: %v", err)
|
|
}
|
|
if loaded.ActiveMission == nil {
|
|
t.Fatalf("активная миссия должна была сохраниться")
|
|
}
|
|
if loaded.ActiveMission.DefIdx != 1 {
|
|
t.Errorf("ожидался индекс миссии 1, получено %d", loaded.ActiveMission.DefIdx)
|
|
}
|
|
if !loaded.ActiveMission.StartedAt.Equal(csT(100)) {
|
|
t.Errorf("время начала миссии должно было сохраниться точно, получено %v", loaded.ActiveMission.StartedAt)
|
|
}
|
|
}
|
|
|
|
// TestOfflineProgressAccruesOnLoad проверяет ключевое ожидаемое
|
|
// поведение: если между сохранением и загрузкой прошло реальное
|
|
// время, пассивный доход от сервиторов должен по-честному
|
|
// начислиться при первом Tick после загрузки — ровно так же, как
|
|
// если бы игрок всё это время не отходил от экрана.
|
|
func TestOfflineProgressAccruesOnLoad(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
g := NewCorpseStarchGame(csT(0))
|
|
g.ServitorCounts[0] = 10 // 10 * 0.5/сек = 5/сек
|
|
if err := SaveCorpseStarchGame(g); err != nil {
|
|
t.Fatalf("неожиданная ошибка сохранения: %v", err)
|
|
}
|
|
|
|
loaded, err := LoadCorpseStarchGame()
|
|
if err != nil {
|
|
t.Fatalf("неожиданная ошибка загрузки: %v", err)
|
|
}
|
|
loaded.Tick(csT(100))
|
|
if loaded.Starch != 500 {
|
|
t.Errorf("ожидался начисленный доход за 100 секунд отсутствия (500), получено %v", loaded.Starch)
|
|
}
|
|
}
|
|
|
|
func TestDeleteCorpseStarchSave(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
g := NewCorpseStarchGame(csT(0))
|
|
if err := SaveCorpseStarchGame(g); err != nil {
|
|
t.Fatalf("неожиданная ошибка сохранения: %v", err)
|
|
}
|
|
if err := DeleteCorpseStarchSave(); err != nil {
|
|
t.Fatalf("неожиданная ошибка удаления: %v", err)
|
|
}
|
|
if _, err := LoadCorpseStarchGame(); err != ErrCorpseStarchNoSave {
|
|
t.Errorf("после удаления файла загрузка должна вернуть ErrCorpseStarchNoSave, получено: %v", err)
|
|
}
|
|
|
|
if err := DeleteCorpseStarchSave(); err != nil {
|
|
t.Errorf("повторное удаление отсутствующего файла не должно быть ошибкой, получено: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoadCorruptedSaveFileReturnsError(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
path, err := corpseStarchSavePath()
|
|
if err != nil {
|
|
t.Fatalf("неожиданная ошибка получения пути: %v", err)
|
|
}
|
|
dir := path[:len(path)-len("/corpsestarch_save.json")]
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatalf("неожиданная ошибка создания директории: %v", err)
|
|
}
|
|
if err := os.WriteFile(path, []byte("это не json"), 0o644); err != nil {
|
|
t.Fatalf("неожиданная ошибка записи повреждённого файла: %v", err)
|
|
}
|
|
|
|
if _, err := LoadCorpseStarchGame(); err == nil {
|
|
t.Fatalf("ожидалась ошибка при загрузке повреждённого файла")
|
|
}
|
|
}
|
|
|
|
func TestSaveOverwritesPreviousSave(t *testing.T) {
|
|
withTempConfigDir(t)
|
|
g1 := NewCorpseStarchGame(csT(0))
|
|
g1.Starch = 10
|
|
SaveCorpseStarchGame(g1)
|
|
|
|
g2 := NewCorpseStarchGame(csT(0))
|
|
g2.Starch = 999
|
|
SaveCorpseStarchGame(g2)
|
|
|
|
loaded, err := LoadCorpseStarchGame()
|
|
if err != nil {
|
|
t.Fatalf("неожиданная ошибка загрузки: %v", err)
|
|
}
|
|
if loaded.Starch != 999 {
|
|
t.Errorf("повторное сохранение должно было перезаписать файл, получено %v", loaded.Starch)
|
|
}
|
|
}
|