diff --git a/crm_5_implementation.py b/crm_5_implementation.py index 36d5866..37fcfa0 100644 --- a/crm_5_implementation.py +++ b/crm_5_implementation.py @@ -1,759 +1,272 @@ -""" -Tic Tac Toe Game Implementation +import logging +from flask import Flask, render_template_string +from typing import Final -This module provides a complete implementation of a Tic Tac Toe game -with HTML, CSS, and JavaScript components. The game supports player -turns, win detection, and game state persistence. +# Configure logging for production readiness +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) -Classes: - TicTacToe: Main game class handling game logic and state management - GameBoard: Represents the game board and its state - Player: Represents a player in the game - -Functions: - main: Entry point for running the game -""" - -import json -import os -from typing import List, Optional, Tuple, Dict, Any -from enum import Enum -from dataclasses import dataclass - - -class PlayerSymbol(Enum): - """Enumeration for player symbols.""" - X = "X" - O = "O" - - -class GameStatus(Enum): - """Enumeration for game status states.""" - PLAYING = "playing" - X_WINS = "x_wins" - O_WINS = "o_wins" - DRAW = "draw" - - -@dataclass -class Player: - """Represents a player in the Tic Tac Toe game.""" - symbol: PlayerSymbol - name: str - - -class GameBoard: - """Represents the game board and its state.""" - - def __init__(self, size: int = 3): - """ - Initialize the game board. - - Args: - size: The size of the board (default 3 for 3x3) - """ - self.size = size - self.board: List[List[Optional[PlayerSymbol]]] = [ - [None for _ in range(size)] for _ in range(size) - ] - - def make_move(self, row: int, col: int, symbol: PlayerSymbol) -> bool: - """ - Make a move on the board. - - Args: - row: Row index (0-based) - col: Column index (0-based) - symbol: Player symbol to place - - Returns: - True if move was successful, False otherwise - - Raises: - IndexError: If row or column is out of bounds - """ - if not (0 <= row < self.size and 0 <= col < self.size): - raise IndexError("Move coordinates are out of bounds") - - if self.board[row][col] is not None: - return False - - self.board[row][col] = symbol - return True - - def get_cell(self, row: int, col: int) -> Optional[PlayerSymbol]: - """ - Get the symbol at a specific cell. - - Args: - row: Row index (0-based) - col: Column index (0-based) - - Returns: - The symbol at the cell or None if empty - """ - return self.board[row][col] - - def is_full(self) -> bool: - """ - Check if the board is completely filled. - - Returns: - True if board is full, False otherwise - """ - for row in self.board: - for cell in row: - if cell is None: - return False - return True - - def get_empty_cells(self) -> List[Tuple[int, int]]: - """ - Get list of all empty cells on the board. - - Returns: - List of tuples containing (row, col) of empty cells - """ - empty_cells = [] - for row in range(self.size): - for col in range(self.size): - if self.board[row][col] is None: - empty_cells.append((row, col)) - return empty_cells - - def reset(self) -> None: - """Reset the board to initial state.""" - self.board = [ - [None for _ in range(self.size)] for _ in range(self.size) - ] - - -class TicTacToe: - """Main game class handling game logic and state management.""" - - def __init__(self, player1: Player, player2: Player, board_size: int = 3): - """ - Initialize the Tic Tac Toe game. - - Args: - player1: First player - player2: Second player - board_size: Size of the game board (default 3) - """ - self.player1 = player1 - self.player2 = player2 - self.current_player = player1 - self.board = GameBoard(board_size) - self.status = GameStatus.PLAYING - self.winner: Optional[Player] = None - self.move_history: List[Tuple[int, int, PlayerSymbol]] = [] - - def make_move(self, row: int, col: int) -> bool: - """ - Make a move for the current player. - - Args: - row: Row index (0-based) - col: Column index (0-based) - - Returns: - True if move was successful, False otherwise - - Raises: - ValueError: If the game has already ended - """ - if self.status != GameStatus.PLAYING: - raise ValueError("Game has already ended") - - success = self.board.make_move(row, col, self.current_player.symbol) - - if success: - # Record the move - self.move_history.append((row, col, self.current_player.symbol)) - - # Check win condition - if self._check_win(row, col): - self.status = GameStatus.X_WINS if self.current_player.symbol == PlayerSymbol.X else GameStatus.O_WINS - self.winner = self.current_player - elif self.board.is_full(): - self.status = GameStatus.DRAW - - # Switch player - self.current_player = self.player2 if self.current_player == self.player1 else self.player1 - - return success - - def _check_win(self, row: int, col: int) -> bool: - """ - Check if the last move resulted in a win. - - Args: - row: Last move row - col: Last move column - - Returns: - True if the move resulted in a win, False otherwise - """ - symbol = self.board.get_cell(row, col) - - # Check row - if all(self.board.get_cell(row, c) == symbol for c in range(self.board.size)): - return True - - # Check column - if all(self.board.get_cell(r, col) == symbol for r in range(self.board.size)): - return True - - # Check diagonals - if row == col: - # Main diagonal - if all(self.board.get_cell(i, i) == symbol for i in range(self.board.size)): - return True - - if row + col == self.board.size - 1: - # Anti-diagonal - if all(self.board.get_cell(i, self.board.size - 1 - i) == symbol for i in range(self.board.size)): - return True - - return False - - def get_game_state(self) -> Dict[str, Any]: - """ - Get the current game state. - - Returns: - Dictionary containing game state information - """ - return { - "status": self.status.value, - "current_player_symbol": self.current_player.symbol.value, - "board": [ - [cell.value if cell else None for cell in row] - for row in self.board.board - ], - "winner": self.winner.symbol.value if self.winner else None, - "move_history": self.move_history - } - - def reset(self) -> None: - """Reset the game to initial state.""" - self.board.reset() - self.status = GameStatus.PLAYING - self.winner = None - self.current_player = self.player1 - self.move_history.clear() - - def get_winner(self) -> Optional[Player]: - """ - Get the winner of the game. - - Returns: - The winning player or None if no winner - """ - return self.winner - - def get_current_player(self) -> Player: - """ - Get the current player. - - Returns: - The current player - """ - return self.current_player - - def get_board(self) -> GameBoard: - """ - Get the game board. - - Returns: - The game board instance - """ - return self.board - - -def create_html_template(game_state: Dict[str, Any], game_id: str) -> str: +class TicTacToeApp: """ - Create an HTML template for the Tic Tac Toe game. + A production-ready Flask application serving a Tic Tac Toe game. - Args: - game_state: Current game state dictionary - game_id: Unique identifier for the game instance - - Returns: - HTML string representing the game interface + This class encapsulates the web server logic and the frontend assets + (HTML, CSS, JS) required to run a client-side Tic Tac Toe game + following the specific architectural requirements provided. """ - # Create basic HTML structure with embedded CSS and JavaScript - html_content = f""" - - - - - - Tic Tac Toe Game - - - -
-

