96 lines
4.2 KiB
Python
96 lines
4.2 KiB
Python
"""Regression tests for #3994 / #3985 — _get_or_materialize_session().
|
|
|
|
rename / move / update of a CLI/agent session that isn't yet in the WebUI store
|
|
should materialize it from CLI metadata (mirroring /api/session/archive) instead
|
|
of 404ing — while still refusing to mutate a read-only (messaging / Claude Code)
|
|
session.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
|
|
def test_materialize_returns_in_store_session_directly():
|
|
"""When get_session() succeeds, the helper returns it (after full-load) untouched."""
|
|
import api.routes as routes
|
|
|
|
existing = SimpleNamespace(session_id="s1", profile="default", messages=[])
|
|
with patch("api.routes.get_session", return_value=existing), \
|
|
patch("api.routes._ensure_full_session_before_mutation", return_value=existing):
|
|
out = routes._get_or_materialize_session("s1")
|
|
assert out is existing
|
|
|
|
|
|
def test_materialize_missing_everywhere_raises_keyerror():
|
|
"""No WebUI session and no CLI metadata → KeyError (caller maps to 404)."""
|
|
import api.routes as routes
|
|
|
|
with patch("api.routes.get_session", side_effect=KeyError("s1")), \
|
|
patch("api.routes._lookup_cli_session_metadata", return_value={}):
|
|
with pytest.raises(KeyError):
|
|
routes._get_or_materialize_session("s1")
|
|
|
|
|
|
def test_materialize_readonly_session_raises_permissionerror():
|
|
"""A read-only imported session (messaging / Claude Code) must not be materialized
|
|
for mutation — the helper raises PermissionError (caller maps to 403)."""
|
|
import api.routes as routes
|
|
|
|
with patch("api.routes.get_session", side_effect=KeyError("ro1")), \
|
|
patch("api.routes._lookup_cli_session_metadata", return_value={"read_only": True, "source_tag": "claude_code"}):
|
|
with pytest.raises(PermissionError):
|
|
routes._get_or_materialize_session("ro1")
|
|
|
|
|
|
def test_materialize_cli_session_imports_full_history():
|
|
"""A regular (non-messaging, non-read-only) CLI session is materialized via
|
|
import_cli_session with its message history."""
|
|
import api.routes as routes
|
|
|
|
cli_meta = {
|
|
"read_only": False,
|
|
"title": "CLI chat",
|
|
"model": "gpt-test",
|
|
"profile": "default",
|
|
"source_tag": "cli",
|
|
}
|
|
imported = SimpleNamespace(session_id="cli1", profile="default", messages=[{"role": "user", "content": "hi"}])
|
|
with patch("api.routes.get_session", side_effect=KeyError("cli1")), \
|
|
patch("api.routes._lookup_cli_session_metadata", return_value=cli_meta), \
|
|
patch("api.routes._is_messaging_session_record", return_value=False), \
|
|
patch("api.routes.get_cli_session_messages", return_value=[{"role": "user", "content": "hi"}]), \
|
|
patch("api.routes.title_from", return_value="CLI chat"), \
|
|
patch("api.routes.import_cli_session", return_value=imported) as mock_import:
|
|
out = routes._get_or_materialize_session("cli1")
|
|
assert out is imported
|
|
assert mock_import.called
|
|
# source metadata is stamped onto the materialized session
|
|
assert getattr(out, "is_cli_session", None) is True
|
|
|
|
|
|
def test_materialize_messaging_session_builds_lightweight_stub():
|
|
"""A messaging session is materialized as a lightweight Session (state.db is the
|
|
source of truth) rather than importing a full message history."""
|
|
import api.routes as routes
|
|
|
|
cli_meta = {
|
|
"read_only": False,
|
|
"title": "tg chat",
|
|
"model": "gpt-test",
|
|
"source_tag": "telegram",
|
|
}
|
|
with patch("api.routes.get_session", side_effect=KeyError("msg1")), \
|
|
patch("api.routes._lookup_cli_session_metadata", return_value=cli_meta), \
|
|
patch("api.routes._is_messaging_session_record", return_value=True), \
|
|
patch("api.routes.get_cli_session_messages", return_value=[]), \
|
|
patch("api.routes.title_from", return_value="tg chat"), \
|
|
patch("api.routes.get_last_workspace", return_value="/tmp/ws"), \
|
|
patch("api.routes.Session") as MockSession:
|
|
stub = MockSession.return_value
|
|
stub.save = lambda **_kw: None
|
|
out = routes._get_or_materialize_session("msg1")
|
|
assert out is MockSession.return_value
|
|
assert MockSession.called
|