test(#5572): prove imported transcript survives clear

This commit is contained in:
Rod Boev committed 2026-07-04 18:39:51 -04:00
1 parent b19f447dd7
commit cc7896cfab
1 file changed
+69 -15
@@ -2,16 +2,44 @@
from __future__ import annotations from __future__ import annotations
import copy import json
import sqlite3
from collections import OrderedDict from collections import OrderedDict
from io import BytesIO from io import BytesIO
from types import SimpleNamespace from types import SimpleNamespace
from urllib.parse import urlparse
import pytest import pytest
pytestmark = pytest.mark.requires_agent_modules pytestmark = pytest.mark.requires_agent_modules
class _GetHandler:
def __init__(self, path: str):
self.path = path
self.headers = {}
self.client_address = ("127.0.0.1", 12345)
self.status = None
self.wfile = BytesIO()
self.response_headers = []
def send_response(self, status):
self.status = status
def send_header(self, key, value):
self.response_headers.append((key, value))
def end_headers(self):
pass
@property
def response_json(self):
return json.loads(self.wfile.getvalue().decode("utf-8"))
def log_message(self, *args, **kwargs):
pass
def _msg(role: str, content: str, ts: float, mid: str) -> dict: def _msg(role: str, content: str, ts: float, mid: str) -> dict:
return {"id": mid, "role": role, "content": content, "timestamp": ts} return {"id": mid, "role": role, "content": content, "timestamp": ts}
@@ -42,6 +70,7 @@ def _post_clear(monkeypatch, sid: str):
body = b'{"session_id":"%s"}' % sid.encode("utf-8") body = b'{"session_id":"%s"}' % sid.encode("utf-8")
monkeypatch.setattr(routes, "_check_csrf", lambda handler: True) monkeypatch.setattr(routes, "_check_csrf", lambda handler: True)
original_j = routes.j
captured = {} captured = {}
@@ -56,10 +85,36 @@ def _post_clear(monkeypatch, sid: str):
headers={"Content-Length": str(len(body))}, headers={"Content-Length": str(len(body))},
rfile=BytesIO(body), rfile=BytesIO(body),
) )
try:
routes.handle_post(handler, SimpleNamespace(path="/api/session/clear")) routes.handle_post(handler, SimpleNamespace(path="/api/session/clear"))
finally:
monkeypatch.setattr(routes, "j", original_j)
return captured return captured
def _make_state_db(path, sid: str, source: str, rows):
conn = sqlite3.connect(path)
try:
conn.execute(
"CREATE TABLE sessions (id TEXT PRIMARY KEY, source TEXT, title TEXT, model TEXT, started_at REAL, message_count INTEGER)"
)
conn.execute(
"CREATE TABLE messages (id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, role TEXT, content TEXT, timestamp REAL)"
)
conn.execute(
"INSERT INTO sessions (id, source, title, model, started_at, message_count) VALUES (?, ?, ?, ?, ?, ?)",
(sid, source, f"Imported {source}", "test-model", 1000.0, len(rows)),
)
for row in rows:
conn.execute(
"INSERT INTO messages (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)",
(sid, row["role"], row["content"], row["timestamp"]),
)
conn.commit()
finally:
conn.close()
@pytest.mark.parametrize( @pytest.mark.parametrize(
("source_tag", "source_label"), ("source_tag", "source_label"),
[("telegram", "Telegram"), ("discord", "Discord")], [("telegram", "Telegram"), ("discord", "Discord")],
@@ -71,7 +126,7 @@ def test_session_clear_preserves_imported_messaging_transcript_and_blocks_state_
source_label, source_label,
): ):
import api.routes as routes import api.routes as routes
from api.models import Session, merge_session_messages_append_only from api.models import Session, get_cli_session_messages, merge_session_messages_append_only
_install_isolated_session_env(monkeypatch, tmp_path) _install_isolated_session_env(monkeypatch, tmp_path)
@@ -109,12 +164,7 @@ def test_session_clear_preserves_imported_messaging_transcript_and_blocks_state_
_msg("user", f"{source_label} external prompt", 10.0, f"{source_tag}-ext-u1"), _msg("user", f"{source_label} external prompt", 10.0, f"{source_tag}-ext-u1"),
_msg("assistant", f"{source_label} external reply", 11.0, f"{source_tag}-ext-a1"), _msg("assistant", f"{source_label} external reply", 11.0, f"{source_tag}-ext-a1"),
] ]
external_before = copy.deepcopy(external_messages) _make_state_db(tmp_path / "state.db", sid, source_tag, external_messages)
state_db_messages = [
_msg("user", f"{source_label} state prompt", 20.0, f"{source_tag}-db-u1"),
_msg("assistant", f"{source_label} state reply", 21.0, f"{source_tag}-db-a1"),
]
state_db_before = copy.deepcopy(state_db_messages)
captured = _post_clear(monkeypatch, sid) captured = _post_clear(monkeypatch, sid)
@@ -156,14 +206,18 @@ def test_session_clear_preserves_imported_messaging_transcript_and_blocks_state_
assert '"pending_started_at": null' in persisted assert '"pending_started_at": null' in persisted
assert '"pending_user_source": null' in persisted assert '"pending_user_source": null' in persisted
assert external_messages == external_before state_db_messages = get_cli_session_messages(sid)
assert state_db_messages == state_db_before assert [(m["role"], m["content"], m["timestamp"]) for m in state_db_messages] == [
(m["role"], m["content"], m["timestamp"]) for m in external_messages
]
display_messages = routes._merged_session_messages_for_display( handler = _GetHandler(f"/api/session?session_id={sid}&resolve_model=0")
loaded, routes.handle_get(handler, urlparse(handler.path))
cli_messages=external_messages, assert handler.status == 200
) payload_messages = handler.response_json["session"]["messages"]
assert display_messages == external_messages assert [(m["role"], m["content"], m["timestamp"]) for m in payload_messages] == [
(m["role"], m["content"], m["timestamp"]) for m in external_messages
]
merged = merge_session_messages_append_only( merged = merge_session_messages_append_only(
loaded.messages, loaded.messages,