package main import "testing" func TestTicTacToeModelPlaceMarkViaDigitKey(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) m.game = NewTicTacToeGame(TicTacToeX) // фиксируем: человек играет крестиками, сейчас его ход next, _ := m.Update(key("5")) m = next.(TicTacToeModel) if m.game.Board[4] != TicTacToeX { t.Fatalf("ожидался крестик в клетке 5 (индекс 4), доска: %v", m.game.Board) } if m.isError { t.Errorf("неожиданная ошибка: %s", m.message) } } func TestTicTacToeModelCursorMovementAndEnter(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) m.game = NewTicTacToeGame(TicTacToeX) m.cursor = 4 // центр next, _ := m.Update(key("up")) m = next.(TicTacToeModel) if m.cursor != 1 { t.Fatalf("ожидался курсор на клетке 1 (индекс 1) после 'up' из центра, получено %d", m.cursor) } next, _ = m.Update(key("enter")) m = next.(TicTacToeModel) if m.game.Board[1] != TicTacToeX { t.Errorf("ожидался крестик в клетке под курсором (индекс 1), доска: %v", m.game.Board) } } func TestTicTacToeModelRejectsMoveWhenNotHumanTurn(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) m.game = NewTicTacToeGame(TicTacToeO) // человек — нолики, сейчас ход бота (крестиков) next, _ := m.Update(key("5")) m = next.(TicTacToeModel) if m.game.MovesPlayed != 0 { t.Errorf("ход не должен был приняться — сейчас очередь бота, а не человека") } } func TestTicTacToeModelBotMovesFirstWhenHumanIsO(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyHard) m.game = NewTicTacToeGame(TicTacToeO) next, _ := m.Update(botMoveMsg{}) m = next.(TicTacToeModel) if m.game.MovesPlayed != 1 { t.Fatalf("бот должен был сходить первым, MovesPlayed=%d", m.game.MovesPlayed) } if m.game.CurrentTurn != TicTacToeO { t.Errorf("после хода бота должна была наступить очередь человека (ноликов)") } } func TestTicTacToeModelRejectsOccupiedCellWithError(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) m.game = NewTicTacToeGame(TicTacToeX) m.game.Board[0] = TicTacToeO m.game.Board[8] = TicTacToeX // клетки заняты вручную, ход всё ещё X next, _ := m.Update(key("1")) m = next.(TicTacToeModel) if !m.isError { t.Errorf("ожидалась ошибка при попытке сходить в занятую клетку") } } func TestTicTacToeModelNewGameAfterOver(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) m.game = NewTicTacToeGame(TicTacToeX) m.game.Board = [9]TicTacToeMark{ TicTacToeX, TicTacToeX, TicTacToeX, TicTacToeO, TicTacToeO, TicTacToeEmpty, TicTacToeEmpty, TicTacToeEmpty, TicTacToeEmpty, } m.game.Phase = TicTacToePhaseOver m.game.Result = &TicTacToeResult{Winner: TicTacToeX, Line: [3]int{0, 1, 2}} next, _ := m.Update(key("n")) next2 := next.(TicTacToeModel) if next2.game.Phase != TicTacToePhasePlay { t.Errorf("после 'n' должна была начаться новая партия") } if next2.game == m.game { t.Errorf("новая партия должна была получить новое состояние, а не переиспользовать старое") } } func TestTicTacToeModelQReturnsToMenuNotQuitsApp(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) next, _ := m.Update(key("q")) m = next.(TicTacToeModel) if !m.backToMenu { t.Errorf("ожидался флаг backToMenu после 'q'") } if m.quitting { t.Errorf("'q' не должно завершать приложение целиком — только возвращать в меню") } } func TestTicTacToeModelActionsIgnoredAfterGameOver(t *testing.T) { m := NewTicTacToeModel(TicTacToeDifficultyEasy) m.game = NewTicTacToeGame(TicTacToeX) m.game.Phase = TicTacToePhaseOver m.game.Result = &TicTacToeResult{Draw: true} next, _ := m.Update(key("5")) m = next.(TicTacToeModel) if m.game.MovesPlayed != 0 { t.Errorf("ход не должен был приниматься после завершения партии") } }