From cc7896cfabe1d93a97929afc18eb23e243954883 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Sat, 4 Jul 2026 18:39:51 -0400 Subject: [PATCH] test(#5572): prove imported transcript survives clear --- ...est_issue5572_messaging_clear_semantics.py | 86 +++++++++++++++---- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/tests/test_issue5572_messaging_clear_semantics.py b/tests/test_issue5572_messaging_clear_semantics.py index 2de54c8fc..95da19676 100644 --- a/tests/test_issue5572_messaging_clear_semantics.py +++ b/tests/test_issue5572_messaging_clear_semantics.py @@ -2,16 +2,44 @@ from __future__ import annotations -import copy +import json +import sqlite3 from collections import OrderedDict from io import BytesIO from types import SimpleNamespace +from urllib.parse import urlparse import pytest 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: 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") monkeypatch.setattr(routes, "_check_csrf", lambda handler: True) + original_j = routes.j captured = {} @@ -56,10 +85,36 @@ def _post_clear(monkeypatch, sid: str): headers={"Content-Length": str(len(body))}, rfile=BytesIO(body), ) - routes.handle_post(handler, SimpleNamespace(path="/api/session/clear")) + try: + routes.handle_post(handler, SimpleNamespace(path="/api/session/clear")) + finally: + monkeypatch.setattr(routes, "j", original_j) 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( ("source_tag", "source_label"), [("telegram", "Telegram"), ("discord", "Discord")], @@ -71,7 +126,7 @@ def test_session_clear_preserves_imported_messaging_transcript_and_blocks_state_ source_label, ): 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) @@ -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("assistant", f"{source_label} external reply", 11.0, f"{source_tag}-ext-a1"), ] - external_before = copy.deepcopy(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) + _make_state_db(tmp_path / "state.db", sid, source_tag, external_messages) 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_user_source": null' in persisted - assert external_messages == external_before - assert state_db_messages == state_db_before + state_db_messages = get_cli_session_messages(sid) + 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( - loaded, - cli_messages=external_messages, - ) - assert display_messages == external_messages + handler = _GetHandler(f"/api/session?session_id={sid}&resolve_model=0") + routes.handle_get(handler, urlparse(handler.path)) + assert handler.status == 200 + payload_messages = handler.response_json["session"]["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( loaded.messages,