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:
parent
aee7ad64b4
commit
50bd3f1591
38
backend/app/api/config.py
Normal file
38
backend/app/api/config.py
Normal 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()
|
||||
@ -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")
|
||||
|
||||
110
backend/tests/api/test_config_api.py
Normal file
110
backend/tests/api/test_config_api.py
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user