From 72061b57ebbf7fd88c6fd1da3cf2931541f37d28 Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 20 Jul 2026 00:44:10 +0200 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD --- tests/test_admin_endpoints.py | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tests/test_admin_endpoints.py diff --git a/tests/test_admin_endpoints.py b/tests/test_admin_endpoints.py new file mode 100644 index 0000000..12c494f --- /dev/null +++ b/tests/test_admin_endpoints.py @@ -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"]