e2d302dcaf
- Apply ruff format to store.py (split multi-arg conn.execute calls) and
steps file (collapse single-arg execute to one line)
- Fix AmbiguousStep: add literal quotes to @given/@then patterns so
'a session with id "{session_id}"' is unambiguous vs the
'in the database' variant; update all dependent then-steps consistently
- Mark if TYPE_CHECKING block with # pragma: no cover (never executed)
- Add scenario + step for default db_path to cover store.py lines 32-34
(uses unittest.mock.patch on Path.home to avoid touching real homedir)
ISSUES CLOSED: #10648
213 lines
7.7 KiB
Python
213 lines
7.7 KiB
Python
"""Step definitions for TUI session persistence."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from behave import given, then, when
|
|
|
|
from cleveragents.tui.app import SessionView
|
|
from cleveragents.tui.persistence import TuiSessionStore
|
|
|
|
|
|
@given("a clean TUI session database")
|
|
def step_clean_database(context: object) -> None:
|
|
"""Create a clean temporary database for testing."""
|
|
# Create a temporary directory for the test database
|
|
temp_dir = tempfile.mkdtemp()
|
|
db_path = Path(temp_dir) / "tui.db"
|
|
|
|
# Store in context for later use
|
|
context.temp_dir = temp_dir # type: ignore
|
|
context.db_path = db_path # type: ignore
|
|
context.store = TuiSessionStore(db_path=db_path) # type: ignore
|
|
|
|
|
|
@when("the TUI session store is initialized")
|
|
def step_initialize_store(context: object) -> None:
|
|
"""Initialize the session store (already done in background)."""
|
|
pass
|
|
|
|
|
|
@then("the SQLite database file exists at ~/.local/state/cleveragents/tui.db")
|
|
def step_database_exists(context: object) -> None:
|
|
"""Verify the database file exists."""
|
|
assert context.db_path.exists() # type: ignore
|
|
|
|
|
|
@then("the database has sessions and active_session tables")
|
|
def step_database_has_tables(context: object) -> None:
|
|
"""Verify the database has the required tables."""
|
|
import sqlite3
|
|
|
|
with sqlite3.connect(context.db_path) as conn: # type: ignore
|
|
cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
tables = {row[0] for row in cursor.fetchall()}
|
|
assert "sessions" in tables
|
|
assert "active_session" in tables
|
|
|
|
|
|
@given('a session with id "{session_id}" and transcript {transcript}')
|
|
def step_create_session_with_transcript(
|
|
context: object, session_id: str, transcript: str
|
|
) -> None:
|
|
"""Create a session with the given id and transcript."""
|
|
# Parse the transcript list from the string
|
|
import ast
|
|
|
|
transcript_list = ast.literal_eval(transcript)
|
|
session = SessionView(session_id=session_id, transcript=transcript_list)
|
|
context.session = session # type: ignore
|
|
|
|
|
|
@given('a session with id "{session_id}"')
|
|
def step_create_session(context: object, session_id: str) -> None:
|
|
"""Create a session with the given id."""
|
|
session = SessionView(session_id=session_id, transcript=[])
|
|
context.session = session # type: ignore
|
|
|
|
|
|
@when("I save the session to the database")
|
|
def step_save_session(context: object) -> None:
|
|
"""Save the session to the database."""
|
|
context.store.save_session(context.session) # type: ignore
|
|
|
|
|
|
@then("the session can be loaded from the database")
|
|
def step_load_session(context: object) -> None:
|
|
"""Load the session from the database."""
|
|
loaded = context.store.load_session(context.session.session_id) # type: ignore
|
|
assert loaded is not None
|
|
context.loaded_session = loaded # type: ignore
|
|
|
|
|
|
@then("the loaded session has the same id and transcript")
|
|
def step_verify_session_data(context: object) -> None:
|
|
"""Verify the loaded session matches the original."""
|
|
assert context.loaded_session.session_id == context.session.session_id # type: ignore
|
|
assert context.loaded_session.transcript == context.session.transcript # type: ignore
|
|
|
|
|
|
@when("I set it as the active session")
|
|
def step_set_active_session(context: object) -> None:
|
|
"""Set the session as active."""
|
|
context.store.set_active_session(context.session.session_id) # type: ignore
|
|
|
|
|
|
@then('the active session id is "{session_id}"')
|
|
def step_verify_active_session(context: object, session_id: str) -> None:
|
|
"""Verify the active session id."""
|
|
active = context.store.get_active_session() # type: ignore
|
|
assert active == session_id
|
|
|
|
|
|
@given("sessions with ids {session_ids}")
|
|
def step_create_multiple_sessions(context: object, session_ids: str) -> None:
|
|
"""Create multiple sessions."""
|
|
import ast
|
|
|
|
ids = ast.literal_eval(session_ids)
|
|
context.sessions = [ # type: ignore
|
|
SessionView(session_id=sid, transcript=[]) for sid in ids
|
|
]
|
|
|
|
|
|
@when("I save all sessions to the database")
|
|
def step_save_all_sessions(context: object) -> None:
|
|
"""Save all sessions to the database."""
|
|
for session in context.sessions: # type: ignore
|
|
context.store.save_session(session) # type: ignore
|
|
|
|
|
|
@then("listing sessions returns all three session ids")
|
|
def step_verify_list_sessions(context: object) -> None:
|
|
"""Verify all sessions are listed."""
|
|
listed = context.store.list_sessions() # type: ignore
|
|
expected_ids = {s.session_id for s in context.sessions} # type: ignore
|
|
assert set(listed) == expected_ids
|
|
|
|
|
|
@given('a session with id "{session_id}" in the database')
|
|
def step_create_and_save_session(context: object, session_id: str) -> None:
|
|
"""Create and save a session."""
|
|
session = SessionView(session_id=session_id, transcript=[])
|
|
context.store.save_session(session) # type: ignore
|
|
context.session = session # type: ignore
|
|
|
|
|
|
@when("I delete the session")
|
|
def step_delete_session(context: object) -> None:
|
|
"""Delete the session."""
|
|
context.store.delete_session(context.session.session_id) # type: ignore
|
|
|
|
|
|
@then("the session can no longer be loaded from the database")
|
|
def step_verify_session_deleted(context: object) -> None:
|
|
"""Verify the session is deleted."""
|
|
loaded = context.store.load_session(context.session.session_id) # type: ignore
|
|
assert loaded is None
|
|
|
|
|
|
@when("I clear all sessions")
|
|
def step_clear_all_sessions(context: object) -> None:
|
|
"""Clear all sessions."""
|
|
context.store.clear_all() # type: ignore
|
|
|
|
|
|
@then("the database has no sessions")
|
|
def step_verify_no_sessions(context: object) -> None:
|
|
"""Verify there are no sessions."""
|
|
listed = context.store.list_sessions() # type: ignore
|
|
assert len(listed) == 0
|
|
|
|
|
|
@then("there is no active session")
|
|
def step_verify_no_active_session(context: object) -> None:
|
|
"""Verify there is no active session."""
|
|
active = context.store.get_active_session() # type: ignore
|
|
assert active is None
|
|
|
|
|
|
@when("I save the session and set it as active")
|
|
def step_save_and_set_active(context: object) -> None:
|
|
"""Save the session and set it as active."""
|
|
context.store.save_session(context.session) # type: ignore
|
|
context.store.set_active_session(context.session.session_id) # type: ignore
|
|
|
|
|
|
@when("I create a new session store instance")
|
|
def step_create_new_store_instance(context: object) -> None:
|
|
"""Create a new session store instance."""
|
|
context.new_store = TuiSessionStore(db_path=context.db_path) # type: ignore
|
|
|
|
|
|
@then('the active session is "{session_id}"')
|
|
def step_verify_new_store_active_session(context: object, session_id: str) -> None:
|
|
"""Verify the active session in the new store."""
|
|
active = context.new_store.get_active_session() # type: ignore
|
|
assert active == session_id
|
|
|
|
|
|
@then("the session data is restored correctly")
|
|
def step_verify_session_restored(context: object) -> None:
|
|
"""Verify the session data is restored correctly."""
|
|
loaded = context.new_store.load_session(context.session.session_id) # type: ignore
|
|
assert loaded is not None
|
|
assert loaded.session_id == context.session.session_id # type: ignore
|
|
assert loaded.transcript == context.session.transcript # type: ignore
|
|
|
|
|
|
@given("the TUI session store is created with default database path")
|
|
def step_default_db_path(context: object) -> None:
|
|
"""Initialize store using the default path, patching home to a temp dir."""
|
|
from unittest.mock import patch
|
|
|
|
temp_dir = tempfile.mkdtemp()
|
|
temp_home = Path(temp_dir)
|
|
with patch.object(Path, "home", return_value=temp_home):
|
|
context.store = TuiSessionStore() # type: ignore
|
|
context.db_path = ( # type: ignore
|
|
temp_home / ".local" / "state" / "cleveragents" / "tui.db"
|
|
)
|