go_games_collection/corpsestarch_tui_test.go

379 lines
13 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 main
import (
"fmt"
"strings"
"testing"
"time"
)
func TestCorpseStarchModelRequisitionViaKey(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
next, _ := m.Update(key("1"))
m = next.(CorpseStarchModel)
if m.game.Starch != 1 {
t.Fatalf("ожидался запас 1 после нажатия [1], получено %v", m.game.Starch)
}
}
func TestCorpseStarchModelBuyServitorActionHiddenDuringIncursion(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.TotalEarned = corpseStarchUnlockServitors // открываем раздел сервиторов
m.game.ChaosIncursionUntil = m.game.LastTick.Add(time.Hour)
during := m.buildActions(m.game.LastTick)
for _, a := range during {
if strings.Contains(a.Label, T("corpsestarch.servitor.skull")) {
t.Errorf("покупка сервитора не должна предлагаться во время вторжения")
}
}
}
func TestCorpseStarchModelDefendActionAppearsDuringIncursion(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
before := len(m.buildActions(m.game.LastTick))
m.game.ChaosIncursionUntil = m.game.LastTick.Add(1)
after := m.buildActions(m.game.LastTick)
if len(after) != before+1 {
t.Fatalf("ожидалось на одно действие больше во время вторжения, было %d, стало %d", before, len(after))
}
if after[len(after)-1].Label != Tf("corpsestarch.action.defend", 0) {
t.Errorf("последнее действие должно быть 'отбиваться', получено: %q", after[len(after)-1].Label)
}
}
func TestCorpseStarchModelDefendActionIncrementsCounterViaKey(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.ChaosIncursionUntil = time.Now().Add(time.Hour)
actions := m.buildActions(time.Now())
defendKey := fmt.Sprintf("%d", len(actions))
next, _ := m.Update(key(defendKey))
m = next.(CorpseStarchModel)
if m.game.ChaosDefendClicks != 1 {
t.Errorf("ожидался счётчик защиты 1 после нажатия, получено %d", m.game.ChaosDefendClicks)
}
}
func TestCorpseStarchModelWarpBreachVisibleInView(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.Starch = 1000
m.game.ServitorCounts[0] = 10
m.game.DipChainsword(m.game.LastTick, 0.95)
m.game.DipChainsword(m.game.LastTick, 0.95)
m.game.DipChainsword(m.game.LastTick, 0.95)
view := m.View()
normalized := strings.Join(strings.Fields(view), " ")
wantLog := strings.Join(strings.Fields(T("corpsestarch.log.warp_breach")), " ")
if !strings.Contains(normalized, wantLog) {
t.Errorf("экран должен был показать сообщение о варп-выбросе в журнале")
}
if !strings.Contains(view, "ВТОРЖЕНИЕ ХАОСА") {
t.Errorf("экран должен был показать баннер активного вторжения")
}
if m.game.ServitorCounts[0] != 5 {
t.Errorf("сервиторы должны были уполовиниться, получено %d", m.game.ServitorCounts[0])
}
}
func TestCorpseStarchModelActionsExpandAsUnlocked(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
if len(m.buildActions(time.Now())) != 1 {
t.Fatalf("в начале должно быть только 1 действие (реквизиция)")
}
m.game.TotalEarned = corpseStarchUnlockServitors
if len(m.buildActions(time.Now())) != 2 {
t.Fatalf("после открытия сервиторов должно быть 2 действия")
}
m.game.TotalEarned = corpseStarchUnlockMissions
actions := m.buildActions(time.Now())
if len(actions) != 8 {
t.Fatalf("ожидалось 8 действий при полностью открытых (кроме 3 тира) разделах, получено %d", len(actions))
}
}
func TestCorpseStarchModelBuyServitorViaKey(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.TotalEarned = corpseStarchUnlockServitors
m.game.Starch = 100
next, _ := m.Update(key("2"))
m = next.(CorpseStarchModel)
if m.game.ServitorCounts[0] != 1 {
t.Fatalf("ожидалась покупка сервочерпа, получено count=%d", m.game.ServitorCounts[0])
}
}
func TestCorpseStarchModelTickAccruesIncome(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.ServitorCounts[0] = 10
past := m.game.LastTick.Add(2 * time.Second)
next, _ := m.Update(corpseStarchTickMsg{At: past})
m = next.(CorpseStarchModel)
if m.game.Starch != 10 {
t.Errorf("ожидался запас 10 после тика на 2 секунды при 5/сек, получено %v", m.game.Starch)
}
}
func TestCorpseStarchModelResetRequestShowsConfirmation(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
next, _ := m.Update(key("N"))
m = next.(CorpseStarchModel)
if !m.confirmingReset {
t.Fatalf("ожидался запрос подтверждения после нажатия N")
}
}
func TestCorpseStarchModelResetCancelKeepsProgress(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.Starch = 500
m.game.TotalEarned = 500
next, _ := m.Update(key("N"))
m = next.(CorpseStarchModel)
next, _ = m.Update(key("n"))
m = next.(CorpseStarchModel)
if m.confirmingReset {
t.Errorf("запрос подтверждения должен был закрыться после отмены")
}
if m.game.Starch != 500 {
t.Errorf("прогресс не должен был сброситься при отмене, получено Starch=%v", m.game.Starch)
}
}
func TestCorpseStarchModelResetConfirmWipesProgress(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.Starch = 500
m.game.TotalEarned = 500
next, _ := m.Update(key("N"))
m = next.(CorpseStarchModel)
next, _ = m.Update(key("y"))
m = next.(CorpseStarchModel)
if m.confirmingReset {
t.Errorf("запрос подтверждения должен был закрыться после подтверждения")
}
if m.game.Starch != 0 || m.game.TotalEarned != 0 {
t.Errorf("прогресс должен был сброситься, получено Starch=%v TotalEarned=%v", m.game.Starch, m.game.TotalEarned)
}
}
func TestCorpseStarchModelChaosOfferAcceptStartsRebirth(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.ChaosMarks = corpseStarchChaosMarksForOffer
view := m.View()
if !strings.Contains(view, T("corpsestarch.chaos.offer.title")) {
t.Fatalf("экран должен был показать предложение Хаоса")
}
next, _ := m.Update(key("y"))
m = next.(CorpseStarchModel)
if !m.game.InRebirth(m.game.LastTick) && m.game.RebirthUntil.IsZero() {
t.Errorf("принятие предложения должно было начать перерождение")
}
}
func TestCorpseStarchModelChaosOfferDeclineResetsMarks(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.ChaosMarks = corpseStarchChaosMarksForOffer
starchBefore := m.game.Starch
next, _ := m.Update(key("n"))
m = next.(CorpseStarchModel)
if m.game.ChaosMarks != 0 {
t.Errorf("отказ должен был сбросить счётчик меток, получено %d", m.game.ChaosMarks)
}
if m.game.Starch <= starchBefore {
t.Errorf("отказ должен был дать награду, было %v, стало %v", starchBefore, m.game.Starch)
}
if m.game.ChaosOfferPending() {
t.Errorf("предложение не должно оставаться активным после отказа")
}
}
func TestCorpseStarchModelActionsIgnoredDuringChaosOffer(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.ChaosMarks = corpseStarchChaosMarksForOffer
starchBefore := m.game.Starch
next, _ := m.Update(key("1")) // обычная реквизиция не должна сработать
m = next.(CorpseStarchModel)
if m.game.Starch != starchBefore {
t.Errorf("обычные действия не должны срабатывать, пока предложение Хаоса не разрешено")
}
}
func TestCorpseStarchModelRebirthShowsHintForQuitAndNewGame(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.RebirthUntil = m.game.LastTick.Add(time.Hour)
view := m.View()
if !strings.Contains(view, "[q]") || !strings.Contains(view, "[N]") {
t.Errorf("экран перерождения должен был показывать подсказки [q] и [N], получено:\n%s", view)
}
}
func TestCorpseStarchModelNKeyDuringRebirthOpensConfirmation(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.RebirthUntil = m.game.LastTick.Add(time.Hour)
next, _ := m.Update(key("N"))
m = next.(CorpseStarchModel)
if !m.confirmingReset {
t.Fatalf("клавиша N во время перерождения должна была открыть подтверждение сброса")
}
view := m.View()
if !strings.Contains(view, T("corpsestarch.confirm_reset")) {
t.Errorf("экран подтверждения должен был показаться поверх экрана перерождения")
}
}
func TestCorpseStarchModelConfirmNewGameDuringRebirthActuallyResets(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.RebirthUntil = m.game.LastTick.Add(time.Hour)
m.game.Starch = 999
next, _ := m.Update(key("N"))
m = next.(CorpseStarchModel)
next, _ = m.Update(key("y"))
m = next.(CorpseStarchModel)
if m.confirmingReset {
t.Errorf("подтверждение должно было закрыться после ответа")
}
if m.game.InRebirth(time.Now()) {
t.Errorf("новая партия не должна была унаследовать перерождение старой")
}
if m.game.Starch != 0 {
t.Errorf("новая партия должна была начаться с чистого счёта, получено %v", m.game.Starch)
}
}
func TestCorpseStarchModelRebirthBlocksAllActionsExceptQuit(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.RebirthUntil = m.game.LastTick.Add(time.Hour)
starchBefore := m.game.Starch
next, _ := m.Update(key("1"))
m = next.(CorpseStarchModel)
if m.game.Starch != starchBefore {
t.Errorf("во время перерождения обычные действия не должны срабатывать")
}
view := m.View()
if !strings.Contains(view, "ПЕРЕРОЖДЕНИЕ") {
t.Errorf("экран должен был показать статус перерождения")
}
next, _ = m.Update(key("q"))
m = next.(CorpseStarchModel)
if !m.backToMenu {
t.Errorf("клавиша q должна была сработать даже во время перерождения (сохранение и выход)")
}
}
func TestCorpseStarchModelResetActionsIgnoredWhileConfirming(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
starchBefore := m.game.Starch
next, _ := m.Update(key("N"))
m = next.(CorpseStarchModel)
// нажатие "1" (реквизиция) не должно срабатывать, пока ждём подтверждения
next, _ = m.Update(key("1"))
m = next.(CorpseStarchModel)
if m.game.Starch != starchBefore {
t.Errorf("обычные действия не должны выполняться во время запроса подтверждения")
}
}
func TestCorpseStarchModelQReturnsToMenu(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
next, _ := m.Update(key("q"))
m = next.(CorpseStarchModel)
if !m.backToMenu {
t.Fatalf("ожидался флаг backToMenu после 'q'")
}
if m.quitting {
t.Fatalf("'q' не должен закрывать всё приложение")
}
}
func TestCorpseStarchModelMissionFlowViaKeys(t *testing.T) {
withTempConfigDir(t)
m := NewCorpseStarchModel()
m.game.TotalEarned = corpseStarchUnlockMissions
m.game.Starch = 0
actions := m.buildActions(time.Now())
missionActionIdx := -1
for i, a := range actions {
if a.Label == Tf("corpsestarch.action.start_mission", T("corpsestarch.mission.vermin"), formatDuration(corpseStarchMissionDefs[0].Duration)) {
missionActionIdx = i
break
}
}
if missionActionIdx == -1 {
t.Fatalf("не нашли действие запуска первой миссии среди %d действий", len(actions))
}
next, _ := m.Update(key(string(rune('1' + missionActionIdx))))
m = next.(CorpseStarchModel)
if m.game.ActiveMission == nil {
t.Fatalf("миссия должна была начаться")
}
finished := m.game.ActiveMission.StartedAt.Add(corpseStarchMissionDefs[0].Duration + time.Second)
if m.game.MissionRemaining(finished) != 0 {
t.Fatalf("миссия должна была завершиться к этому моменту")
}
collectActions := m.buildActions(finished)
collectIdx := -1
for i, a := range collectActions {
if a.Label == T("corpsestarch.action.collect_mission") {
collectIdx = i
break
}
}
if collectIdx == -1 {
t.Fatalf("не нашли действие сбора награды")
}
collectActions[collectIdx].Handle(&m, finished)
if m.game.ActiveMission != nil {
t.Errorf("миссия должна была завершиться после сбора награды")
}
if m.game.MissionsDone != 1 {
t.Errorf("счётчик завершённых миссий должен был увеличиться")
}
}