Реал-тайм через SSE (push-to-invalidate): живые приглашения/партии, авто-обновление без перезагрузки
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""SSE-шина: доставка событий подписчику; эндпойнт /api/events требует вход."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_hub_delivers_to_subscriber():
|
||||
from app.core.events import EventHub
|
||||
|
||||
async def run():
|
||||
hub = EventHub()
|
||||
hub.bind_loop(asyncio.get_running_loop())
|
||||
q = hub.subscribe(1)
|
||||
hub.publish([1, 2], {"type": "match", "match_id": 5})
|
||||
event = await asyncio.wait_for(q.get(), timeout=1)
|
||||
assert event == {"type": "match", "match_id": 5}
|
||||
hub.unsubscribe(1, q)
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_hub_isolates_users():
|
||||
from app.core.events import EventHub
|
||||
|
||||
async def run():
|
||||
hub = EventHub()
|
||||
hub.bind_loop(asyncio.get_running_loop())
|
||||
q1 = hub.subscribe(1)
|
||||
q2 = hub.subscribe(2)
|
||||
hub.publish([2], {"type": "invitations"}) # только пользователю 2
|
||||
got2 = await asyncio.wait_for(q2.get(), timeout=1)
|
||||
assert got2 == {"type": "invitations"}
|
||||
assert q1.empty() # пользователю 1 ничего не пришло
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
|
||||
def test_events_requires_auth(client: TestClient):
|
||||
# Без сессии SSE-эндпойнт не отдаёт поток (401 на зависимости get_current_user).
|
||||
r = client.get("/api/events")
|
||||
assert r.status_code == 401
|
||||
Reference in New Issue
Block a user