"""Relay round-trip tests — verify events flow through the relay and reach the client. Tests: 1. WebSocket round-trip: publish → subscribe → receive (no browser) 2. Relay → Browser: publish event via WS, verify it renders in the feed """ import json import time import uuid import pytest from selenium.webdriver.common.by import By from test.nostr_helpers import make_event, TEST_SECKEY, TEST_PUBKEY class TestWSRoundTrip: """Pure WebSocket relay round-trip — no browser needed.""" def test_publish_and_receive(self, relay): """Publish an event, subscribe, and verify it comes back.""" import websocket ws_url = relay["ws"] marker = f"ws-roundtrip-{uuid.uuid4().hex[:8]}" ev = make_event(TEST_SECKEY, marker, kind=1) # Publish. pub = websocket.create_connection(ws_url, timeout=5) pub.send(json.dumps(["EVENT", ev])) ok_raw = pub.recv() pub.close() ok = json.loads(ok_raw) assert ok[0] == "OK", f"expected OK, got {ok}" assert ok[2] is True, f"relay rejected event: {ok}" # Subscribe and fetch. sub = websocket.create_connection(ws_url, timeout=5) sub_id = f"test-{uuid.uuid4().hex[:8]}" sub.send( json.dumps(["REQ", sub_id, {"ids": [ev["id"]]}]) ) found = False for _ in range(20): msg = json.loads(sub.recv()) if msg[0] == "EVENT" and msg[1] == sub_id: assert msg[2]["id"] == ev["id"] assert msg[2]["content"] == marker found = True break if msg[0] == "EOSE": break sub.send(json.dumps(["CLOSE", sub_id])) sub.close() assert found, "event not received back from relay" def test_live_subscription(self, relay): """Subscribe first, then publish — verify live delivery.""" import websocket ws_url = relay["ws"] marker = f"live-{uuid.uuid4().hex[:8]}" sub_id = f"live-{uuid.uuid4().hex[:8]}" # Subscribe to our test pubkey. sub = websocket.create_connection(ws_url, timeout=10) sub.send( json.dumps( ["REQ", sub_id, {"authors": [TEST_PUBKEY], "kinds": [1], "limit": 5}] ) ) # Drain EOSE. while True: msg = json.loads(sub.recv()) if msg[0] == "EOSE": break # Now publish. ev = make_event(TEST_SECKEY, marker, kind=1) pub = websocket.create_connection(ws_url, timeout=5) pub.send(json.dumps(["EVENT", ev])) pub.recv() # OK pub.close() # Should arrive on the subscription. sub.settimeout(5) msg = json.loads(sub.recv()) assert msg[0] == "EVENT" assert msg[2]["content"] == marker sub.send(json.dumps(["CLOSE", sub_id])) sub.close() class TestBrowserRoundTrip: """Publish events via WebSocket, verify they render in the browser feed.""" def test_event_appears_in_feed(self, relay, browser, h): """Publish a kind-1 note, set the matching pubkey, verify the note renders.""" import websocket marker = f"browser-test-{uuid.uuid4().hex[:8]}" ev = make_event(TEST_SECKEY, marker, kind=1) # Publish to relay. ws = websocket.create_connection(relay["ws"], timeout=5) ws.send(json.dumps(["EVENT", ev])) ok = json.loads(ws.recv()) ws.close() assert ok[2] is True, f"relay rejected: {ok}" # Load app with the test pubkey so the feed subscribes. browser.get(relay["url"]) time.sleep(1) h.js(f"localStorage.setItem('smesh-pubkey', '{TEST_PUBKEY}')") browser.get(relay["url"]) # Poll for up to 20s — SW registration + relay connect + subscription. found = False for _ in range(20): time.sleep(1) page_text = h.js("return document.body.innerText") or "" if marker in page_text: found = True break if not found: # Diagnostic: dump the page state. status = h.js("return document.body.innerText") or "(empty)" sw_state = h.js( "return navigator.serviceWorker.controller " "? 'active' : 'no controller'" ) pytest.fail( f"event '{marker}' not in feed after 20s.\n" f"SW: {sw_state}\nPage: {status[:300]}" ) def test_localhost_relay_connected(self, relay, browser, h): """Verify the app connects to the localhost relay (our fallback).""" browser.get(relay["url"]) time.sleep(1) h.js(f"localStorage.setItem('smesh-pubkey', '{TEST_PUBKEY}')") browser.get(relay["url"]) time.sleep(3) # Check console for the local relay log line. local_relay = h.js("return document.querySelector('body')?.dataset?.localRelay") # Alternatively, check that the SW connected by looking for console output. # The app logs "local relay: ws://127.0.0.1:3334" on startup. logs = h.js(""" var entries = []; if (window.__testLogs) entries = window.__testLogs; return entries.join('|'); """) # Even if we can't capture console.log, verify the relay served the page. page = h.js("return document.body.innerText || ''") assert page is not None and len(page) > 0, "page did not load" def test_multiple_events_ordered(self, relay, browser, h): """Publish multiple events, verify they appear in the feed.""" import websocket now = int(time.time()) events = [] for i in range(3): content = f"order-test-{uuid.uuid4().hex[:6]}-{i}" ev = make_event(TEST_SECKEY, content, kind=1, created_at=now - (2 - i)) events.append(ev) ws = websocket.create_connection(relay["ws"], timeout=5) for ev in events: ws.send(json.dumps(["EVENT", ev])) ws.recv() # OK ws.close() browser.get(relay["url"]) time.sleep(1) h.js(f"localStorage.setItem('smesh-pubkey', '{TEST_PUBKEY}')") browser.get(relay["url"]) # Poll for up to 20s. found_count = 0 for _ in range(20): time.sleep(1) page_text = h.js("return document.body.innerText") or "" found_count = sum(1 for ev in events if ev["content"] in page_text) if found_count == len(events): break assert found_count == len(events), ( f"only {found_count}/{len(events)} events appeared in feed" )