feat: auth-aware UI and role-based access control #67

Merged
TheFurya merged 10 commits from feature/auth-aware-ui-and-role-based-access-control into develop 2026-03-21 11:44:07 +01:00
4 changed files with 269 additions and 242 deletions
Showing only changes of commit c17e776408 - Show all commits

View File

@@ -7,7 +7,7 @@ from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
import app.models # noqa: F401 — ensures all models register with Base.metadata
from app.core.auth import AuthUser, get_current_user
from app.core.auth import AuthUser, get_current_user, require_admin
from app.core.database import Base, get_session
from app.main import app
@@ -70,7 +70,11 @@ async def client(db_session):
@pytest.fixture
def mock_auth_user():
"""Return a mock authenticated user for tests."""
return AuthUser(id="test-user-123", email="test@example.com", role="authenticated")
return AuthUser(
id="00000000-0000-4000-a000-000000000001",
email="test@example.com",
role="authenticated",
)
@pytest.fixture
@@ -94,11 +98,34 @@ async def auth_client(db_session, auth_override):
yield ac
@pytest.fixture
def admin_override(mock_auth_user):
"""Override require_admin and get_current_user to return a mock user."""
def _override():
return mock_auth_user
app.dependency_overrides[require_admin] = _override
app.dependency_overrides[get_current_user] = _override
yield
app.dependency_overrides.pop(require_admin, None)
app.dependency_overrides.pop(get_current_user, None)
@pytest.fixture
async def admin_client(db_session, admin_override):
"""Async HTTP client with mocked admin authentication."""
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
yield ac
@pytest.fixture
def valid_token():
"""Generate a valid JWT token for testing."""
payload = {
"sub": "test-user-123",
"sub": "00000000-0000-4000-a000-000000000001",
"email": "test@example.com",
"role": "authenticated",
"aud": "authenticated",

View File

@@ -17,9 +17,9 @@ GAME_PAYLOAD = {
@pytest.fixture
async def game(auth_client: AsyncClient) -> dict:
async def game(admin_client: AsyncClient) -> dict:
"""A game created via the API (no version_group_id)."""
response = await auth_client.post(BASE, json=GAME_PAYLOAD)
response = await admin_client.post(BASE, json=GAME_PAYLOAD)
assert response.status_code == 201
return response.json()
@@ -68,8 +68,8 @@ class TestListGames:
class TestCreateGame:
async def test_creates_and_returns_game(self, auth_client: AsyncClient):
response = await auth_client.post(BASE, json=GAME_PAYLOAD)
async def test_creates_and_returns_game(self, admin_client: AsyncClient):
response = await admin_client.post(BASE, json=GAME_PAYLOAD)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Pokemon Red"
@@ -77,15 +77,15 @@ class TestCreateGame:
assert isinstance(data["id"], int)
async def test_duplicate_slug_returns_409(
self, auth_client: AsyncClient, game: dict
self, admin_client: AsyncClient, game: dict
):
response = await auth_client.post(
response = await admin_client.post(
BASE, json={**GAME_PAYLOAD, "name": "Pokemon Red v2"}
)
assert response.status_code == 409
async def test_missing_required_field_returns_422(self, auth_client: AsyncClient):
response = await auth_client.post(BASE, json={"name": "Pokemon Red"})
async def test_missing_required_field_returns_422(self, admin_client: AsyncClient):
response = await admin_client.post(BASE, json={"name": "Pokemon Red"})
assert response.status_code == 422
@@ -115,35 +115,35 @@ class TestGetGame:
class TestUpdateGame:
async def test_updates_name(self, auth_client: AsyncClient, game: dict):
response = await auth_client.put(
async def test_updates_name(self, admin_client: AsyncClient, game: dict):
response = await admin_client.put(
f"{BASE}/{game['id']}", json={"name": "Pokemon Blue"}
)
assert response.status_code == 200
assert response.json()["name"] == "Pokemon Blue"
async def test_slug_unchanged_on_partial_update(
self, auth_client: AsyncClient, game: dict
self, admin_client: AsyncClient, game: dict
):
response = await auth_client.put(
response = await admin_client.put(
f"{BASE}/{game['id']}", json={"name": "New Name"}
)
assert response.json()["slug"] == "red"
async def test_not_found_returns_404(self, auth_client: AsyncClient):
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (
await auth_client.put(f"{BASE}/9999", json={"name": "x"})
await admin_client.put(f"{BASE}/9999", json={"name": "x"})
).status_code == 404
async def test_duplicate_slug_returns_409(self, auth_client: AsyncClient):
await auth_client.post(
async def test_duplicate_slug_returns_409(self, admin_client: AsyncClient):
await admin_client.post(
BASE, json={**GAME_PAYLOAD, "slug": "blue", "name": "Blue"}
)
r1 = await auth_client.post(
r1 = await admin_client.post(
BASE, json={**GAME_PAYLOAD, "slug": "red", "name": "Red"}
)
game_id = r1.json()["id"]
response = await auth_client.put(f"{BASE}/{game_id}", json={"slug": "blue"})
response = await admin_client.put(f"{BASE}/{game_id}", json={"slug": "blue"})
assert response.status_code == 409
@@ -153,13 +153,13 @@ class TestUpdateGame:
class TestDeleteGame:
async def test_deletes_game(self, auth_client: AsyncClient, game: dict):
response = await auth_client.delete(f"{BASE}/{game['id']}")
async def test_deletes_game(self, admin_client: AsyncClient, game: dict):
response = await admin_client.delete(f"{BASE}/{game['id']}")
assert response.status_code == 204
assert (await auth_client.get(f"{BASE}/{game['id']}")).status_code == 404
assert (await admin_client.get(f"{BASE}/{game['id']}")).status_code == 404
async def test_not_found_returns_404(self, auth_client: AsyncClient):
assert (await auth_client.delete(f"{BASE}/9999")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.delete(f"{BASE}/9999")).status_code == 404
# ---------------------------------------------------------------------------
@@ -195,9 +195,9 @@ class TestListByRegion:
class TestCreateRoute:
async def test_creates_route(self, auth_client: AsyncClient, game_with_vg: tuple):
async def test_creates_route(self, admin_client: AsyncClient, game_with_vg: tuple):
game_id, _ = game_with_vg
response = await auth_client.post(
response = await admin_client.post(
f"{BASE}/{game_id}/routes",
json={"name": "Pallet Town", "order": 1},
)
@@ -208,35 +208,35 @@ class TestCreateRoute:
assert isinstance(data["id"], int)
async def test_game_detail_includes_route(
self, auth_client: AsyncClient, game_with_vg: tuple
self, admin_client: AsyncClient, game_with_vg: tuple
):
game_id, _ = game_with_vg
await auth_client.post(
await admin_client.post(
f"{BASE}/{game_id}/routes", json={"name": "Route 1", "order": 1}
)
response = await auth_client.get(f"{BASE}/{game_id}")
response = await admin_client.get(f"{BASE}/{game_id}")
routes = response.json()["routes"]
assert len(routes) == 1
assert routes[0]["name"] == "Route 1"
async def test_game_without_version_group_returns_400(
self, auth_client: AsyncClient, game: dict
self, admin_client: AsyncClient, game: dict
):
response = await auth_client.post(
response = await admin_client.post(
f"{BASE}/{game['id']}/routes",
json={"name": "Route 1", "order": 1},
)
assert response.status_code == 400
async def test_list_routes_excludes_routes_without_encounters(
self, auth_client: AsyncClient, game_with_vg: tuple
self, admin_client: AsyncClient, game_with_vg: tuple
):
"""list_game_routes only returns routes that have Pokemon encounters."""
game_id, _ = game_with_vg
await auth_client.post(
await admin_client.post(
f"{BASE}/{game_id}/routes", json={"name": "Route 1", "order": 1}
)
response = await auth_client.get(f"{BASE}/{game_id}/routes?flat=true")
response = await admin_client.get(f"{BASE}/{game_id}/routes?flat=true")
assert response.status_code == 200
assert response.json() == []
@@ -248,15 +248,15 @@ class TestCreateRoute:
class TestUpdateRoute:
async def test_updates_route_name(
self, auth_client: AsyncClient, game_with_vg: tuple
self, admin_client: AsyncClient, game_with_vg: tuple
):
game_id, _ = game_with_vg
r = (
await auth_client.post(
await admin_client.post(
f"{BASE}/{game_id}/routes", json={"name": "Old Name", "order": 1}
)
).json()
response = await auth_client.put(
response = await admin_client.put(
f"{BASE}/{game_id}/routes/{r['id']}",
json={"name": "New Name"},
)
@@ -264,11 +264,11 @@ class TestUpdateRoute:
assert response.json()["name"] == "New Name"
async def test_route_not_found_returns_404(
self, auth_client: AsyncClient, game_with_vg: tuple
self, admin_client: AsyncClient, game_with_vg: tuple
):
game_id, _ = game_with_vg
assert (
await auth_client.put(f"{BASE}/{game_id}/routes/9999", json={"name": "x"})
await admin_client.put(f"{BASE}/{game_id}/routes/9999", json={"name": "x"})
).status_code == 404
@@ -278,26 +278,26 @@ class TestUpdateRoute:
class TestDeleteRoute:
async def test_deletes_route(self, auth_client: AsyncClient, game_with_vg: tuple):
async def test_deletes_route(self, admin_client: AsyncClient, game_with_vg: tuple):
game_id, _ = game_with_vg
r = (
await auth_client.post(
await admin_client.post(
f"{BASE}/{game_id}/routes", json={"name": "Route 1", "order": 1}
)
).json()
assert (
await auth_client.delete(f"{BASE}/{game_id}/routes/{r['id']}")
await admin_client.delete(f"{BASE}/{game_id}/routes/{r['id']}")
).status_code == 204
# No longer in game detail
detail = (await auth_client.get(f"{BASE}/{game_id}")).json()
detail = (await admin_client.get(f"{BASE}/{game_id}")).json()
assert all(route["id"] != r["id"] for route in detail["routes"])
async def test_route_not_found_returns_404(
self, auth_client: AsyncClient, game_with_vg: tuple
self, admin_client: AsyncClient, game_with_vg: tuple
):
game_id, _ = game_with_vg
assert (
await auth_client.delete(f"{BASE}/{game_id}/routes/9999")
await admin_client.delete(f"{BASE}/{game_id}/routes/9999")
).status_code == 404
@@ -307,20 +307,20 @@ class TestDeleteRoute:
class TestReorderRoutes:
async def test_reorders_routes(self, auth_client: AsyncClient, game_with_vg: tuple):
async def test_reorders_routes(self, admin_client: AsyncClient, game_with_vg: tuple):
game_id, _ = game_with_vg
r1 = (
await auth_client.post(
await admin_client.post(
f"{BASE}/{game_id}/routes", json={"name": "A", "order": 1}
)
).json()
r2 = (
await auth_client.post(
await admin_client.post(
f"{BASE}/{game_id}/routes", json={"name": "B", "order": 2}
)
).json()
response = await auth_client.put(
response = await admin_client.put(
f"{BASE}/{game_id}/routes/reorder",
json={
"routes": [{"id": r1["id"], "order": 2}, {"id": r2["id"], "order": 1}]

View File

@@ -55,7 +55,7 @@ async def games_ctx(db_session: AsyncSession) -> dict:
@pytest.fixture
async def ctx(db_session: AsyncSession, client: AsyncClient, games_ctx: dict) -> dict:
async def ctx(db_session: AsyncSession, admin_client: AsyncClient, games_ctx: dict) -> dict:
"""Full context: routes + pokemon + genlocke + encounter for advance/transfer tests."""
route1 = Route(name="GT Route 1", version_group_id=games_ctx["vg1_id"], order=1)
route2 = Route(name="GT Route 2", version_group_id=games_ctx["vg2_id"], order=1)
@@ -67,7 +67,7 @@ async def ctx(db_session: AsyncSession, client: AsyncClient, games_ctx: dict) ->
db_session.add(pikachu)
await db_session.commit()
r = await client.post(
r = await admin_client.post(
GENLOCKES_BASE,
json={
"name": "Test Genlocke",
@@ -80,7 +80,7 @@ async def ctx(db_session: AsyncSession, client: AsyncClient, games_ctx: dict) ->
leg1 = next(leg for leg in genlocke["legs"] if leg["legOrder"] == 1)
run_id = leg1["runId"]
enc_r = await client.post(
enc_r = await admin_client.post(
f"{RUNS_BASE}/{run_id}/encounters",
json={"routeId": route1.id, "pokemonId": pikachu.id, "status": "caught"},
)
@@ -104,13 +104,13 @@ async def ctx(db_session: AsyncSession, client: AsyncClient, games_ctx: dict) ->
class TestListGenlockes:
async def test_empty_returns_empty_list(self, client: AsyncClient):
response = await client.get(GENLOCKES_BASE)
async def test_empty_returns_empty_list(self, admin_client: AsyncClient):
response = await admin_client.get(GENLOCKES_BASE)
assert response.status_code == 200
assert response.json() == []
async def test_returns_created_genlocke(self, client: AsyncClient, ctx: dict):
response = await client.get(GENLOCKES_BASE)
async def test_returns_created_genlocke(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.get(GENLOCKES_BASE)
assert response.status_code == 200
names = [g["name"] for g in response.json()]
assert "Test Genlocke" in names
@@ -123,9 +123,9 @@ class TestListGenlockes:
class TestCreateGenlocke:
async def test_creates_with_legs_and_first_run(
self, client: AsyncClient, games_ctx: dict
self, admin_client: AsyncClient, games_ctx: dict
):
response = await client.post(
response = await admin_client.post(
GENLOCKES_BASE,
json={
"name": "My Genlocke",
@@ -144,14 +144,14 @@ class TestCreateGenlocke:
leg2 = next(leg for leg in data["legs"] if leg["legOrder"] == 2)
assert leg2["runId"] is None
async def test_empty_game_ids_returns_400(self, client: AsyncClient):
response = await client.post(
async def test_empty_game_ids_returns_400(self, admin_client: AsyncClient):
response = await admin_client.post(
GENLOCKES_BASE, json={"name": "Bad", "gameIds": []}
)
assert response.status_code == 400
async def test_invalid_game_id_returns_404(self, client: AsyncClient):
response = await client.post(
async def test_invalid_game_id_returns_404(self, admin_client: AsyncClient):
response = await admin_client.post(
GENLOCKES_BASE, json={"name": "Bad", "gameIds": [9999]}
)
assert response.status_code == 404
@@ -164,9 +164,9 @@ class TestCreateGenlocke:
class TestGetGenlocke:
async def test_returns_genlocke_with_legs_and_stats(
self, client: AsyncClient, ctx: dict
self, admin_client: AsyncClient, ctx: dict
):
response = await client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}")
response = await admin_client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}")
assert response.status_code == 200
data = response.json()
assert data["id"] == ctx["genlocke_id"]
@@ -174,8 +174,8 @@ class TestGetGenlocke:
assert "stats" in data
assert data["stats"]["totalLegs"] == 2
async def test_not_found_returns_404(self, client: AsyncClient):
assert (await client.get(f"{GENLOCKES_BASE}/9999")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.get(f"{GENLOCKES_BASE}/9999")).status_code == 404
# ---------------------------------------------------------------------------
@@ -184,30 +184,30 @@ class TestGetGenlocke:
class TestUpdateGenlocke:
async def test_updates_name(self, client: AsyncClient, ctx: dict):
response = await client.patch(
async def test_updates_name(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.patch(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}", json={"name": "Renamed"}
)
assert response.status_code == 200
assert response.json()["name"] == "Renamed"
async def test_not_found_returns_404(self, client: AsyncClient):
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (
await client.patch(f"{GENLOCKES_BASE}/9999", json={"name": "x"})
await admin_client.patch(f"{GENLOCKES_BASE}/9999", json={"name": "x"})
).status_code == 404
class TestDeleteGenlocke:
async def test_deletes_genlocke(self, client: AsyncClient, ctx: dict):
async def test_deletes_genlocke(self, admin_client: AsyncClient, ctx: dict):
assert (
await client.delete(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}")
await admin_client.delete(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}")
).status_code == 204
assert (
await client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}")
await admin_client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}")
).status_code == 404
async def test_not_found_returns_404(self, client: AsyncClient):
assert (await client.delete(f"{GENLOCKES_BASE}/9999")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.delete(f"{GENLOCKES_BASE}/9999")).status_code == 404
# ---------------------------------------------------------------------------
@@ -216,8 +216,8 @@ class TestDeleteGenlocke:
class TestGenlockeLegs:
async def test_adds_leg(self, client: AsyncClient, ctx: dict):
response = await client.post(
async def test_adds_leg(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.post(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs",
json={"gameId": ctx["game1_id"]},
)
@@ -225,28 +225,28 @@ class TestGenlockeLegs:
legs = response.json()["legs"]
assert len(legs) == 3 # was 2, now 3
async def test_remove_leg_without_run(self, client: AsyncClient, ctx: dict):
async def test_remove_leg_without_run(self, admin_client: AsyncClient, ctx: dict):
# Leg 2 has no run yet — can be removed
leg2 = next(leg for leg in ctx["genlocke"]["legs"] if leg["legOrder"] == 2)
response = await client.delete(
response = await admin_client.delete(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/{leg2['id']}"
)
assert response.status_code == 204
async def test_remove_leg_with_run_returns_400(
self, client: AsyncClient, ctx: dict
self, admin_client: AsyncClient, ctx: dict
):
# Leg 1 has a run — cannot remove
leg1 = next(leg for leg in ctx["genlocke"]["legs"] if leg["legOrder"] == 1)
response = await client.delete(
response = await admin_client.delete(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/{leg1['id']}"
)
assert response.status_code == 400
async def test_add_leg_invalid_game_returns_404(
self, client: AsyncClient, ctx: dict
self, admin_client: AsyncClient, ctx: dict
):
response = await client.post(
response = await admin_client.post(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs",
json={"gameId": 9999},
)
@@ -259,33 +259,33 @@ class TestGenlockeLegs:
class TestAdvanceLeg:
async def test_uncompleted_run_returns_400(self, client: AsyncClient, ctx: dict):
async def test_uncompleted_run_returns_400(self, admin_client: AsyncClient, ctx: dict):
"""Cannot advance when leg 1's run is still active."""
response = await client.post(
response = await admin_client.post(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/1/advance"
)
assert response.status_code == 400
async def test_no_next_leg_returns_400(self, client: AsyncClient, games_ctx: dict):
async def test_no_next_leg_returns_400(self, admin_client: AsyncClient, games_ctx: dict):
"""A single-leg genlocke cannot be advanced."""
r = await client.post(
r = await admin_client.post(
GENLOCKES_BASE,
json={"name": "Single Leg", "gameIds": [games_ctx["game1_id"]]},
)
genlocke = r.json()
run_id = genlocke["legs"][0]["runId"]
await client.patch(f"{RUNS_BASE}/{run_id}", json={"status": "completed"})
await admin_client.patch(f"{RUNS_BASE}/{run_id}", json={"status": "completed"})
response = await client.post(
response = await admin_client.post(
f"{GENLOCKES_BASE}/{genlocke['id']}/legs/1/advance"
)
assert response.status_code == 400
async def test_advances_to_next_leg(self, client: AsyncClient, ctx: dict):
async def test_advances_to_next_leg(self, admin_client: AsyncClient, ctx: dict):
"""Completing the current run allows advancing to the next leg."""
await client.patch(f"{RUNS_BASE}/{ctx['run_id']}", json={"status": "completed"})
await admin_client.patch(f"{RUNS_BASE}/{ctx['run_id']}", json={"status": "completed"})
response = await client.post(
response = await admin_client.post(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/1/advance"
)
assert response.status_code == 200
@@ -293,11 +293,11 @@ class TestAdvanceLeg:
leg2 = next(leg for leg in legs if leg["legOrder"] == 2)
assert leg2["runId"] is not None
async def test_advances_with_transfers(self, client: AsyncClient, ctx: dict):
async def test_advances_with_transfers(self, admin_client: AsyncClient, ctx: dict):
"""Advancing with transfer_encounter_ids creates egg encounters in the next leg."""
await client.patch(f"{RUNS_BASE}/{ctx['run_id']}", json={"status": "completed"})
await admin_client.patch(f"{RUNS_BASE}/{ctx['run_id']}", json={"status": "completed"})
response = await client.post(
response = await admin_client.post(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/1/advance",
json={"transferEncounterIds": [ctx["encounter_id"]]},
)
@@ -308,7 +308,7 @@ class TestAdvanceLeg:
assert new_run_id is not None
# The new run should contain the transferred (egg) encounter
run_detail = (await client.get(f"{RUNS_BASE}/{new_run_id}")).json()
run_detail = (await admin_client.get(f"{RUNS_BASE}/{new_run_id}")).json()
assert len(run_detail["encounters"]) == 1
@@ -318,56 +318,56 @@ class TestAdvanceLeg:
class TestGenlockeGraveyard:
async def test_returns_empty_graveyard(self, client: AsyncClient, ctx: dict):
response = await client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/graveyard")
async def test_returns_empty_graveyard(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/graveyard")
assert response.status_code == 200
data = response.json()
assert data["entries"] == []
assert data["totalDeaths"] == 0
async def test_not_found_returns_404(self, client: AsyncClient):
assert (await client.get(f"{GENLOCKES_BASE}/9999/graveyard")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.get(f"{GENLOCKES_BASE}/9999/graveyard")).status_code == 404
class TestGenlockeLineages:
async def test_returns_empty_lineages(self, client: AsyncClient, ctx: dict):
response = await client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/lineages")
async def test_returns_empty_lineages(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/lineages")
assert response.status_code == 200
data = response.json()
assert data["lineages"] == []
assert data["totalLineages"] == 0
async def test_not_found_returns_404(self, client: AsyncClient):
assert (await client.get(f"{GENLOCKES_BASE}/9999/lineages")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.get(f"{GENLOCKES_BASE}/9999/lineages")).status_code == 404
class TestGenlockeRetiredFamilies:
async def test_returns_empty_retired_families(self, client: AsyncClient, ctx: dict):
response = await client.get(
async def test_returns_empty_retired_families(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.get(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/retired-families"
)
assert response.status_code == 200
data = response.json()
assert data["retired_pokemon_ids"] == []
async def test_not_found_returns_404(self, client: AsyncClient):
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (
await client.get(f"{GENLOCKES_BASE}/9999/retired-families")
await admin_client.get(f"{GENLOCKES_BASE}/9999/retired-families")
).status_code == 404
class TestLegSurvivors:
async def test_returns_survivors(self, client: AsyncClient, ctx: dict):
async def test_returns_survivors(self, admin_client: AsyncClient, ctx: dict):
"""The one caught encounter in leg 1 shows up as a survivor."""
response = await client.get(
response = await admin_client.get(
f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/1/survivors"
)
assert response.status_code == 200
assert len(response.json()) == 1
async def test_leg_not_found_returns_404(self, client: AsyncClient, ctx: dict):
async def test_leg_not_found_returns_404(self, admin_client: AsyncClient, ctx: dict):
assert (
await client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/99/survivors")
await admin_client.get(f"{GENLOCKES_BASE}/{ctx['genlocke_id']}/legs/99/survivors")
).status_code == 404
@@ -385,13 +385,13 @@ BOSS_PAYLOAD = {
class TestBossCRUD:
async def test_empty_list(self, client: AsyncClient, games_ctx: dict):
response = await client.get(f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses")
async def test_empty_list(self, admin_client: AsyncClient, games_ctx: dict):
response = await admin_client.get(f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses")
assert response.status_code == 200
assert response.json() == []
async def test_creates_boss(self, client: AsyncClient, games_ctx: dict):
response = await client.post(
async def test_creates_boss(self, admin_client: AsyncClient, games_ctx: dict):
response = await admin_client.post(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses", json=BOSS_PAYLOAD
)
assert response.status_code == 201
@@ -400,50 +400,50 @@ class TestBossCRUD:
assert data["levelCap"] == 14
assert data["pokemon"] == []
async def test_updates_boss(self, client: AsyncClient, games_ctx: dict):
async def test_updates_boss(self, admin_client: AsyncClient, games_ctx: dict):
boss = (
await client.post(
await admin_client.post(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses", json=BOSS_PAYLOAD
)
).json()
response = await client.put(
response = await admin_client.put(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses/{boss['id']}",
json={"levelCap": 20},
)
assert response.status_code == 200
assert response.json()["levelCap"] == 20
async def test_deletes_boss(self, client: AsyncClient, games_ctx: dict):
async def test_deletes_boss(self, admin_client: AsyncClient, games_ctx: dict):
boss = (
await client.post(
await admin_client.post(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses", json=BOSS_PAYLOAD
)
).json()
assert (
await client.delete(
await admin_client.delete(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses/{boss['id']}"
)
).status_code == 204
assert (
await client.get(f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses")
await admin_client.get(f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses")
).json() == []
async def test_boss_not_found_returns_404(
self, client: AsyncClient, games_ctx: dict
self, admin_client: AsyncClient, games_ctx: dict
):
assert (
await client.put(
await admin_client.put(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses/9999",
json={"levelCap": 10},
)
).status_code == 404
async def test_invalid_game_returns_404(self, client: AsyncClient):
assert (await client.get(f"{GAMES_BASE}/9999/bosses")).status_code == 404
async def test_invalid_game_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.get(f"{GAMES_BASE}/9999/bosses")).status_code == 404
async def test_game_without_version_group_returns_400(self, client: AsyncClient):
async def test_game_without_version_group_returns_400(self, admin_client: AsyncClient):
game = (
await client.post(
await admin_client.post(
GAMES_BASE,
json={
"name": "No VG",
@@ -454,7 +454,7 @@ class TestBossCRUD:
)
).json()
assert (
await client.get(f"{GAMES_BASE}/{game['id']}/bosses")
await admin_client.get(f"{GAMES_BASE}/{game['id']}/bosses")
).status_code == 400
@@ -465,27 +465,27 @@ class TestBossCRUD:
class TestBossResults:
@pytest.fixture
async def boss_ctx(self, client: AsyncClient, games_ctx: dict) -> dict:
async def boss_ctx(self, admin_client: AsyncClient, games_ctx: dict) -> dict:
"""A boss battle and a run for boss-result tests."""
boss = (
await client.post(
await admin_client.post(
f"{GAMES_BASE}/{games_ctx['game1_id']}/bosses", json=BOSS_PAYLOAD
)
).json()
run = (
await client.post(
await admin_client.post(
RUNS_BASE, json={"gameId": games_ctx["game1_id"], "name": "Boss Run"}
)
).json()
return {"boss_id": boss["id"], "run_id": run["id"]}
async def test_empty_list(self, client: AsyncClient, boss_ctx: dict):
response = await client.get(f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results")
async def test_empty_list(self, admin_client: AsyncClient, boss_ctx: dict):
response = await admin_client.get(f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results")
assert response.status_code == 200
assert response.json() == []
async def test_creates_boss_result(self, client: AsyncClient, boss_ctx: dict):
response = await client.post(
async def test_creates_boss_result(self, admin_client: AsyncClient, boss_ctx: dict):
response = await admin_client.post(
f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results",
json={"bossBattleId": boss_ctx["boss_id"], "result": "won", "attempts": 1},
)
@@ -495,13 +495,13 @@ class TestBossResults:
assert data["attempts"] == 1
assert data["completedAt"] is not None
async def test_upserts_existing_result(self, client: AsyncClient, boss_ctx: dict):
async def test_upserts_existing_result(self, admin_client: AsyncClient, boss_ctx: dict):
"""POSTing the same boss twice updates the result (upsert)."""
await client.post(
await admin_client.post(
f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results",
json={"bossBattleId": boss_ctx["boss_id"], "result": "won", "attempts": 1},
)
response = await client.post(
response = await admin_client.post(
f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results",
json={"bossBattleId": boss_ctx["boss_id"], "result": "lost", "attempts": 3},
)
@@ -510,31 +510,31 @@ class TestBossResults:
assert response.json()["attempts"] == 3
# Still only one record
all_results = (
await client.get(f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results")
await admin_client.get(f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results")
).json()
assert len(all_results) == 1
async def test_deletes_boss_result(self, client: AsyncClient, boss_ctx: dict):
async def test_deletes_boss_result(self, admin_client: AsyncClient, boss_ctx: dict):
result = (
await client.post(
await admin_client.post(
f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results",
json={"bossBattleId": boss_ctx["boss_id"], "result": "won"},
)
).json()
assert (
await client.delete(
await admin_client.delete(
f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results/{result['id']}"
)
).status_code == 204
assert (
await client.get(f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results")
await admin_client.get(f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results")
).json() == []
async def test_invalid_run_returns_404(self, client: AsyncClient, boss_ctx: dict):
assert (await client.get(f"{RUNS_BASE}/9999/boss-results")).status_code == 404
async def test_invalid_run_returns_404(self, admin_client: AsyncClient, boss_ctx: dict):
assert (await admin_client.get(f"{RUNS_BASE}/9999/boss-results")).status_code == 404
async def test_invalid_boss_returns_404(self, client: AsyncClient, boss_ctx: dict):
response = await client.post(
async def test_invalid_boss_returns_404(self, admin_client: AsyncClient, boss_ctx: dict):
response = await admin_client.post(
f"{RUNS_BASE}/{boss_ctx['run_id']}/boss-results",
json={"bossBattleId": 9999, "result": "won"},
)
@@ -547,8 +547,8 @@ class TestBossResults:
class TestStats:
async def test_returns_stats_structure(self, client: AsyncClient):
response = await client.get(STATS_BASE)
async def test_returns_stats_structure(self, admin_client: AsyncClient):
response = await admin_client.get(STATS_BASE)
assert response.status_code == 200
data = response.json()
assert data["totalRuns"] == 0
@@ -556,9 +556,9 @@ class TestStats:
assert data["topCaughtPokemon"] == []
assert data["typeDistribution"] == []
async def test_reflects_created_data(self, client: AsyncClient, ctx: dict):
async def test_reflects_created_data(self, admin_client: AsyncClient, ctx: dict):
"""Stats should reflect the run and encounter created in ctx."""
response = await client.get(STATS_BASE)
response = await admin_client.get(STATS_BASE)
assert response.status_code == 200
data = response.json()
assert data["totalRuns"] >= 1
@@ -572,23 +572,23 @@ class TestStats:
class TestExport:
async def test_export_games_returns_list(self, client: AsyncClient):
response = await client.get(f"{EXPORT_BASE}/games")
async def test_export_games_returns_list(self, admin_client: AsyncClient):
response = await admin_client.get(f"{EXPORT_BASE}/games")
assert response.status_code == 200
assert isinstance(response.json(), list)
async def test_export_pokemon_returns_list(self, client: AsyncClient):
response = await client.get(f"{EXPORT_BASE}/pokemon")
async def test_export_pokemon_returns_list(self, admin_client: AsyncClient):
response = await admin_client.get(f"{EXPORT_BASE}/pokemon")
assert response.status_code == 200
assert isinstance(response.json(), list)
async def test_export_evolutions_returns_list(self, client: AsyncClient):
response = await client.get(f"{EXPORT_BASE}/evolutions")
async def test_export_evolutions_returns_list(self, admin_client: AsyncClient):
response = await admin_client.get(f"{EXPORT_BASE}/evolutions")
assert response.status_code == 200
assert isinstance(response.json(), list)
async def test_export_game_routes_not_found_returns_404(self, client: AsyncClient):
assert (await client.get(f"{EXPORT_BASE}/games/9999/routes")).status_code == 404
async def test_export_game_routes_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.get(f"{EXPORT_BASE}/games/9999/routes")).status_code == 404
async def test_export_game_bosses_not_found_returns_404(self, client: AsyncClient):
assert (await client.get(f"{EXPORT_BASE}/games/9999/bosses")).status_code == 404
async def test_export_game_bosses_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.get(f"{EXPORT_BASE}/games/9999/bosses")).status_code == 404

View File

@@ -29,21 +29,21 @@ CHARMANDER_DATA = {
@pytest.fixture
async def pikachu(client: AsyncClient) -> dict:
response = await client.post(POKEMON_BASE, json=PIKACHU_DATA)
async def pikachu(admin_client: AsyncClient) -> dict:
response = await admin_client.post(POKEMON_BASE, json=PIKACHU_DATA)
assert response.status_code == 201
return response.json()
@pytest.fixture
async def charmander(client: AsyncClient) -> dict:
response = await client.post(POKEMON_BASE, json=CHARMANDER_DATA)
async def charmander(admin_client: AsyncClient) -> dict:
response = await admin_client.post(POKEMON_BASE, json=CHARMANDER_DATA)
assert response.status_code == 201
return response.json()
@pytest.fixture
async def ctx(db_session: AsyncSession, client: AsyncClient) -> dict:
async def ctx(db_session: AsyncSession, admin_client: AsyncClient) -> dict:
"""Full context: game + route + two pokemon + nuzlocke encounter on pikachu."""
vg = VersionGroup(name="Poke Test VG", slug="poke-test-vg")
db_session.add(vg)
@@ -63,11 +63,11 @@ async def ctx(db_session: AsyncSession, client: AsyncClient) -> dict:
db_session.add(route)
await db_session.flush()
r1 = await client.post(POKEMON_BASE, json=PIKACHU_DATA)
r1 = await admin_client.post(POKEMON_BASE, json=PIKACHU_DATA)
assert r1.status_code == 201
pikachu = r1.json()
r2 = await client.post(POKEMON_BASE, json=CHARMANDER_DATA)
r2 = await admin_client.post(POKEMON_BASE, json=CHARMANDER_DATA)
assert r2.status_code == 201
charmander = r2.json()
@@ -146,8 +146,8 @@ class TestListPokemon:
class TestCreatePokemon:
async def test_creates_pokemon(self, client: AsyncClient):
response = await client.post(POKEMON_BASE, json=PIKACHU_DATA)
async def test_creates_pokemon(self, admin_client: AsyncClient):
response = await admin_client.post(POKEMON_BASE, json=PIKACHU_DATA)
assert response.status_code == 201
data = response.json()
assert data["name"] == "pikachu"
@@ -156,16 +156,16 @@ class TestCreatePokemon:
assert isinstance(data["id"], int)
async def test_duplicate_pokeapi_id_returns_409(
self, client: AsyncClient, pikachu: dict
self, admin_client: AsyncClient, pikachu: dict
):
response = await client.post(
response = await admin_client.post(
POKEMON_BASE,
json={**PIKACHU_DATA, "name": "pikachu-copy"},
)
assert response.status_code == 409
async def test_missing_required_returns_422(self, client: AsyncClient):
response = await client.post(POKEMON_BASE, json={"name": "pikachu"})
async def test_missing_required_returns_422(self, admin_client: AsyncClient):
response = await admin_client.post(POKEMON_BASE, json={"name": "pikachu"})
assert response.status_code == 422
@@ -190,25 +190,25 @@ class TestGetPokemon:
class TestUpdatePokemon:
async def test_updates_name(self, client: AsyncClient, pikachu: dict):
response = await client.put(
async def test_updates_name(self, admin_client: AsyncClient, pikachu: dict):
response = await admin_client.put(
f"{POKEMON_BASE}/{pikachu['id']}", json={"name": "Pikachu"}
)
assert response.status_code == 200
assert response.json()["name"] == "Pikachu"
async def test_duplicate_pokeapi_id_returns_409(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
):
response = await client.put(
response = await admin_client.put(
f"{POKEMON_BASE}/{pikachu['id']}",
json={"pokeapiId": charmander["pokeapiId"]},
)
assert response.status_code == 409
async def test_not_found_returns_404(self, client: AsyncClient):
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (
await client.put(f"{POKEMON_BASE}/9999", json={"name": "x"})
await admin_client.put(f"{POKEMON_BASE}/9999", json={"name": "x"})
).status_code == 404
@@ -218,22 +218,22 @@ class TestUpdatePokemon:
class TestDeletePokemon:
async def test_deletes_pokemon(self, client: AsyncClient, charmander: dict):
async def test_deletes_pokemon(self, admin_client: AsyncClient, charmander: dict):
assert (
await client.delete(f"{POKEMON_BASE}/{charmander['id']}")
await admin_client.delete(f"{POKEMON_BASE}/{charmander['id']}")
).status_code == 204
assert (
await client.get(f"{POKEMON_BASE}/{charmander['id']}")
await admin_client.get(f"{POKEMON_BASE}/{charmander['id']}")
).status_code == 404
async def test_not_found_returns_404(self, client: AsyncClient):
assert (await client.delete(f"{POKEMON_BASE}/9999")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.delete(f"{POKEMON_BASE}/9999")).status_code == 404
async def test_pokemon_with_encounters_returns_409(
self, client: AsyncClient, ctx: dict
self, admin_client: AsyncClient, ctx: dict
):
"""Pokemon referenced by a nuzlocke encounter cannot be deleted."""
response = await client.delete(f"{POKEMON_BASE}/{ctx['pikachu_id']}")
response = await admin_client.delete(f"{POKEMON_BASE}/{ctx['pikachu_id']}")
assert response.status_code == 409
@@ -249,9 +249,9 @@ class TestPokemonFamilies:
assert response.json()["families"] == []
async def test_returns_family_grouping(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
):
await client.post(
await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -259,7 +259,7 @@ class TestPokemonFamilies:
"trigger": "level-up",
},
)
response = await client.get(f"{POKEMON_BASE}/families")
response = await admin_client.get(f"{POKEMON_BASE}/families")
assert response.status_code == 200
families = response.json()["families"]
assert len(families) == 1
@@ -280,9 +280,9 @@ class TestPokemonEvolutionChain:
assert response.json() == []
async def test_returns_chain_for_multi_stage(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
):
await client.post(
await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -290,7 +290,7 @@ class TestPokemonEvolutionChain:
"trigger": "level-up",
},
)
response = await client.get(f"{POKEMON_BASE}/{pikachu['id']}/evolution-chain")
response = await admin_client.get(f"{POKEMON_BASE}/{pikachu['id']}/evolution-chain")
assert response.status_code == 200
chain = response.json()
assert len(chain) == 1
@@ -317,9 +317,9 @@ class TestListEvolutions:
assert data["total"] == 0
async def test_returns_created_evolution(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
):
await client.post(
await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -327,14 +327,14 @@ class TestListEvolutions:
"trigger": "level-up",
},
)
response = await client.get(EVO_BASE)
response = await admin_client.get(EVO_BASE)
assert response.status_code == 200
assert response.json()["total"] == 1
async def test_filter_by_trigger(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
):
await client.post(
await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -342,9 +342,9 @@ class TestListEvolutions:
"trigger": "use-item",
},
)
hit = await client.get(EVO_BASE, params={"trigger": "use-item"})
hit = await admin_client.get(EVO_BASE, params={"trigger": "use-item"})
assert hit.json()["total"] == 1
miss = await client.get(EVO_BASE, params={"trigger": "level-up"})
miss = await admin_client.get(EVO_BASE, params={"trigger": "level-up"})
assert miss.json()["total"] == 0
@@ -355,9 +355,9 @@ class TestListEvolutions:
class TestCreateEvolution:
async def test_creates_evolution(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
):
response = await client.post(
response = await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -374,9 +374,9 @@ class TestCreateEvolution:
assert data["toPokemon"]["name"] == "charmander"
async def test_invalid_from_pokemon_returns_404(
self, client: AsyncClient, charmander: dict
self, admin_client: AsyncClient, charmander: dict
):
response = await client.post(
response = await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": 9999,
@@ -387,9 +387,9 @@ class TestCreateEvolution:
assert response.status_code == 404
async def test_invalid_to_pokemon_returns_404(
self, client: AsyncClient, pikachu: dict
self, admin_client: AsyncClient, pikachu: dict
):
response = await client.post(
response = await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -408,9 +408,9 @@ class TestCreateEvolution:
class TestUpdateEvolution:
@pytest.fixture
async def evolution(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
) -> dict:
response = await client.post(
response = await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -420,16 +420,16 @@ class TestUpdateEvolution:
)
return response.json()
async def test_updates_trigger(self, client: AsyncClient, evolution: dict):
response = await client.put(
async def test_updates_trigger(self, admin_client: AsyncClient, evolution: dict):
response = await admin_client.put(
f"{EVO_BASE}/{evolution['id']}", json={"trigger": "use-item"}
)
assert response.status_code == 200
assert response.json()["trigger"] == "use-item"
async def test_not_found_returns_404(self, client: AsyncClient):
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (
await client.put(f"{EVO_BASE}/9999", json={"trigger": "level-up"})
await admin_client.put(f"{EVO_BASE}/9999", json={"trigger": "level-up"})
).status_code == 404
@@ -441,9 +441,9 @@ class TestUpdateEvolution:
class TestDeleteEvolution:
@pytest.fixture
async def evolution(
self, client: AsyncClient, pikachu: dict, charmander: dict
self, admin_client: AsyncClient, pikachu: dict, charmander: dict
) -> dict:
response = await client.post(
response = await admin_client.post(
EVO_BASE,
json={
"fromPokemonId": pikachu["id"],
@@ -453,12 +453,12 @@ class TestDeleteEvolution:
)
return response.json()
async def test_deletes_evolution(self, client: AsyncClient, evolution: dict):
assert (await client.delete(f"{EVO_BASE}/{evolution['id']}")).status_code == 204
assert (await client.get(EVO_BASE)).json()["total"] == 0
async def test_deletes_evolution(self, admin_client: AsyncClient, evolution: dict):
assert (await admin_client.delete(f"{EVO_BASE}/{evolution['id']}")).status_code == 204
assert (await admin_client.get(EVO_BASE)).json()["total"] == 0
async def test_not_found_returns_404(self, client: AsyncClient):
assert (await client.delete(f"{EVO_BASE}/9999")).status_code == 404
async def test_not_found_returns_404(self, admin_client: AsyncClient):
assert (await admin_client.delete(f"{EVO_BASE}/9999")).status_code == 404
# ---------------------------------------------------------------------------
@@ -467,13 +467,13 @@ class TestDeleteEvolution:
class TestRouteEncounters:
async def test_empty_list_for_route(self, client: AsyncClient, ctx: dict):
response = await client.get(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon")
async def test_empty_list_for_route(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.get(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon")
assert response.status_code == 200
assert response.json() == []
async def test_creates_route_encounter(self, client: AsyncClient, ctx: dict):
response = await client.post(
async def test_creates_route_encounter(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.post(
f"{ROUTE_BASE}/{ctx['route_id']}/pokemon",
json={
"pokemonId": ctx["charmander_id"],
@@ -490,8 +490,8 @@ class TestRouteEncounters:
assert data["encounterRate"] == 10
assert data["pokemon"]["name"] == "charmander"
async def test_invalid_route_returns_404(self, client: AsyncClient, ctx: dict):
response = await client.post(
async def test_invalid_route_returns_404(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.post(
f"{ROUTE_BASE}/9999/pokemon",
json={
"pokemonId": ctx["charmander_id"],
@@ -504,8 +504,8 @@ class TestRouteEncounters:
)
assert response.status_code == 404
async def test_invalid_pokemon_returns_404(self, client: AsyncClient, ctx: dict):
response = await client.post(
async def test_invalid_pokemon_returns_404(self, admin_client: AsyncClient, ctx: dict):
response = await admin_client.post(
f"{ROUTE_BASE}/{ctx['route_id']}/pokemon",
json={
"pokemonId": 9999,
@@ -518,8 +518,8 @@ class TestRouteEncounters:
)
assert response.status_code == 404
async def test_updates_route_encounter(self, client: AsyncClient, ctx: dict):
r = await client.post(
async def test_updates_route_encounter(self, admin_client: AsyncClient, ctx: dict):
r = await admin_client.post(
f"{ROUTE_BASE}/{ctx['route_id']}/pokemon",
json={
"pokemonId": ctx["charmander_id"],
@@ -531,23 +531,23 @@ class TestRouteEncounters:
},
)
enc = r.json()
response = await client.put(
response = await admin_client.put(
f"{ROUTE_BASE}/{ctx['route_id']}/pokemon/{enc['id']}",
json={"encounterRate": 25},
)
assert response.status_code == 200
assert response.json()["encounterRate"] == 25
async def test_update_not_found_returns_404(self, client: AsyncClient, ctx: dict):
async def test_update_not_found_returns_404(self, admin_client: AsyncClient, ctx: dict):
assert (
await client.put(
await admin_client.put(
f"{ROUTE_BASE}/{ctx['route_id']}/pokemon/9999",
json={"encounterRate": 5},
)
).status_code == 404
async def test_deletes_route_encounter(self, client: AsyncClient, ctx: dict):
r = await client.post(
async def test_deletes_route_encounter(self, admin_client: AsyncClient, ctx: dict):
r = await admin_client.post(
f"{ROUTE_BASE}/{ctx['route_id']}/pokemon",
json={
"pokemonId": ctx["charmander_id"],
@@ -560,13 +560,13 @@ class TestRouteEncounters:
)
enc = r.json()
assert (
await client.delete(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon/{enc['id']}")
await admin_client.delete(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon/{enc['id']}")
).status_code == 204
assert (
await client.get(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon")
await admin_client.get(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon")
).json() == []
async def test_delete_not_found_returns_404(self, client: AsyncClient, ctx: dict):
async def test_delete_not_found_returns_404(self, admin_client: AsyncClient, ctx: dict):
assert (
await client.delete(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon/9999")
await admin_client.delete(f"{ROUTE_BASE}/{ctx['route_id']}/pokemon/9999")
).status_code == 404