feat: operator admin UI + backing endpoints (v0.2.0) #16

Merged
padreug merged 4 commits from feat/admin-ui into main 2026-07-19 22:46:12 +00:00
Showing only changes of commit 72061b57eb - Show all commits

test: operator admin endpoint logic (ownership, update, settings merge)

5 tests calling the view functions directly with a fake auth key: _owned_room
403/404, list-rooms wallet filter, room update applies only mutable fields
(wallet/id immutable), settings PUT merges editable fields. 28 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD
Padreug 2026-07-20 00:44:10 +02:00

View file

@ -0,0 +1,93 @@
"""Operator admin endpoint logic (backs the admin UI). Calls the view
functions directly with a fake auth key (bypassing FastAPI's Depends) and
monkeypatched crud no live server."""
import asyncio
from types import SimpleNamespace
import pytest
from fastapi import HTTPException
from .. import crud, views_api
from ..models import ChateletSettings, CreateRoomData, RoomStatus
from .conftest import make_room
def _key(wallet_id: str = "w1"):
return SimpleNamespace(wallet=SimpleNamespace(id=wallet_id, user="u1"))
def test_owned_room_rejects_other_wallet(monkeypatch):
async def gr(_):
return make_room(wallet="w1")
monkeypatch.setattr(crud, "get_room", gr)
with pytest.raises(HTTPException) as e:
asyncio.run(views_api._owned_room("room1", _key("w2")))
assert e.value.status_code == 403
def test_owned_room_404_when_missing(monkeypatch):
async def gr(_):
return None
monkeypatch.setattr(crud, "get_room", gr)
with pytest.raises(HTTPException) as e:
asyncio.run(views_api._owned_room("nope", _key()))
assert e.value.status_code == 404
def test_list_rooms_filters_to_calling_wallet(monkeypatch):
async def gr():
return [make_room("a", wallet="w1"), make_room("b", wallet="w2")]
monkeypatch.setattr(crud, "get_rooms", gr)
out = asyncio.run(views_api.api_list_rooms(key=_key("w1")))
assert [r.id for r in out] == ["a"]
def test_update_room_applies_mutable_fields_only(monkeypatch):
room = make_room("room1", wallet="w1", status=RoomStatus.inactive)
async def gr(_):
return room
async def ur(r):
return r
monkeypatch.setattr(crud, "get_room", gr)
monkeypatch.setattr(crud, "update_room", ur)
data = CreateRoomData(
wallet="attacker", title="New Title", price_amount=200,
price_currency="USD", max_guests=5, checkin_instructions="gate 7",
)
out = asyncio.run(views_api.api_update_room("room1", data, key=_key("w1")))
assert out.title == "New Title"
assert out.price_amount == 200
assert out.checkin_instructions == "gate 7"
assert out.wallet == "w1" # wallet is NOT client-mutable
assert out.id == "room1" # id unchanged
def test_update_settings_merges_editable_fields(monkeypatch):
existing = ChateletSettings(operator_id=None, deposit_percent=100)
async def gs():
return existing
async def us(s):
return s
monkeypatch.setattr(crud, "get_or_create_settings", gs)
monkeypatch.setattr(crud, "update_settings", us)
incoming = ChateletSettings(
operator_id="op1", deposit_percent=50, default_hold_minutes=45,
relays=["wss://relay.example"],
)
out = asyncio.run(views_api.api_update_settings(incoming, key=_key()))
assert out.operator_id == "op1"
assert out.deposit_percent == 50
assert out.default_hold_minutes == 45
assert out.relays == ["wss://relay.example"]