HERMES-GIELIE/tests/test_issue2863_session_index_prime.py

62 lines
2.6 KiB
Python
Raw Normal View History

"""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
Release v0.51.345 — Release LI (model override picker for scheduled jobs #3809) (#3896) Absorbs contributor PR #3809 (@b3nw), rebased onto fresh master (was ~20 behind, panels.js conflict resolved by merging the new !isNoAgent skill-tags guard with the model-select call). Adds a Model Override dropdown to the Tasks scheduled-jobs create/edit form, populated from /api/models grouped by provider, persisting model+provider, clearable to default, disabled in no-agent mode. Surfaces hermes-agent's existing per-job model override (CLI parity). greptile P1s (override cleared on fast-save / on API failure) verified ALREADY-FIXED in PR head; also applied an Opus UX hardening (keep the model select disabled on a failed /api/models load so the user can't think they cleared the override). UX approved by Nathan via screenshots. Pre-merge fixes: - i18n: the PR added the 3 cron_model_* keys to all locales but left 10 of them as 'TODO: translate' English stubs (only es was done), tripping test_zh_hant_locale. Provided real translations for de/zh/zh-Hant/ru/ja/fr/pl/ it/pt/tr. - test isolation: #3809's new test file shifts pytest-shard composition so test_issue2863's background-rebuild test ran after a test that leaves the #3884 _SESSION_INDEX_REBUILD_THREAD globals populated, suppressing the fresh thread it asserts on. Made that test hermetic (joins+clears the rebuild-thread globals up front) so it passes regardless of shard run order. Co-authored-by: nesquena-hermes <[email protected]> Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-06-09 21:45:59 +00:00
# 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"}