🚧 Update topic explorer

This commit is contained in:
Jonas Bengtsson
2026-09-20 17:57:05 +02:00
parent 6385a27411
commit b8e7c14368
10 changed files with 1344 additions and 3888 deletions
+3 -1
View File
@@ -256,6 +256,7 @@ def client(db_session, monkeypatch):
self.id = id
self.username = username
self.topic_filter = topic_filter
self.buffer = [] # list of message dicts, index-tagged
class FakeSessions:
def __init__(self):
@@ -281,7 +282,8 @@ def client(db_session, monkeypatch):
monkeypatch.setattr(explorer_module, "sessions", fake_sessions)
def fake_messages_since(session_id, index):
return []
session = fake_sessions.get(session_id)
return [dict(m) for m in session.buffer if m["index"] > index]
def fake_list_retained(max_results=200, timeout=3.0):
return [
+52
View File
@@ -283,6 +283,58 @@ def test_topic_explorer_flow(admin_client):
assert any(m["topic"] == "site/config/level" for m in retained.json())
def test_topic_explorer_stream_delivers_first_message(admin_client, monkeypatch):
"""Regression test: the SSE cursor must include a session's FIRST
buffered message (index 0). messages_since is strictly-greater, so
a cursor starting at 0 dropped it — subscribe, publish {"cmd":"start"}
then {"cmd":"stop"}, and the stream must deliver BOTH."""
from app import explorer as explorer_module
sub = admin_client.post("/api/v1/topics/subscribe", json={"topic_filter": "e2e/#"})
assert sub.status_code == 200
session_id = sub.json()["session_id"]
session = explorer_module.sessions.get(session_id)
# Simulate the broker delivering both payloads (as _on_message would).
session.buffer = [
{
"index": 0,
"topic": "e2e/diff/device1",
"qos": 1,
"retain": False,
"received_at": 1.0,
"payload_b64": "eyJjbWQiOiAic3RhcnQifQ==",
"payload_text": '{"cmd": "start"}',
"properties": None,
},
{
"index": 1,
"topic": "e2e/diff/device1",
"qos": 1,
"retain": False,
"received_at": 2.0,
"payload_b64": "eyJjbWQiOiAic3RvcCJ9",
"payload_text": '{"cmd": "stop"}',
"properties": None,
},
]
with admin_client.stream("GET", f"/api/v1/topics/stream/{session_id}") as response:
assert response.status_code == 200
assert response.headers["content-type"].startswith("text/event-stream")
received = []
for line in response.iter_lines():
if line.startswith("data:"):
received.append(json.loads(line[len("data:"):].strip()))
if len(received) == 2:
break
assert [m["payload_text"] for m in received] == [
'{"cmd": "start"}',
'{"cmd": "stop"}',
], "stream must deliver the session's first message (index 0), not drop it"
def test_topic_explorer_publish_audited(admin_client, db_session):
"""Publishes through the test console are audited (spec section 35),
and wildcards are rejected."""