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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD
90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
"""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
|