From 1465a30a01fdaa27f078bf87fedc17e07782c114 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 19 Jul 2026 18:09:35 +0200 Subject: [PATCH] test: event-builder shape (30402/30078/31923/22001) Pure tests (no signing/relays): listing tags (d/title/price/status/g/t), reservation carries canonical amount_sat as plaintext JSON, calendar start/end + no broken 'a' tag, availability response plaintext. 20 pass. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD --- tests/test_events.py | 90 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/test_events.py diff --git a/tests/test_events.py b/tests/test_events.py new file mode 100644 index 0000000..972e4b0 --- /dev/null +++ b/tests/test_events.py @@ -0,0 +1,90 @@ +"""Nostr event builders — tag/kind/content shape (no signing, no relays).""" + +import json + +from ..models import AvailabilityResult, Booking, BookingStatus +from ..nostr import events +from ..nostr.kinds import ( + KIND_AVAILABILITY_RESPONSE, + KIND_CALENDAR_EVENT, + KIND_LISTING, + KIND_RESERVATION, +) +from .conftest import make_room + + +def _tag(ev, name): + """First tag value for `name`, or None.""" + return next((t[1] for t in ev["tags"] if t[0] == name), None) + + +def _tags(ev, name): + return [t[1] for t in ev["tags"] if t[0] == name] + + +def test_listing_event_shape(): + room = make_room( + currency="EUR", price=80.0, + ) + room.amenities = ["wifi", "breakfast"] + room.location = "Château" + room.geohash = "u0j2" + room.images = ["https://img/1.jpg"] + + ev = events.build_listing_event(room) + + assert ev["kind"] == KIND_LISTING # 30402 + assert _tag(ev, "d") == room.id # addressable: re-publish replaces + assert _tag(ev, "title") == "Tower Room" + price = next(t for t in ev["tags"] if t[0] == "price") + assert price == ["price", "80.0", "EUR", "night"] + assert _tag(ev, "status") == "active" + assert _tag(ev, "location") == "Château" + assert _tag(ev, "g") == "u0j2" + assert _tags(ev, "image") == ["https://img/1.jpg"] + assert set(_tags(ev, "t")) == {"wifi", "breakfast"} + + +def test_reservation_event_is_plaintext_json_no_scaffolding(): + booking = Booking( + id="bk1", room_id="room1", guest_pubkey="npub_guest", + check_in="2026-08-01", check_out="2026-08-04", nights=3, num_guests=2, + currency="sat", price_fiat=300.0, amount_sat=300, deposit_sat=300, + status=BookingStatus.confirmed, + ) + ev = events.build_reservation_event(booking, booking.guest_pubkey) + + assert ev["kind"] == KIND_RESERVATION # 30078 + assert _tag(ev, "d") == "bk1" + assert _tag(ev, "p") == "npub_guest" + assert "_plaintext" not in ev # scaffolding removed; service.py encrypts content + payload = json.loads(ev["content"]) + assert payload["amount_sat"] == 300 # canonical, carried verbatim + assert payload["status"] == "confirmed" + + +def test_block_calendar_event_shape(): + room = make_room() + ev = events.build_block_calendar_event(room, "2026-09-01", "2026-09-05", "blk1") + + assert ev["kind"] == KIND_CALENDAR_EVENT # 31923 + assert _tag(ev, "d") == "room1:blk1" + assert _tag(ev, "start") == "2026-09-01" + assert _tag(ev, "end") == "2026-09-05" + # the broken {operator_pubkey} 'a' tag was removed + assert not any(t[0] == "a" for t in ev["tags"]) + + +def test_availability_response_is_plaintext(): + result = AvailabilityResult( + room_id="room1", check_in="2026-08-01", check_out="2026-08-04", + available=True, nights=3, quote_sat=300, quote_fiat=3.0, currency="sat", + ) + ev = events.build_availability_response(result, "npub_requester") + + assert ev["kind"] == KIND_AVAILABILITY_RESPONSE # 22001 + assert _tag(ev, "p") == "npub_requester" + assert "_plaintext" not in ev # public info: no encryption + payload = json.loads(ev["content"]) + assert payload["available"] is True + assert payload["quote_sat"] == 300