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.auth import router as auth_router
|
||||||
from app.api.collections import router as collections_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.decks import router as decks_router
|
||||||
from app.api.games import router as games_router
|
from app.api.games import router as games_router
|
||||||
from app.api.users import router as users_router
|
from app.api.users import router as users_router
|
||||||
@ -166,6 +167,7 @@ async def readiness_check() -> dict[str, str | int]:
|
|||||||
|
|
||||||
# === API Routers ===
|
# === API Routers ===
|
||||||
app.include_router(auth_router, prefix="/api")
|
app.include_router(auth_router, prefix="/api")
|
||||||
|
app.include_router(config_router, prefix="/api")
|
||||||
app.include_router(users_router, prefix="/api")
|
app.include_router(users_router, prefix="/api")
|
||||||
app.include_router(collections_router, prefix="/api")
|
app.include_router(collections_router, prefix="/api")
|
||||||
app.include_router(decks_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