feat: improve session navigation
This commit is contained in:
@@ -81,6 +81,16 @@ class ChatServiceTests(unittest.TestCase):
|
||||
"Gesundheitsfrage",
|
||||
)
|
||||
|
||||
def test_manual_title_is_normalized_and_limited(self) -> None:
|
||||
session = self.service.new_session()
|
||||
|
||||
renamed = self.service.rename_session(session.id, " Mein Titel ")
|
||||
shortened = self.service.rename_session(session.id, "x" * 80)
|
||||
|
||||
self.assertEqual(renamed.title, "Mein Titel")
|
||||
self.assertEqual(len(shortened.title), 60)
|
||||
self.assertTrue(shortened.title.endswith("…"))
|
||||
|
||||
def test_streaming_response_is_persisted_exactly_once_after_completion(self) -> None:
|
||||
provider = StreamingProvider()
|
||||
service = ChatService(self.service.store, provider)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import sqlite3
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
@@ -167,6 +168,76 @@ class CliTests(unittest.TestCase):
|
||||
)
|
||||
self.assertIn("Gemini-Schlüssel vorhanden: ja", output)
|
||||
|
||||
def test_numbered_sessions_load_rename_and_search(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
store = SQLiteSessionStore(Path(directory) / "sessions.sqlite3")
|
||||
service = ChatService(store, _FakeProvider())
|
||||
first = service.new_session()
|
||||
service.send(first.id, "Erste Frage")
|
||||
second = service.new_session()
|
||||
service.send(second.id, "Zweite Frage")
|
||||
shown_before = service.list_sessions()
|
||||
target = shown_before[1]
|
||||
inputs = iter(
|
||||
[
|
||||
"/sessions",
|
||||
"/load 2",
|
||||
"/rename Gefundene Sitzung",
|
||||
"/search Gefundene",
|
||||
"/exit",
|
||||
]
|
||||
)
|
||||
output: list[str] = []
|
||||
|
||||
result = run_chat(
|
||||
service,
|
||||
session_id=first.id,
|
||||
input_fn=lambda _prompt: next(inputs),
|
||||
output=output.append,
|
||||
)
|
||||
renamed_title = store.get_session(target.id).title
|
||||
|
||||
self.assertEqual(result, 0)
|
||||
self.assertTrue(any(line.startswith("1. ") and "Frage" in line for line in output))
|
||||
self.assertIn(f"Sitzung geladen: {target.id}", output)
|
||||
self.assertEqual(renamed_title, "Gefundene Sitzung")
|
||||
self.assertTrue(any("Gefundene Sitzung" in line for line in output))
|
||||
|
||||
def test_load_accepts_unique_prefix_and_rejects_ambiguous_prefix(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
database = Path(directory) / "sessions.sqlite3"
|
||||
store = SQLiteSessionStore(database)
|
||||
service = ChatService(store, _FakeProvider())
|
||||
active = service.new_session()
|
||||
with sqlite3.connect(database) as connection:
|
||||
connection.executemany(
|
||||
"""
|
||||
INSERT INTO sessions
|
||||
(id, created_at, updated_at, provider, model, title, last_provider)
|
||||
VALUES (?, '2026-01-01', '2026-01-01',
|
||||
'ollama', 'test-model', ?, 'ollama')
|
||||
""",
|
||||
(
|
||||
("shared-111", "Eins"),
|
||||
("shared-222", "Zwei"),
|
||||
("unique-333", "Drei"),
|
||||
),
|
||||
)
|
||||
connection.close()
|
||||
inputs = iter(["/load unique", "/load shared", "/exit"])
|
||||
output: list[str] = []
|
||||
|
||||
result = run_chat(
|
||||
service,
|
||||
session_id=active.id,
|
||||
input_fn=lambda _prompt: next(inputs),
|
||||
output=output.append,
|
||||
)
|
||||
|
||||
self.assertEqual(result, 0)
|
||||
self.assertIn("Sitzung geladen: unique-333", output)
|
||||
self.assertTrue(any("nicht eindeutig" in line for line in output))
|
||||
|
||||
def test_gemini_configuration_defaults_to_no(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "javis.toml"
|
||||
|
||||
@@ -3,14 +3,18 @@ import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from javis.memory.sqlite_store import SessionNotFoundError, SQLiteSessionStore
|
||||
from javis.memory.sqlite_store import (
|
||||
AmbiguousSessionReferenceError,
|
||||
SessionNotFoundError,
|
||||
SQLiteSessionStore,
|
||||
)
|
||||
|
||||
|
||||
class SQLiteSessionStoreTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.temporary_directory = tempfile.TemporaryDirectory()
|
||||
database = Path(self.temporary_directory.name) / "sessions.sqlite3"
|
||||
self.store = SQLiteSessionStore(database)
|
||||
self.database = Path(self.temporary_directory.name) / "sessions.sqlite3"
|
||||
self.store = SQLiteSessionStore(self.database)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.temporary_directory.cleanup()
|
||||
@@ -119,6 +123,49 @@ class SQLiteSessionStoreTests(unittest.TestCase):
|
||||
["Alte Nachricht"],
|
||||
)
|
||||
|
||||
def test_rename_and_search_only_titles_and_user_messages(self) -> None:
|
||||
first = self.store.create_session("ollama", "test-model")
|
||||
second = self.store.create_session("ollama", "test-model")
|
||||
self.store.rename_session(first.id, "SQLite Hilfe")
|
||||
self.store.append_exchange(first.id, "Transaktion erklären", "Privates Lösungswort")
|
||||
self.store.append_exchange(second.id, "Andere Frage", "Nur SQLite in Antwort")
|
||||
|
||||
self.assertEqual(self.store.get_session(first.id).title, "SQLite Hilfe")
|
||||
self.assertEqual(
|
||||
[session.id for session in self.store.search_sessions("Transaktion")],
|
||||
[first.id],
|
||||
)
|
||||
self.assertEqual(self.store.search_sessions("Lösungswort"), [])
|
||||
self.assertEqual(self.store.search_sessions("SQLite"), [self.store.get_session(first.id)])
|
||||
|
||||
def test_search_treats_sql_wildcards_and_injection_as_plain_text(self) -> None:
|
||||
session = self.store.create_session("ollama", "test-model")
|
||||
self.store.rename_session(session.id, "100% SQLite_Name")
|
||||
|
||||
self.assertEqual(self.store.search_sessions("%")[0].id, session.id)
|
||||
self.assertEqual(self.store.search_sessions("_")[0].id, session.id)
|
||||
self.assertEqual(self.store.search_sessions("' OR 1=1 --"), [])
|
||||
|
||||
def test_unique_and_ambiguous_session_prefixes(self) -> None:
|
||||
with sqlite3.connect(self.database) as connection:
|
||||
connection.executemany(
|
||||
"""
|
||||
INSERT INTO sessions
|
||||
(id, created_at, updated_at, provider, model, title, last_provider)
|
||||
VALUES (?, '2026-01-01', '2026-01-01', 'ollama', 'model', ?, 'ollama')
|
||||
""",
|
||||
(
|
||||
("abc111", "Eins"),
|
||||
("abc222", "Zwei"),
|
||||
("unique333", "Drei"),
|
||||
),
|
||||
)
|
||||
connection.close()
|
||||
|
||||
self.assertEqual(self.store.resolve_unique_prefix("unique").id, "unique333")
|
||||
with self.assertRaises(AmbiguousSessionReferenceError):
|
||||
self.store.resolve_unique_prefix("abc")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user