235 lines
8.6 KiB
Go
235 lines
8.6 KiB
Go
package main
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
func csT(offsetSec int) time.Time {
|
||
base := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
|
||
return base.Add(time.Duration(offsetSec) * time.Second)
|
||
}
|
||
|
||
func TestNewCorpseStarchGameInitialState(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
if g.Starch != 0 || g.TotalEarned != 0 {
|
||
t.Fatalf("новая партия должна начинаться с нулевого запаса")
|
||
}
|
||
if g.ClickYield != 1 {
|
||
t.Errorf("ожидался базовый доход клика 1, получено %v", g.ClickYield)
|
||
}
|
||
if len(g.ServitorCounts) != len(corpseStarchServitorDefs) {
|
||
t.Errorf("количество счётчиков сервиторов должно совпадать с числом тиров")
|
||
}
|
||
}
|
||
|
||
func TestRequisitionAddsClickYield(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
g.Requisition(csT(0))
|
||
if g.Starch != 1 || g.TotalEarned != 1 {
|
||
t.Errorf("ожидался запас 1 после одной реквизиции, получено %v/%v", g.Starch, g.TotalEarned)
|
||
}
|
||
g.Requisition(csT(1))
|
||
if g.Starch != 2 {
|
||
t.Errorf("ожидался запас 2 после двух реквизиций, получено %v", g.Starch)
|
||
}
|
||
}
|
||
|
||
func TestTickAccruesPassiveIncome(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
g.ServitorCounts[0] = 2 // 2 сервочерепа по 0.5/сек = 1/сек
|
||
g.Tick(csT(10))
|
||
if g.Starch != 10 {
|
||
t.Errorf("ожидался запас 10 после 10 секунд по 1/сек, получено %v", g.Starch)
|
||
}
|
||
if g.TotalEarned != 10 {
|
||
t.Errorf("TotalEarned тоже должен был вырасти на 10, получено %v", g.TotalEarned)
|
||
}
|
||
}
|
||
|
||
func TestTickWithNoElapsedTimeDoesNothing(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(5))
|
||
g.ServitorCounts[0] = 5
|
||
g.Tick(csT(5))
|
||
if g.Starch != 0 {
|
||
t.Errorf("без прошедшего времени доход не должен начисляться, получено %v", g.Starch)
|
||
}
|
||
}
|
||
|
||
func TestSectionsUnlockProgressively(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
if g.ServitorsUnlocked() || g.ShopUnlocked() || g.SumpUnlocked() || g.MissionsUnlocked() {
|
||
t.Fatalf("в начале партии ни один раздел не должен быть открыт")
|
||
}
|
||
|
||
g.TotalEarned = corpseStarchUnlockServitors
|
||
if !g.ServitorsUnlocked() {
|
||
t.Errorf("сервиторы должны открыться на пороге %v", corpseStarchUnlockServitors)
|
||
}
|
||
if g.ShopUnlocked() {
|
||
t.Errorf("магазин не должен быть открыт раньше своего порога")
|
||
}
|
||
|
||
g.TotalEarned = corpseStarchUnlockMissions
|
||
if !g.ShopUnlocked() || !g.SumpUnlocked() || !g.MissionsUnlocked() {
|
||
t.Errorf("на пороге зачисток все более ранние разделы тоже должны быть открыты")
|
||
}
|
||
}
|
||
|
||
func TestBuyServitorRespectsUnlockAndCost(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
g.Starch = 5
|
||
if g.BuyServitor(0) {
|
||
t.Fatalf("покупка не должна была пройти при нехватке средств")
|
||
}
|
||
g.Starch = 10
|
||
if !g.BuyServitor(0) {
|
||
t.Fatalf("покупка должна была пройти при ровно достаточных средствах")
|
||
}
|
||
if g.ServitorCounts[0] != 1 {
|
||
t.Errorf("счётчик тира 0 должен был увеличиться")
|
||
}
|
||
if g.Starch != 0 {
|
||
t.Errorf("стоимость должна была списаться полностью, получено %v", g.Starch)
|
||
}
|
||
|
||
g.Starch = 1000
|
||
if g.BuyServitor(1) {
|
||
t.Errorf("тир 1 не должен быть доступен без достаточного TotalEarned")
|
||
}
|
||
}
|
||
|
||
func TestServitorCostScalesUp(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
firstCost := g.servitorCost(0)
|
||
g.Starch = firstCost
|
||
g.BuyServitor(0)
|
||
secondCost := g.servitorCost(0)
|
||
if secondCost <= firstCost {
|
||
t.Errorf("стоимость следующей покупки должна расти, было %v стало %v", firstCost, secondCost)
|
||
}
|
||
}
|
||
|
||
func TestBuyClickUpgrade(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
g.Starch = corpseStarchClickUpgradeBaseCost
|
||
before := g.ClickYield
|
||
if !g.BuyClickUpgrade() {
|
||
t.Fatalf("апгрейд должен был пройти при достаточных средствах")
|
||
}
|
||
if g.ClickYield <= before {
|
||
t.Errorf("доход клика должен был вырасти")
|
||
}
|
||
if g.Starch != 0 {
|
||
t.Errorf("стоимость апгрейда должна была списаться полностью")
|
||
}
|
||
}
|
||
|
||
func TestDipChainswordBonusOutcome(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
before := g.Starch
|
||
outcome := g.DipChainsword(csT(0), 0.1)
|
||
if outcome != DipBonusStarch {
|
||
t.Fatalf("ожидался исход DipBonusStarch при малом roll, получено %v", outcome)
|
||
}
|
||
if g.Starch <= before {
|
||
t.Errorf("запас должен был вырасти при бонусном исходе")
|
||
}
|
||
}
|
||
|
||
func TestDipChainswordBlessingGrantsTemporaryMultiplier(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
outcome := g.DipChainsword(csT(0), 0.6)
|
||
if outcome != DipMachineSpiritBlessing {
|
||
t.Fatalf("ожидался исход DipMachineSpiritBlessing, получено %v", outcome)
|
||
}
|
||
if g.currentClickYield(csT(0)) != g.ClickYield*2 {
|
||
t.Errorf("во время благословения доход клика должен удваиваться")
|
||
}
|
||
if g.currentClickYield(csT(31)) != g.ClickYield {
|
||
t.Errorf("после истечения 30 секунд бонус должен был закончиться")
|
||
}
|
||
}
|
||
|
||
func TestDipChainswordNothingOutcome(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
before := g.Starch
|
||
outcome := g.DipChainsword(csT(0), 0.8)
|
||
if outcome != DipNothing {
|
||
t.Fatalf("ожидался исход DipNothing, получено %v", outcome)
|
||
}
|
||
if g.Starch != before {
|
||
t.Errorf("запас не должен был измениться при пустом исходе")
|
||
}
|
||
}
|
||
|
||
func TestDipChainswordCorruptionOutcome(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
g.Starch = 100
|
||
outcome := g.DipChainsword(csT(0), 0.95)
|
||
if outcome != DipCorruption {
|
||
t.Fatalf("ожидался исход DipCorruption, получено %v", outcome)
|
||
}
|
||
if g.Corruption != 1 {
|
||
t.Errorf("скверна должна была увеличиться на 1")
|
||
}
|
||
if g.Starch >= 100 {
|
||
t.Errorf("запас должен был уменьшиться от порчи, получено %v", g.Starch)
|
||
}
|
||
}
|
||
|
||
func TestStartMissionRequiresUnlockAndNoActiveMission(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
if g.StartMission(csT(0), 0) {
|
||
t.Fatalf("миссия не должна была начаться без открытого порога")
|
||
}
|
||
g.TotalEarned = corpseStarchUnlockMissions
|
||
if !g.StartMission(csT(0), 0) {
|
||
t.Fatalf("миссия должна была начаться при открытом пороге")
|
||
}
|
||
if g.StartMission(csT(1), 1) {
|
||
t.Errorf("нельзя начать вторую миссию, пока активна первая")
|
||
}
|
||
}
|
||
|
||
func TestMissionRemainingAndCollect(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
g.TotalEarned = corpseStarchUnlockMissions
|
||
g.StartMission(csT(0), 0) // "vermin", 20 секунд
|
||
|
||
if g.MissionRemaining(csT(5)) <= 0 {
|
||
t.Fatalf("через 5 секунд миссия ещё не должна была закончиться")
|
||
}
|
||
if _, ok := g.CollectMission(csT(5), 0.5); ok {
|
||
t.Errorf("нельзя забрать награду до завершения миссии")
|
||
}
|
||
|
||
if g.MissionRemaining(csT(25)) != 0 {
|
||
t.Errorf("через 25 секунд миссия (20 сек) должна была завершиться")
|
||
}
|
||
reward, ok := g.CollectMission(csT(25), 0.5)
|
||
if !ok {
|
||
t.Fatalf("награда должна была быть получена после завершения")
|
||
}
|
||
def := corpseStarchMissionDefs[0]
|
||
wantReward := def.RewardMin + 0.5*(def.RewardMax-def.RewardMin)
|
||
if reward != wantReward {
|
||
t.Errorf("ожидалась награда %v, получено %v", wantReward, reward)
|
||
}
|
||
if g.ActiveMission != nil {
|
||
t.Errorf("активная миссия должна была сброситься после получения награды")
|
||
}
|
||
if g.MissionsDone != 1 {
|
||
t.Errorf("счётчик завершённых миссий должен был увеличиться")
|
||
}
|
||
}
|
||
|
||
func TestLogTrimsToLimit(t *testing.T) {
|
||
g := NewCorpseStarchGame(csT(0))
|
||
for i := 0; i < corpseStarchLogLimit+5; i++ {
|
||
g.addLog("test.key")
|
||
}
|
||
if len(g.Log) != corpseStarchLogLimit {
|
||
t.Errorf("лог должен обрезаться до лимита %d, получено %d", corpseStarchLogLimit, len(g.Log))
|
||
}
|
||
}
|