Files
isparkclaw-webui/tests/test_issue856_session_streaming_state.py
nesquena-hermes b82954ee70 feat(ui): session attention indicators — streaming spinner, unread dot, timestamps (#856)
Closes #856. Co-authored-by: Frank Song <138988108+franksong2702@users.noreply.github.com>
Reviewed-by: nesquena (709bd37 — test isolation fix also included)
2026-04-23 09:05:57 -07:00

94 lines
3.0 KiB
Python

"""
Regression tests for session streaming indicator payloads used by the session list.
This ensures backend payloads report per-session streaming status from active stream
tracking, not only for the foreground conversation.
"""
import threading
import pytest
import api.models as models
from api.models import Session, all_sessions
@pytest.fixture(autouse=True)
def _isolate_session_stream_state(tmp_path, monkeypatch):
"""Keep session/index/stream state isolated from the host environment."""
session_dir = tmp_path / "sessions"
session_dir.mkdir()
index_file = session_dir / "_index.json"
monkeypatch.setattr(models, "SESSION_DIR", session_dir)
monkeypatch.setattr(models, "SESSION_INDEX_FILE", index_file)
models.SESSIONS.clear()
stream_map = {}
stream_lock = threading.Lock()
monkeypatch.setattr(models, "STREAMS", stream_map)
monkeypatch.setattr(models, "STREAMS_LOCK", stream_lock)
yield
models.SESSIONS.clear()
def _make_session(session_id, stream_id=None, message_count=1):
s = Session(
session_id=session_id,
title=session_id,
messages=[{"role": "user", "content": f"seed-{session_id}"}] * message_count,
)
s.active_stream_id = stream_id
return s
def test_all_sessions_marks_indexed_and_in_memory_streaming_sessions():
"""Session records from both index and in-memory cache should expose is_streaming."""
s_disk = _make_session("disk_session", stream_id="stream-1")
s_disk.save()
s_memory = _make_session("memory_session", stream_id="stream-2")
with models.LOCK:
models.SESSIONS[s_memory.session_id] = s_memory
models.STREAMS["stream-1"] = object()
models.STREAMS["stream-2"] = object()
listed = all_sessions()
by_sid = {s["session_id"]: s for s in listed}
assert by_sid["disk_session"]["is_streaming"] is True
assert by_sid["memory_session"]["is_streaming"] is True
assert by_sid["memory_session"]["active_stream_id"] == "stream-2"
def test_all_sessions_marks_streaming_false_when_stream_is_not_active():
"""Stale active_stream_id should not imply streaming without active STREAMS entry."""
s = _make_session("stalesession", stream_id="stale-stream")
s.save()
assert all_sessions()[0]["is_streaming"] is False
models.STREAMS["stale-stream"] = object()
assert all_sessions()[0]["is_streaming"] is True
models.STREAMS.pop("stale-stream", None)
assert all_sessions()[0]["is_streaming"] is False
def test_all_sessions_does_not_report_streaming_after_restart_without_active_registry():
"""Server restarts should not resurrect sidebar streaming state from disk alone."""
s = _make_session("restart_session", stream_id="old-stream")
s.save()
models.SESSIONS.clear()
reloaded = Session.load("restart_session")
assert reloaded is not None
assert reloaded.active_stream_id == "old-stream"
listed = all_sessions()
assert listed[0]["active_stream_id"] == "old-stream"
assert listed[0]["is_streaming"] is False