diff --git a/backend/app/api/config.py b/backend/app/api/config.py new file mode 100644 index 0000000..efb5be5 --- /dev/null +++ b/backend/app/api/config.py @@ -0,0 +1,38 @@ +"""Configuration API router for Mantimon TCG. + +Provides endpoints for fetching game configuration. The frontend uses these +values for UI display (progress bars, limits) and includes them in requests. + +The backend is stateless - these defaults are starting points. The frontend +may customize them based on game mode (campaign, freeplay, custom) and send +the appropriate config in validation/creation requests. + +Endpoints: + GET /config/deck - Get default DeckConfig +""" + +from fastapi import APIRouter + +from app.core.config import DeckConfig + +router = APIRouter(prefix="/config", tags=["config"]) + + +@router.get("/deck", response_model=DeckConfig) +async def get_default_deck_config() -> DeckConfig: + """Get default deck building configuration. + + Returns the standard Mantimon deck rules: + - 40-card main deck + - 20-card energy deck + - 4-copy limit per card + - 1 Basic Pokemon minimum + + Frontend uses these for: + - UI hints (progress bar targets, card limit indicators) + - Default values in deck validation requests + + Returns: + DeckConfig with default Mantimon TCG rules. + """ + return DeckConfig() diff --git a/backend/app/main.py b/backend/app/main.py index 48ce2cb..ef85d20 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -20,6 +20,7 @@ from fastapi.middleware.cors import CORSMiddleware from app.api.auth import router as auth_router from app.api.collections import router as collections_router +from app.api.config import router as config_router from app.api.decks import router as decks_router from app.api.games import router as games_router from app.api.users import router as users_router @@ -166,6 +167,7 @@ async def readiness_check() -> dict[str, str | int]: # === API Routers === app.include_router(auth_router, prefix="/api") +app.include_router(config_router, prefix="/api") app.include_router(users_router, prefix="/api") app.include_router(collections_router, prefix="/api") app.include_router(decks_router, prefix="/api") diff --git a/backend/tests/api/test_config_api.py b/backend/tests/api/test_config_api.py new file mode 100644 index 0000000..d2c6aa0 --- /dev/null +++ b/backend/tests/api/test_config_api.py @@ -0,0 +1,110 @@ +"""Tests for configuration API endpoints. + +Tests the config endpoints that provide default game configurations. +These endpoints are public (no auth required) and return static defaults. +""" + +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from app.api.config import router as config_router +from app.core.config import DeckConfig + + +@pytest.fixture +def app(): + """Create a test FastAPI app with config router.""" + test_app = FastAPI() + test_app.include_router(config_router, prefix="/api") + return test_app + + +@pytest.fixture +def client(app): + """Create a test client.""" + return TestClient(app) + + +class TestGetDefaultDeckConfig: + """Tests for GET /api/config/deck endpoint.""" + + def test_returns_default_deck_config(self, client): + """ + Test that the endpoint returns the default DeckConfig values. + + The frontend uses these values for UI display (progress bars, limits) + before the user has selected a game mode or custom rules. + """ + response = client.get("/api/config/deck") + + assert response.status_code == 200 + data = response.json() + + # Verify it matches DeckConfig defaults + expected = DeckConfig() + assert data["min_size"] == expected.min_size + assert data["max_size"] == expected.max_size + assert data["max_copies_per_card"] == expected.max_copies_per_card + assert data["energy_deck_size"] == expected.energy_deck_size + assert data["min_basic_pokemon"] == expected.min_basic_pokemon + + def test_returns_all_deck_config_fields(self, client): + """ + Test that all DeckConfig fields are included in the response. + + Frontend may need any of these fields for UI or validation requests, + so the endpoint should return the complete configuration. + """ + response = client.get("/api/config/deck") + + assert response.status_code == 200 + data = response.json() + + # All DeckConfig fields should be present + expected_fields = [ + "min_size", + "max_size", + "exact_size_required", + "max_copies_per_card", + "max_copies_basic_energy", + "min_basic_pokemon", + "energy_deck_enabled", + "energy_deck_size", + "starting_hand_size", + ] + for field in expected_fields: + assert field in data, f"Missing field: {field}" + + def test_default_values_match_mantimon_rules(self, client): + """ + Test that defaults match Mantimon TCG house rules. + + Mantimon uses a 40-card main deck with 20-card energy deck, + 4-copy limit, and requires at least 1 Basic Pokemon. + """ + response = client.get("/api/config/deck") + + assert response.status_code == 200 + data = response.json() + + # Mantimon TCG defaults (not standard Pokemon TCG) + assert data["min_size"] == 40, "Main deck should be 40 cards" + assert data["max_size"] == 40, "Main deck should be exactly 40" + assert data["energy_deck_size"] == 20, "Energy deck should be 20 cards" + assert data["max_copies_per_card"] == 4, "4-copy limit per card" + assert data["min_basic_pokemon"] == 1, "At least 1 Basic Pokemon" + assert data["energy_deck_enabled"] is True, "Separate energy deck enabled" + + def test_no_authentication_required(self, client): + """ + Test that the endpoint is accessible without authentication. + + Config endpoints provide public defaults that the frontend needs + before the user has logged in or selected a game mode. + """ + # No auth headers provided + response = client.get("/api/config/deck") + + # Should succeed without auth + assert response.status_code == 200