go_games_collection/go_tui_test.go
2026-07-10 09:12:06 +03:00

198 lines
6.6 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 "testing"
func TestGoModelPlayStoneViaKey(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
m.cursor = GoPos{Row: 4, Col: 4}
next, _ := m.Update(key("enter"))
m = next.(GoModel)
if m.isError {
t.Fatalf("неожиданная ошибка: %s", m.message)
}
if m.game.Board[4][4] != GoBlack {
t.Fatalf("камень должен был встать в (4,4) как чёрный")
}
}
func TestGoModelCursorMovement(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
m.cursor = GoPos{Row: 4, Col: 4}
next, _ := m.Update(key("up"))
m = next.(GoModel)
if m.cursor.Row != 3 {
t.Errorf("курсор должен был сдвинуться вверх")
}
next, _ = m.Update(key("right"))
m = next.(GoModel)
if m.cursor.Col != 5 {
t.Errorf("курсор должен был сдвинуться вправо")
}
}
func TestGoModelCursorClampsAtEdges(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
m.cursor = GoPos{Row: 0, Col: 0}
next, _ := m.Update(key("up"))
m = next.(GoModel)
if m.cursor.Row != 0 {
t.Errorf("курсор не должен был уйти за верхний край")
}
next, _ = m.Update(key("left"))
m = next.(GoModel)
if m.cursor.Col != 0 {
t.Errorf("курсор не должен был уйти за левый край")
}
}
func TestGoModelPassViaKey(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
next, _ := m.Update(key("p"))
m = next.(GoModel)
if m.isError {
t.Fatalf("неожиданная ошибка паса: %s", m.message)
}
}
func TestGoModelInvalidMoveShowsError(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
m.cursor = GoPos{Row: 0, Col: 0}
m.game.Board[0][0] = GoBlack
next, _ := m.Update(key("enter"))
m = next.(GoModel)
if !m.isError {
t.Fatalf("ожидалась ошибка хода на занятую точку")
}
}
// TestGoModelBotPassShowsDistinctMessage — по мотивам реального
// наблюдения пользователя: "бот делал ход, но камень не появлялся"
// — на самом деле бот пасовал, но интерфейс молча показывал то же
// сообщение "Бот сходил.", что и при обычном ходе, из-за чего
// казалось, будто ход просто не отрисовался.
func TestGoModelBotPassShowsDistinctMessage(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
// заполняем всю доску без единой пустой клетки — тогда легальных
// ходов гарантированно нет ни у кого, пас неизбежен
for r := 0; r < 9; r++ {
for c := 0; c < 9; c++ {
if (r+c)%2 == 0 {
m.game.Board[r][c] = GoBlack
} else {
m.game.Board[r][c] = GoWhite
}
}
}
m.game.Turn = GoWhite
next, _ := m.Update(botMoveMsg{})
m = next.(GoModel)
if m.message != T("go.msg.bot_passed") {
t.Errorf("ожидалось отдельное сообщение о пасе бота, получено: %q", m.message)
}
if m.message == T("go.msg.bot_moved") {
t.Errorf("сообщение о пасе не должно совпадать с сообщением об обычном ходе")
}
}
func TestGoModelQReturnsToMenu(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
next, _ := m.Update(key("q"))
m = next.(GoModel)
if !m.backToMenu {
t.Fatalf("ожидался флаг backToMenu после 'q'")
}
if m.quitting {
t.Fatalf("'q' не должен закрывать всё приложение")
}
}
func TestGoModelNewGameAfterResult(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
m.game.Result = &GoResult{Winner: GoBlack, BlackScore: 10, WhiteScore: 7.5}
next, _ := m.Update(key("n"))
m = next.(GoModel)
if m.game.Result != nil {
t.Errorf("новая партия не должна была начаться с результатом")
}
if m.game.Size != 9 {
t.Errorf("новая партия должна была сохранить размер доски")
}
}
func TestGoModelSupportsAllBoardSizes(t *testing.T) {
for _, size := range []int{9, 13, 19} {
m := NewGoModel(size, GoDifficultyMedium, ColorChoiceA)
if m.game.Size != size {
t.Errorf("ожидался размер доски %d, получено %d", size, m.game.Size)
}
}
}
// TestGoModelFullAutoPlaythrough прогоняет TUI-модель от начала до
// конца на маленькой доске, где "человек" отыгрывает свои ходы
// через бота, проверяя, что весь путь через Update не паникует и
// партия завершается.
func TestGoModelFullAutoPlaythrough(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
helperBot := GoBot{Difficulty: GoDifficultyEasy}
rnd := m.rnd
const maxSteps = 3000
for i := 0; i < maxSteps && m.game.Result == nil; i++ {
if m.game.Turn != m.human {
next, _ := m.Update(botMoveMsg{})
m = next.(GoModel)
continue
}
pos, ok := helperBot.DecideMove(m.game, rnd)
if !ok {
next, _ := m.Update(key("p"))
m = next.(GoModel)
continue
}
m.cursor = pos
next, _ := m.Update(key("enter"))
m = next.(GoModel)
if m.isError {
t.Fatalf("неожиданная ошибка хода человеком: %s", m.message)
}
}
if m.game.Result == nil {
t.Fatalf("партия через TUI не завершилась за %d шагов", maxSteps)
}
}
func TestNewGoModelRespectsColorChoiceBlack(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceA)
if m.human != GoBlack {
t.Errorf("при выборе ColorChoiceA человек должен был играть чёрными, получено %v", m.human)
}
}
func TestNewGoModelRespectsColorChoiceWhite(t *testing.T) {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceB)
if m.human != GoWhite {
t.Errorf("при выборе ColorChoiceB человек должен был играть белыми, получено %v", m.human)
}
}
func TestNewGoModelRandomColorGivesValidColor(t *testing.T) {
seenBlack, seenWhite := false, false
for i := 0; i < 50 && !(seenBlack && seenWhite); i++ {
m := NewGoModel(9, GoDifficultyEasy, ColorChoiceRandom)
if m.human == GoBlack {
seenBlack = true
} else {
seenWhite = true
}
}
if !seenBlack || !seenWhite {
t.Errorf("случайный выбор цвета должен был давать оба варианта за 50 попыток: чёрные=%v белые=%v", seenBlack, seenWhite)
}
}