Add config API endpoint for frontend game settings

Expose game configuration (energy types, card types, rule constants) via
/api/config endpoint so frontend can dynamically load game rules without
hardcoding values.
This commit is contained in:
Cal Corum 2026-01-31 15:43:41 -06:00
parent aee7ad64b4
commit 50bd3f1591
3 changed files with 150 additions and 0 deletions

38
backend/app/api/config.py Normal file
View File

@ -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()

View File

@ -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")

View File

@ -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