* stage v0.51.347: render/stream cluster (#3892 #3898 #3885 #3882 #3868) + 2 Opus SHOULD-FIX * stage v0.51.347: trim #3885 error-guard comment to fit diagnostic-test window * Stamp v0.51.347 — Release LK (streaming & render reliability cluster) * Remove stray uv.lock accidentally staged (not part of any cluster PR) --------- Co-authored-by: nesquena-hermes <[email protected]>
62 lines
2.6 KiB
Python
62 lines
2.6 KiB
Python
"""Regression tests for #2863 missing session-index background rebuild."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import time
|
|
|
|
|
|
def test_missing_index_starts_background_rebuild_while_preserving_first_scan(monkeypatch, tmp_path):
|
|
import api.models as models
|
|
from api.models import all_sessions
|
|
|
|
# Hermetic isolation: a prior test in the same worker may have left the
|
|
# background rebuild thread bookkeeping populated. Since #3884 the start
|
|
# helper short-circuits when a rebuild thread for the same target is still
|
|
# alive, so stale globals from another test could suppress the fresh thread
|
|
# this test asserts on. Join any leftover thread and clear both globals so
|
|
# this test only observes the thread IT triggers, regardless of run order.
|
|
_stale = getattr(models, "_SESSION_INDEX_REBUILD_THREAD", None)
|
|
if _stale is not None:
|
|
try:
|
|
_stale.join(timeout=5)
|
|
except Exception:
|
|
pass
|
|
models._SESSION_INDEX_REBUILD_THREAD = None
|
|
if hasattr(models, "_SESSION_INDEX_REBUILD_THREAD_TARGET"):
|
|
models._SESSION_INDEX_REBUILD_THREAD_TARGET = None
|
|
|
|
session_dir = tmp_path / "sessions"
|
|
session_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(models, "SESSION_DIR", session_dir)
|
|
monkeypatch.setattr(models, "SESSION_INDEX_FILE", session_dir / "_index.json")
|
|
models.SESSIONS.clear()
|
|
|
|
for idx in range(3):
|
|
payload = {
|
|
"session_id": f"issue2863{idx}",
|
|
"title": f"Session {idx}",
|
|
"workspace": str(tmp_path),
|
|
"model": "test-model",
|
|
"messages": [{"role": "user", "content": f"hello {idx}", "timestamp": time.time() + idx}],
|
|
"created_at": time.time() + idx,
|
|
"updated_at": time.time() + idx,
|
|
}
|
|
(session_dir / f"issue2863{idx}.json").write_text(json.dumps(payload), encoding="utf-8")
|
|
|
|
rows = all_sessions()
|
|
|
|
assert {row["session_id"] for row in rows} == {"issue28630", "issue28631", "issue28632"}
|
|
|
|
thread = models._SESSION_INDEX_REBUILD_THREAD
|
|
# Fast runners can complete the background rebuild and clear the global
|
|
# thread slot before this assertion observes it. The invariant is that the
|
|
# first scan remains correct and the index is rebuilt, not that the transient
|
|
# thread object is still visible.
|
|
if thread is not None:
|
|
thread.join(timeout=5)
|
|
assert not thread.is_alive()
|
|
|
|
index = json.loads(models.SESSION_INDEX_FILE.read_text(encoding="utf-8"))
|
|
assert {row["session_id"] for row in index} == {"issue28630", "issue28631", "issue28632"}
|
|
assert {row["session_id"] for row in all_sessions()} == {"issue28630", "issue28631", "issue28632"}
|