Tic Tac Toe

-
Current Player: {game_state['current_player_symbol']}
-
- {"".join([ - f'
' - for r in range(3) for c in range(3) - ])} -
-
- - -
-
- - - - -""" - - return html_content - -def save_game_state(game: TicTacToe, file_path: str) -> None: - """ - Save the current game state to a JSON file. - - Args: - game: The TicTacToe game instance to save - file_path: Path to the file where game state should be saved - """ - try: - game_state = game.get_game_state() - - # Ensure directory exists - os.makedirs(os.path.dirname(file_path), exist_ok=True) - - with open(file_path, 'w') as f: - json.dump(game_state, f, indent=2) + # HTML/CSS/JS Content Template + # Following Architecture Design: HTML5, CSS3 (Grid/Flexbox), JS (ES6+ Module Pattern, Event Delegation) + TEMPLATE: Final[str] = """ + + + + + + CRM-5: Tic Tac Toe + + + + +
+

Tic Tac Toe

+
Player X's Turn
- except Exception as e: - raise IOError(f"Failed to save game state: {str(e)}") - +
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
-def load_game_state(file_path: str) -> Dict[str, Any]: + + + """ - Load game state from a JSON file. - - Args: - file_path: Path to the file containing game state - - Returns: - Dictionary containing loaded game state - - Raises: - FileNotFoundError: If the file does not exist - json.JSONDecodeError: If the file is not valid JSON - """ - try: - with open(file_path, 'r') as f: - return json.load(f) - except FileNotFoundError: - raise FileNotFoundError(f"Game state file not found: {file_path}") - except json.JSONDecodeError: - raise json.JSONDecodeError("Invalid JSON in game state file", "", 0) - - -def main() -> None: - """Main function to demonstrate the Tic Tac Toe game.""" - # Create players - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - # Create game instance - game = TicTacToe(player1, player2) - - # Display initial state - print("Initial Game State:") - state = game.get_game_state() - print(json.dumps(state, indent=2)) - - # Example moves - try: - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(0, 1) # X plays at (0,1) - game.make_move(1, 0) # O plays at (1,0) - game.make_move(0, 2) # X plays at (0,2) - wins - - print("\nAfter example moves:") - state = game.get_game_state() - print(json.dumps(state, indent=2)) - - # Save the game state - save_game_state(game, "games/tic_tac_toe_game.json") - print("\nGame state saved successfully.") - - except Exception as e: - print(f"Error during game execution: {e}") - - -if __name__ == "__main__": - main() -Tic Tac Toe Game Implementation (Clean Version) - -Python backend game engine with optional HTML/CSS/JS generation. -""" - -from typing import List, Optional, Dict, Any -import json - - -# ----------------------------- -# Player -# ----------------------------- -class Player: - """Represents a Tic Tac Toe player.""" - - def __init__(self, symbol: str, name: str): - if symbol not in ("X", "O"): - raise ValueError("Symbol must be 'X' or 'O'") - self.symbol = symbol - self.name = name - - -# ----------------------------- -# Game Board -# ----------------------------- -class GameBoard: - """3x3 Tic Tac Toe board.""" - - def __init__(self): - self.size = 3 - self.board = [["" for _ in range(3)] for _ in range(3)] - - def get_cell(self, row: int, col: int) -> str: - self._validate_position(row, col) - return self.board[row][col] - def set_cell(self, row: int, col: int, symbol: str) -> None: - self._validate_position(row, col) - - if symbol not in ("X", "O"): - raise ValueError("Invalid symbol") - - if self.board[row][col] != "": - raise ValueError("Cell already occupied") - - self.board[row][col] = symbol - - def clear_cell(self, row: int, col: int) -> None: - self._validate_position(row, col) - self.board[row][col] = "" - - def is_full(self) -> bool: - return all(cell != "" for row in self.board for cell in row) - - def reset(self) -> None: - self.board = [["" for _ in range(3)] for _ in range(3)] - - def to_dict(self) -> List[List[str]]: - return [row[:] for row in self.board] - - def _validate_position(self, row: int, col: int) -> None: - if not (0 <= row < 3 and 0 <= col < 3): - raise IndexError("Row and column must be between 0 and 2") - - -# ----------------------------- -# Win Checker -# ----------------------------- -class WinChecker: - """Utility class for win checking.""" - - @staticmethod - def check_winner(board: GameBoard) -> Optional[str]: - b = board.board - - lines = ( - # rows - b[0], b[1], b[2], - # columns - [b[0][0], b[1][0], b[2][0]], - [b[0][1], b[1][1], b[2][1]], - [b[0][2], b[1][2], b[2][2]], - # diagonals - [b[0][0], b[1][1], b[2][2]], - [b[0][2], b[1][1], b[2][0]], - ) - - for line in lines: - if line[0] and line.count(line[0]) == 3: - return line[0] - - return None - - -# ----------------------------- -# Game Engine -# ----------------------------- -class GameEngine: - """Main Tic Tac Toe game engine.""" - - def __init__(self): - self.players = [ - Player("X", "Player X"), - Player("O", "Player O"), - ] - self.current_player_index = 0 - self.board = GameBoard() - self.winner: Optional[str] = None - self.game_over = False - self.move_history: List[Dict[str, Any]] = [] - - def current_player(self) -> Player: - return self.players[self.current_player_index] - - def make_move(self, row: int, col: int) -> Dict[str, Any]: - if self.game_over: - return {"success": False, "error": "Game is already over"} + def __init__(self, host: str = '0.0.0.0', port: int = 5000, debug: bool = False): + """ + Initializes the TicTacToe Server. + :param host: Network interface to bind to. + :param port: Port to listen on. + :param debug: Enable/Disable Flask debug mode. + """ + self.app = Flask(__name__) + self.host = host + self.port = port + self.debug = debug + self._setup_routes() + + def _setup_routes(self) -> None: + """Configures the Flask application routes.""" + @self.app.route('/') + def index() -> str: + """Serves the main game page.""" + try: + return render_template_string(self.TEMPLATE) + except Exception as e: + logger.error(f"Error rendering template: {e}") + return "Internal Server Error", 500 + + def run(self) -> None: + """Starts the Flask production server.""" try: - symbol = self.current_player().symbol - self.board.set_cell(row, col, symbol) - - self.move_history.append( - {"row": row, "col": col, "symbol": symbol} - ) - - winner = WinChecker.check_winner(self.board) - if winner: - self.winner = winner - self.game_over = True - return self._response(f"{winner} wins!") - - if self.board.is_full(): - self.game_over = True - return self._response("Game ended in a draw") - - self._switch_player() - return self._response("Move successful") - + logger.info(f"Starting Tic Tac Toe server on {self.host}:{self.port}") + self.app.run(host=self.host, port=self.port, debug=self.debug) except Exception as e: - return {"success": False, "error": str(e)} - - def undo_move(self) -> bool: - if not self.move_history or self.game_over: - return False - - last = self.move_history.pop() - self.board.clear_cell(last["row"], last["col"]) - self._switch_player() - return True - - def reset_game(self) -> None: - self.board.reset() - self.current_player_index = 0 - self.winner = None - self.game_over = False - self.move_history.clear() - - def get_game_state(self) -> Dict[str, Any]: - return { - "board": self.board.to_dict(), - "current_player": self.current_player().name, - "winner": self.winner, - "game_over": self.game_over, - "moves": self.move_history, - } - - def _switch_player(self) -> None: - self.current_player_index = (self.current_player_index + 1) % 2 - - def _response(self, message: str) -> Dict[str, Any]: - return { - "success": True, - "message": message, - "state": self.get_game_state(), - } - - -# ----------------------------- -# Demo -# ----------------------------- -def main() -> None: - game = GameEngine() - print("Tic Tac Toe started\n") - - print(json.dumps(game.make_move(0, 0), indent=2)) - print(json.dumps(game.make_move(1, 1), indent=2)) - print(json.dumps(game.make_move(0, 1), indent=2)) - print(json.dumps(game.make_move(2, 2), indent=2)) - print(json.dumps(game.make_move(0, 2), indent=2)) # X wins - + logger.critical(f"Failed to start server: {e}") if __name__ == "__main__": - main() + # Production configuration + game_server = TicTacToeApp( + host='127.0.0.1', + port=8080, + debug=False + ) + game_server.run() \ No newline at end of file diff --git a/test_crm_5_implementation.py b/test_crm_5_implementation.py index 97d1bc5..ac32ef0 100644 --- a/test_crm_5_implementation.py +++ b/test_crm_5_implementation.py @@ -1,769 +1,157 @@ import pytest from unittest.mock import patch, MagicMock -import os -import json - -from crm_5_implementation import ( - PlayerSymbol, - GameStatus, - Player, - GameBoard, - TicTacToe, - create_html_template, - save_game_state, - load_game_state -) - - -class TestPlayerSymbol: - """Test PlayerSymbol enumeration.""" - - def test_player_symbol_values(self): - """Test that PlayerSymbol has correct values.""" - assert PlayerSymbol.X.value == "X" - assert PlayerSymbol.O.value == "O" - - -class TestPlayer: - """Test Player class.""" - - def test_player_creation(self): - """Test Player creation with valid parameters.""" - player = Player(PlayerSymbol.X, "Test Player") - assert player.symbol == PlayerSymbol.X - assert player.name == "Test Player" - - def test_player_equality(self): - """Test Player equality comparison.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.X, "Player X") - player3 = Player(PlayerSymbol.O, "Player O") - - assert player1 == player2 - assert player1 != player3 - - -class TestGameBoard: - """Test GameBoard class.""" - - def test_board_initialization(self): - """Test board initialization with default size.""" - board = GameBoard() - assert board.size == 3 - assert len(board.board) == 3 - assert all(len(row) == 3 for row in board.board) - assert all(cell is None for row in board.board for cell in row) - - def test_board_initialization_custom_size(self): - """Test board initialization with custom size.""" - board = GameBoard(5) - assert board.size == 5 - assert len(board.board) == 5 - assert all(len(row) == 5 for row in board.board) - - def test_make_move_valid(self): - """Test making a valid move.""" - board = GameBoard() - result = board.make_move(0, 0, PlayerSymbol.X) - - assert result is True - assert board.get_cell(0, 0) == PlayerSymbol.X - - def test_make_move_invalid_coordinates(self): - """Test making a move with invalid coordinates.""" - board = GameBoard() - - with pytest.raises(IndexError): - board.make_move(5, 5, PlayerSymbol.X) - - def test_make_move_occupied_cell(self): - """Test making a move on an occupied cell.""" - board = GameBoard() - board.make_move(0, 0, PlayerSymbol.X) - result = board.make_move(0, 0, PlayerSymbol.O) - - assert result is False - assert board.get_cell(0, 0) == PlayerSymbol.X - - def test_get_cell(self): - """Test getting cell value.""" - board = GameBoard() - board.make_move(1, 1, PlayerSymbol.O) - - assert board.get_cell(0, 0) is None - assert board.get_cell(1, 1) == PlayerSymbol.O - - def test_is_full_empty_board(self): - """Test is_full on empty board.""" - board = GameBoard() - assert board.is_full() is False - - def test_is_full_full_board(self): - """Test is_full on full board.""" - board = GameBoard() - for i in range(3): - for j in range(3): - board.make_move(i, j, PlayerSymbol.X) - - assert board.is_full() is True - - def test_get_empty_cells(self): - """Test getting empty cells.""" - board = GameBoard() - board.make_move(0, 0, PlayerSymbol.X) - - empty_cells = board.get_empty_cells() - assert len(empty_cells) == 8 - assert (0, 0) not in empty_cells - - def test_reset_board(self): - """Test resetting the board.""" - board = GameBoard() - board.make_move(0, 0, PlayerSymbol.X) - board.reset() - - assert board.is_full() is False - assert all(cell is None for row in board.board for cell in row) - - -class TestTicTacToe: - """Test TicTacToe class.""" - - def test_game_initialization(self): - """Test game initialization.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - assert game.player1 == player1 - assert game.player2 == player2 - assert game.current_player == player1 - assert game.status == GameStatus.PLAYING - assert game.winner is None - assert len(game.move_history) == 0 - - def test_game_initialization_custom_size(self): - """Test game initialization with custom board size.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2, 5) - - assert game.board.size == 5 - - def test_make_move_valid(self): - """Test making a valid move.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - result = game.make_move(0, 0) - - assert result is True - assert game.current_player == player2 - assert len(game.move_history) == 1 - assert game.move_history[0] == (0, 0, PlayerSymbol.X) - - def test_make_move_invalid_game_ended(self): - """Test making a move when game has ended.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - # Make moves to end the game - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(0, 1) # X plays at (0,1) - game.make_move(1, 0) # O plays at (1,0) - game.make_move(0, 2) # X plays at (0,2) - wins - - with pytest.raises(Exception): # Should raise an exception or handle properly - game.make_move(2, 2) - - def test_check_win_row(self): - """Test win condition checking for row.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - # Make moves to create a winning row - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(0, 1) # X plays at (0,1) - game.make_move(1, 0) # O plays at (1,0) - result = game.make_move(0, 2) # X plays at (0,2) - wins - - assert result is True - assert game.status == GameStatus.PLAYING # Should still be playing until next move - - def test_check_win_column(self): - """Test win condition checking for column.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - # Make moves to create a winning column - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(1, 0) # X plays at (1,0) - game.make_move(2, 1) # O plays at (2,1) - result = game.make_move(2, 0) # X plays at (2,0) - wins - - assert result is True - - def test_check_win_diagonal(self): - """Test win condition checking for diagonal.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - # Make moves to create a winning diagonal - game.make_move(0, 0) # X plays at (0,0) - game.make_move(1, 1) # O plays at (1,1) - game.make_move(1, 0) # X plays at (1,0) - game.make_move(2, 2) # O plays at (2,2) - result = game.make_move(2, 0) # X plays at (2,0) - wins - - assert result is True - - def test_get_game_state(self): - """Test getting game state.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - state = game.get_game_state() - - assert isinstance(state, dict) - assert "current_player_symbol" in state - assert "board" in state - assert "status" in state - assert "winner" in state - - def test_reset_game(self): - """Test resetting the game.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - game.make_move(0, 0) # X plays at (0,0) - - # Reset the game - # Note: There's no explicit reset method in the original code, - # but we can simulate by creating a new game instance - - game2 = TicTacToe(player1, player2) - - assert game2.current_player == player1 - assert game2.status == GameStatus.PLAYING - assert len(game2.move_history) == 0 - - def test_draw_condition(self): - """Test draw condition.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - # Fill board with alternating moves to create a draw - game.make_move(0, 0) # X plays at (0,0) - game.make_move(0, 1) # O plays at (0,1) - game.make_move(0, 2) # X plays at (0,2) - game.make_move(1, 0) # O plays at (1,0) - game.make_move(1, 1) # X plays at (1,1) - game.make_move(1, 2) # O plays at (1,2) - game.make_move(2, 0) # X plays at (2,0) - game.make_move(2, 1) # O plays at (2,1) - game.make_move(2, 2) # X plays at (2,2) - - # Check that game state is updated correctly - state = game.get_game_state() - assert state["status"] == "draw" or state["status"] == "o_wins" or state["status"] == "x_wins" - - -class TestCreateHtmlTemplate: - """Test create_html_template function.""" - - def test_create_html_template(self): - """Test creating HTML template with valid game state.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - state = game.get_game_state() - - html_content = create_html_template(state, "test_game") - - assert isinstance(html_content, str) - assert "Tic Tac Toe" in html_content - assert "Current Player:" in html_content - assert "resetGame()" in html_content - assert "saveGame()" in html_content - - -class TestSaveLoadGameState: - """Test save_game_state and load_game_state functions.""" - - def test_save_game_state(self, tmp_path): - """Test saving game state to file.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - game.make_move(0, 0) # X plays at (0,0) - - file_path = tmp_path / "test_game.json" - save_game_state(game, str(file_path)) - - assert file_path.exists() - - # Read and verify content - with open(file_path, 'r') as f: - saved_data = json.load(f) - - assert "current_player_symbol" in saved_data - assert "board" in saved_data - assert "status" in saved_data - - def test_save_game_state_error(self, tmp_path): - """Test saving game state with invalid path.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - - # Try to save to a non-writable location - with pytest.raises(IOError): - save_game_state(game, "/nonexistent/directory/game.json") - - def test_load_game_state(self, tmp_path): - """Test loading game state from file.""" - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - game = TicTacToe(player1, player2) - game.make_move(0, 0) # X plays at (0,0) - - file_path = tmp_path / "test_game.json" - save_game_state(game, str(file_path)) - - loaded_data = load_game_state(str(file_path)) - - assert isinstance(loaded_data, dict) - assert "current_player_symbol" in loaded_data - assert "board" in loaded_data - - def test_load_game_state_file_not_found(self): - """Test loading game state from non-existent file.""" - with pytest.raises(FileNotFoundError): - load_game_state("nonexistent_file.json") - - def test_load_game_state_invalid_json(self, tmp_path): - """Test loading game state from invalid JSON file.""" - file_path = tmp_path / "invalid.json" - with open(file_path, 'w') as f: - f.write("invalid json content") - - with pytest.raises(json.JSONDecodeError): - load_game_state(str(file_path)) - - -class TestMainFunction: - """Test main function.""" - - @patch('sys.stdout', new_callable=MagicMock) - def test_main_function(self, mock_stdout): - """Test main function execution.""" - # This is a basic test - in real scenario, it would execute the full main function - # We're just ensuring it doesn't crash - - try: - # Import here to avoid circular imports in test setup - from crm_5_implementation import main - - # We can't easily test the full main function without mocking more complex behavior - # But we can at least ensure it doesn't crash on basic execution - main() - - # If we get here without exception, the test passes - assert True - - except Exception as e: - # If there are exceptions, they should not be from basic functionality - # This is just a smoke test - assert "Error during game execution" not in str(e) or "main" in str(e) - - -def test_edge_cases(): - """Test edge cases for all classes and functions.""" - - # Test invalid board size - with pytest.raises(Exception): - GameBoard(-1) # This might raise an exception in real implementation - - # Test invalid player creation - with pytest.raises(Exception): - Player("invalid_symbol", "Player") # This might raise an exception in real implementation - - # Test multiple wins condition (should be handled by game logic) - - # Test large board size - large_board = GameBoard(100) - assert large_board.size == 100 - - # Test negative board size - with pytest.raises(Exception): - GameBoard(-5) - - # Test zero board size - with pytest.raises(Exception): - GameBoard(0) - - -def test_code_coverage(): - """Test that all code paths are covered.""" - - # Test that all enums have proper values - assert PlayerSymbol.X.value == "X" - assert PlayerSymbol.O.value == "O" - - # Test that all game statuses are handled - assert GameStatus.PLAYING.value == "playing" - assert GameStatus.X_WINS.value == "x_wins" - assert GameStatus.O_WINS.value == "o_wins" - assert GameStatus.DRAW.value == "draw" - - # Test that the main function can be called without error - try: - from crm_5_implementation import main - # We're not actually running main() here, just ensuring it exists - assert callable(main) - except Exception: - # If there are import errors, the test should fail gracefully - assert False - - # Test that all classes can be instantiated - player1 = Player(PlayerSymbol.X, "Player X") - player2 = Player(PlayerSymbol.O, "Player O") - - board = GameBoard() - game = TicTacToe(player1, player2) - - assert isinstance(player1, Player) - assert isinstance(board, GameBoard) - assert isinstance(game, TicTacToe) - - # Test that all methods exist - assert hasattr(board, 'make_move') - assert hasattr(board, 'get_cell') - assert hasattr(board, 'is_full') - assert hasattr(board, 'get_empty_cells') - assert hasattr(board, 'reset') - - assert hasattr(game, 'make_move') - assert hasattr(game, 'get_game_state') - assert hasattr(game, 'current_player') - assert hasattr(game, 'status') - - # Test that all constants are accessible - assert PlayerSymbol.X is not None - assert PlayerSymbol.O is not None - assert GameStatus.PLAYING is not None - assert GameStatus.X_WINS is not None - assert GameStatus.O_WINS is not None - assert GameStatus.DRAW is not None -""" -Comprehensive unit tests for Tic Tac Toe game implementation. - -Tests all classes, methods, and edge cases for the Tic Tac Toe game. -""" - -import pytest -from crm_5_implementation import TicTacToeGame, TicTacToeUI, create_tic_tac_toe_game - - -class TestTicTacToeGame: - """Test cases for the TicTacToeGame class.""" - - def test_initialization(self): - """Test that game initializes correctly.""" - game = TicTacToeGame() - assert game.board == [''] * 9 - assert game.current_player == 'X' - assert game.game_over is False - assert game.winner is None - assert game.move_count == 0 - - def test_make_move_valid_position(self): - """Test making a valid move at position 0.""" - game = TicTacToeGame() - result = game.make_move(0) - assert result is True - assert game.board[0] == 'X' - assert game.current_player == 'O' - assert game.move_count == 1 - - def test_make_move_invalid_position_negative(self): - """Test making a move at negative position.""" - game = TicTacToeGame() - with pytest.raises(ValueError): - game.make_move(-1) - - def test_make_move_invalid_position_too_high(self): - """Test making a move at position 9.""" - game = TicTacToeGame() - with pytest.raises(ValueError): - game.make_move(9) - - def test_make_move_occupied_position(self): - """Test making a move at already occupied position.""" - game = TicTacToeGame() - game.make_move(0) # X at position 0 - with pytest.raises(IndexError): - game.make_move(0) # Try to place O at same position - - def test_make_move_game_over(self): - """Test making a move after game is already over.""" - game = TicTacToeGame() - # Fill the board to make it full - for i in range(9): - game.make_move(i) - # Try to make another move - result = game.make_move(8) - assert result is False - - def test_check_win_rows(self): - """Test win detection for rows.""" - game = TicTacToeGame() - # X wins with first row - game.make_move(0) - game.make_move(3) - game.make_move(1) - game.make_move(4) - result = game.make_move(2) - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - def test_check_win_columns(self): - """Test win detection for columns.""" - game = TicTacToeGame() - # O wins with first column - game.make_move(0) - game.make_move(3) - game.make_move(1) - game.make_move(6) - result = game.make_move(2) - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - def test_check_win_diagonals(self): - """Test win detection for diagonals.""" - game = TicTacToeGame() - # X wins with diagonal - game.make_move(0) - game.make_move(1) - game.make_move(4) - game.make_move(2) - result = game.make_move(8) - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - def test_check_win_draw(self): - """Test draw detection.""" - game = TicTacToeGame() - # Fill board with alternating moves to create a draw - game.make_move(0) # X - game.make_move(1) # O - game.make_move(2) # X - game.make_move(4) # O - game.make_move(3) # X - game.make_move(5) # O - game.make_move(7) # X - game.make_move(6) # O - result = game.make_move(8) # X - should end in draw - assert result is True - assert game.game_over is True - assert game.winner == 'Draw' - - def test_reset_game(self): - """Test resetting the game.""" - game = TicTacToeGame() - game.make_move(0) - game.make_move(1) - game.reset_game() - assert game.board == [''] * 9 - assert game.current_player == 'X' - assert game.game_over is False - assert game.winner is None - assert game.move_count == 0 - - def test_get_board_state(self): - """Test getting board state.""" - game = TicTacToeGame() - game.make_move(0) - board_state = game.get_board_state() - assert board_state[0] == 'X' - assert board_state[1] == '' - # Original board should be unchanged - assert game.board[0] == 'X' - - def test_get_game_status(self): - """Test getting game status.""" - game = TicTacToeGame() - game.make_move(0) - status = game.get_game_status() - assert status['board'][0] == 'X' - assert status['current_player'] == 'O' - assert status['game_over'] is False - assert status['winner'] is None - assert status['move_count'] == 1 - - def test_alternating_players(self): - """Test that players alternate correctly.""" - game = TicTacToeGame() - game.make_move(0) - assert game.current_player == 'O' - game.make_move(1) - assert game.current_player == 'X' - game.make_move(2) - assert game.current_player == 'O' - - -class TestTicTacToeUI: - """Test cases for the TicTacToeUI class.""" - - def test_ui_initialization(self): - """Test UI initialization.""" - ui = TicTacToeUI() - assert isinstance(ui.game, TicTacToeGame) - - def test_generate_html(self): - """Test HTML generation.""" - ui = TicTacToeUI() - html_content = ui.generate_html() - assert '' in html_content - assert 'Tic Tac Toe' in html_content - assert '
' in html_content - - def test_generate_css(self): - """Test CSS generation.""" - ui = TicTacToeUI() - css_content = ui.generate_css() - assert 'body {' in css_content - assert '.cell {' in css_content - assert '@media' in css_content - - def test_generate_javascript(self): - """Test JavaScript generation.""" - ui = TicTacToeUI() - js_content = ui.generate_javascript() - assert 'class TicTacToeGame' in js_content - assert 'makeMove' in js_content - assert 'updateUI' in js_content - - -def test_create_tic_tac_toe_game(): - """Test creating complete game package.""" - result = create_tic_tac_toe_game() - assert isinstance(result, dict) - assert 'html' in result - assert 'css' in result - assert 'js' in result - assert len(result['html']) > 0 - assert len(result['css']) > 0 - assert len(result['js']) > 0 - - -def test_main_function(): - """Test main function runs without errors.""" - # This is mainly for code coverage and to ensure the function can run - # The actual content is tested in other tests - pass - - -class TestEdgeCases: - """Test edge cases for the game.""" - - def test_multiple_wins_same_row(self): - """Test that win is detected correctly even with multiple moves.""" - game = TicTacToeGame() - # Create a winning condition - game.make_move(0) # X - game.make_move(3) # O - game.make_move(1) # X - game.make_move(4) # O - result = game.make_move(2) # X - should win - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - def test_full_board_draw(self): - """Test draw with specific board arrangement.""" - game = TicTacToeGame() - # Create a full board that should end in draw - moves = [0, 1, 2, 4, 3, 5, 7, 6, 8] # X, O, X, O, X, O, X, O, X - for i, move in enumerate(moves): - game.make_move(move) - assert game.game_over is True - assert game.winner == 'Draw' - - def test_empty_board(self): - """Test that initial board is empty.""" - game = TicTacToeGame() - assert all(cell == '' for cell in game.board) - - def test_invalid_move_after_win(self): - """Test making move after win.""" - game = TicTacToeGame() - # Create a win condition - game.make_move(0) # X - game.make_move(3) # O - game.make_move(1) # X - game.make_move(4) # O - game.make_move(2) # X - win - # Try to make another move after win - result = game.make_move(5) - assert result is False - - def test_multiple_resets(self): - """Test resetting multiple times.""" - game = TicTacToeGame() - game.make_move(0) - game.reset_game() - game.reset_game() # Second reset - assert game.board == [''] * 9 - assert game.current_player == 'X' - - def test_win_with_different_patterns(self): - """Test win detection with different patterns.""" - game = TicTacToeGame() - - # Test column win - game.make_move(0) # X - game.make_move(1) # O - game.make_move(3) # X - game.make_move(4) # O - result = game.make_move(6) # X - win column 0 - assert result is True - assert game.game_over is True - assert game.winner == 'X' - - # Reset and test diagonal win - game.reset_game() - game.make_move(0) # X - game.make_move(1) # O - game.make_move(4) # X - game.make_move(2) # O - result = game.make_move(8) # X - win diagonal - assert result is True - assert game.game_over is True - assert game.winner == 'X' +from flask import Flask +from crm_5_implementation import TicTacToeApp + +@pytest.fixture +def app_instance(): + """Fixture to provide a TicTacToeApp instance for testing.""" + return TicTacToeApp(host='127.0.0.1', port=8080, debug=False) + +@pytest.fixture +def client(app_instance): + """Fixture to provide a Flask test client.""" + app_instance.app.config['TESTING'] = True + with app_instance.app.test_client() as client: + yield client + +def test_initialization(app_instance): + """ + Test that the TicTacToeApp initializes with correct attributes. + """ + assert app_instance.host == '127.0.0.1' + assert app_instance.port == 8080 + assert app_instance.debug is False + assert isinstance(app_instance.app, Flask) + +def test_index_route_success(client): + """ + Test the index route ('/') to ensure it returns 200 OK and contains expected HTML content. + """ + response = client.get('/') + assert response.status_code == 200 + html_content = response.data.decode('utf-8') + assert 'CRM-5: Tic Tac Toe' in html_content + assert 'id="board"' in html_content + assert 'class="cell"' in html_content + assert 'id="reset-btn"' in html_content + assert 'const TicTacToe =' in html_content + +def test_index_route_template_rendering_failure(app_instance): + """ + Test the index route error handling when template rendering fails. + """ + with patch('crm_5_implementation.render_template_string') as mocked_render: + mocked_render.side_effect = Exception("Template Error") + + with app_instance.app.test_client() as client: + response = client.get('/') + assert response.status_code == 500 + assert b"Internal Server Error" in response.data + +def test_run_method_success(app_instance): + """ + Test the run method to ensure it calls Flask's run with correct arguments. + """ + with patch.object(app_instance.app, 'run') as mocked_run: + app_instance.run() + mocked_run.assert_called_once_with( + host='127.0.0.1', + port=8080, + debug=False + ) + +def test_run_method_exception(app_instance): + """ + Test the run method's exception handling when Flask fails to start. + """ + with patch.object(app_instance.app, 'run') as mocked_run: + mocked_run.side_effect = Exception("Port already in use") + with patch('crm_5_implementation.logger.critical') as mocked_logger: + app_instance.run() + mocked_logger.assert_called_once() + assert "Failed to start server" in mocked_logger.call_args[0][0] + +def test_template_content_integrity(app_instance): + """ + Test that the TEMPLATE constant contains essential game logic and styles. + """ + template = app_instance.TEMPLATE + # Check CSS variables + assert '--x-color' in template + assert '--o-color' in template + # Check Grid layout + assert 'display: grid' in template + # Check JS Logic + assert 'winningConditions' in template + assert 'handleCellClick' in template + assert 'restartGame' in template + # Check Event Listeners + assert "addEventListener('click', TicTacToe.handleCellClick)" in template + +def test_custom_config_initialization(): + """ + Test initialization with non-default host and port. + """ + custom_app = TicTacToeApp(host='192.168.1.1', port=9000, debug=True) + assert custom_app.host == '192.168.1.1' + assert custom_app.port == 9000 + assert custom_app.debug is True + +def test_index_route_contains_all_cells(client): + """ + Verify that the rendered HTML contains all 9 cells for the Tic Tac Toe board. + """ + response = client.get('/') + html_content = response.data.decode('utf-8') + for i in range(9): + assert f'data-index="{i}"' in html_content + +@pytest.mark.parametrize("element", [ + "status-indicator", + "game-board", + "cell", + "controls", + "reset-btn" +]) +def test_ui_elements_presence(client, element): + """ + Verify the presence of essential UI class names and IDs in the response. + """ + response = client.get('/') + assert element.encode() in response.data + +def test_logger_info_on_run(app_instance): + """ + Verify that the server logs an info message when starting. + """ + with patch.object(app_instance.app, 'run'): + with patch('crm_5_implementation.logger.info') as mocked_info: + app_instance.run() + mocked_info.assert_called() + assert "Starting Tic Tac Toe server" in mocked_info.call_args[0][0] + +def test_route_registration(app_instance): + """ + Verify that the '/' route is correctly registered in the Flask app. + """ + rules = [rule.rule for rule in app_instance.app.url_map.iter_rules()] + assert '/' in rules + +def test_template_is_final_string(app_instance): + """ + Ensure the TEMPLATE is a string and not empty. + """ + assert isinstance(app_instance.TEMPLATE, str) + assert len(app_instance.TEMPLATE) > 0 + +def test_internal_server_error_logging(app_instance): + """ + Verify that template rendering errors are logged as errors. + """ + with patch('crm_5_implementation.render_template_string', side_effect=Exception("Render fail")): + with patch('crm_5_implementation.logger.error') as mocked_error: + with app_instance.app.test_client() as client: + client.get('/') + mocked_error.assert_called_once() + assert "Error rendering template" in mocked_error.call_args[0][0] \ No newline at end of file diff --git a/test_integration.py b/test_integration.py index 167927c..86a0718 100644 --- a/test_integration.py +++ b/test_integration.py @@ -1,279 +1,109 @@ -""" -Integration tests for Tic Tac Toe Game Implementation - -This module contains integration tests that verify the complete -functionality of the Tic Tac Toe game system, including -component interactions, API endpoints, and database operations. -""" - import pytest -import json -from unittest.mock import patch, MagicMock -from crm_5_implementation import TicTacToe, GameBoard, Player - +from crm_5_implementation import TicTacToeApp @pytest.fixture(scope="module") -def game_instance(): - """Create a Tic Tac Toe game instance for testing.""" - return TicTacToe() - - -@pytest.fixture(scope="function") -def clean_game_board(): - """Create a fresh game board instance for each test.""" - return GameBoard() - - -@pytest.fixture(scope="function") -def player_x(): - """Create a player X instance.""" - return Player("X") - - -@pytest.fixture(scope="function") -def player_o(): - """Create a player O instance.""" - return Player("O") - - -@pytest.fixture(autouse=True) -def setup_and_teardown(): - """Setup and teardown for all tests.""" - # Setup - yield - - # Teardown - clean up any test data if needed - pass - - -def test_game_initialization(game_instance): - """Test that the game initializes correctly.""" - assert game_instance is not None - assert hasattr(game_instance, 'board') - assert hasattr(game_instance, 'current_player') - assert hasattr(game_instance, 'players') - assert len(game_instance.players) == 2 - - -def test_board_creation(clean_game_board): - """Test that game board is created with correct dimensions.""" - assert clean_game_board is not None - assert len(clean_game_board.board) == 3 - assert all(len(row) == 3 for row in clean_game_board.board) - assert all(cell == '' for row in clean_game_board.board for cell in row) - - -def test_player_creation(player_x, player_o): - """Test that players are created with correct symbols.""" - assert player_x.symbol == "X" - assert player_o.symbol == "O" - - -def test_game_board_state_management(clean_game_board): - """Test board state management operations.""" - # Test initial state - assert clean_game_board.get_state() == [['', '', ''], ['', '', ''], ['', '', '']] - - # Test making a move - clean_game_board.make_move(0, 0, "X") - state = clean_game_board.get_state() - assert state[0][0] == "X" - assert state[0][1] == "" - assert state[0][2] == "" - - -def test_game_turn_management(game_instance, player_x, player_o): - """Test that game manages turns correctly.""" - # Test initial player - assert game_instance.current_player == player_x - - # Test turn switching - game_instance.switch_player() - assert game_instance.current_player == player_o - - # Test turn switching back - game_instance.switch_player() - assert game_instance.current_player == player_x - - -def test_game_win_detection(clean_game_board): - """Test win detection logic.""" - # Test horizontal win - clean_game_board.board = [["X", "X", "X"], ["", "", ""], ["", "", ""]] - assert clean_game_board.check_win() == "X" - - # Test vertical win - clean_game_board.board = [["O", "", ""], ["O", "", ""], ["O", "", ""]] - assert clean_game_board.check_win() == "O" - - # Test diagonal win - clean_game_board.board = [["X", "", ""], ["", "X", ""], ["", "", "X"]] - assert clean_game_board.check_win() == "X" - - # Test no win - clean_game_board.board = [["X", "O", "X"], ["O", "X", "O"], ["O", "X", "O"]] - assert clean_game_board.check_win() is None - - -def test_game_draw_detection(clean_game_board): - """Test draw detection logic.""" - # Test draw scenario - clean_game_board.board = [["X", "O", "X"], ["O", "X", "O"], ["O", "X", "O"]] - assert clean_game_board.check_draw() is True - - # Test ongoing game - clean_game_board.board = [["X", "O", ""], ["O", "X", ""], ["O", "X", ""]] - assert clean_game_board.check_draw() is False - - -def test_game_reset_functionality(game_instance): - """Test that game can be reset properly.""" - # Make some moves - game_instance.board.make_move(0, 0, "X") - - # Reset game - game_instance.reset_game() - - # Verify reset - assert game_instance.current_player == game_instance.players[0] - assert game_instance.board.get_state() == [['', '', ''], ['', '', ''], ['', '', '']] - - -def test_player_symbol_assignment(): - """Test that players are assigned correct symbols.""" - game = TicTacToe() - - assert game.players[0].symbol == "X" - assert game.players[1].symbol == "O" - - -def test_complete_game_scenario(): - """Test a complete game scenario with multiple moves.""" - game = TicTacToe() - - # Player X makes first move - game.make_move(0, 0, "X") - assert game.current_player.symbol == "O" - - # Player O makes second move - game.make_move(1, 1, "O") - assert game.current_player.symbol == "X" - - # Player X makes third move - game.make_move(0, 1, "X") - assert game.current_player.symbol == "O" - - # Player O makes fourth move - game.make_move(1, 0, "O") - assert game.current_player.symbol == "X" - - # Player X makes fifth move (winning move) - game.make_move(0, 2, "X") - - # Verify win condition - assert game.check_game_status() == "X" - - -def test_game_state_serialization(): - """Test that game state can be serialized and deserialized.""" - game = TicTacToe() - - # Make some moves - game.make_move(0, 0, "X") - game.make_move(1, 1, "O") - - # Serialize state - state = game.serialize_state() - assert isinstance(state, str) - - # Deserialize state - new_game = TicTacToe() - new_game.deserialize_state(state) - - # Verify state is restored - assert new_game.board.get_state()[0][0] == "X" - assert new_game.board.get_state()[1][1] == "O" - - -def test_multiple_game_instances(): - """Test that multiple game instances can exist independently.""" - game1 = TicTacToe() - game2 = TicTacToe() - - # Make moves in first game - game1.make_move(0, 0, "X") - - # Verify second game is unaffected - assert game2.board.get_state()[0][0] == "" - - # Verify first game state - assert game1.board.get_state()[0][0] == "X" - - -def test_invalid_move_handling(clean_game_board): - """Test handling of invalid moves.""" - # Test move on occupied cell - clean_game_board.make_move(0, 0, "X") - result = clean_game_board.make_move(0, 0, "O") - - # Should not allow move on occupied cell - assert result is False - - # Verify original move still there - assert clean_game_board.board[0][0] == "X" - - -def test_edge_cases(): - """Test edge cases in game logic.""" - game = TicTacToe() - - # Test moves outside board bounds - assert game.make_move(-1, 0, "X") is False - assert game.make_move(3, 0, "X") is False - assert game.make_move(0, -1, "X") is False - assert game.make_move(0, 3, "X") is False - - # Test invalid player symbols - assert game.make_move(0, 0, "Invalid") is False - - -def test_game_status_end_conditions(): - """Test various end game conditions.""" - # Test win condition - game = TicTacToe() - game.board.board = [["X", "X", "X"], ["", "", ""], ["", "", ""]] - assert game.check_game_status() == "X" - - # Test draw condition - game = TicTacToe() - game.board.board = [["X", "O", "X"], ["O", "X", "O"], ["O", "X", "O"]] - assert game.check_game_status() == "draw" - - # Test ongoing game - game = TicTacToe() - game.board.board = [["X", "", ""], ["", "", ""], ["", "", ""]] - assert game.check_game_status() is None - - -def test_concurrent_games(): - """Test that multiple concurrent games work correctly.""" - game1 = TicTacToe() - game2 = TicTacToe() - - # Play first game to win - game1.make_move(0, 0, "X") - game1.make_move(0, 1, "X") - game1.make_move(0, 2, "X") - - # Play second game to draw - game2.make_move(0, 0, "X") - game2.make_move(1, 1, "O") - game2.make_move(0, 1, "O") - game2.make_move(1, 0, "X") - game2.make_move(2, 2, "O") - - # Verify first game has win - assert game1.check_game_status() == "X" - - # Verify second game has draw - assert game2.check_game_status() == "draw" +def app_instance(): + """ + Setup fixture for the TicTacToeApp instance. + Ensures the Flask application is configured for testing mode. + """ + # Initialize the application class + service = TicTacToeApp() + # Access the Flask app instance encapsulated within the class + flask_app = service.app + flask_app.config.update({ + "TESTING": True, + }) + return flask_app + +@pytest.fixture +def client(app_instance): + """ + Fixture to provide a test client for the Flask application. + Handles setup and teardown of the test client context. + """ + with app_instance.test_client() as client: + yield client + +def test_index_route_integration(client): + """ + Test the integration between the Flask route and the template rendering engine. + Verifies that the root endpoint returns a 200 OK status and serves the HTML content. + """ + response = client.get('/') + assert response.status_code == 200 + + html_content = response.data.decode('utf-8') + + # Verify core HTML structure is present + assert "" in html_content + assert "Tic Tac Toe" in html_content + assert "html" in html_content.lower() + +def test_frontend_assets_integration(client): + """ + Verify that CSS and JavaScript components are correctly integrated into the response. + Since this is a client-side game, these assets should be embedded or linked. + """ + response = client.get('/') + html_content = response.data.decode('utf-8') + + # Check for CSS integration (style tags or links) + assert "