Skip to content

Commit 7df4e24

Browse files
committed
test(hermes): add opt-in real CLI smoke and state.db fixture tests
- Rewrite test_hermes_cli_available_and_mocked into two opt-in smoke tests: * test_hermes_cli_available: fast hermes --version probe (skip if missing) * test_real_hermes_chat_smoke: real CLI invocation + backend-path call (opt-in via SKILLOPT_REAL_HERMES=1; uses -Q --max-turns 1 flags) - Add tests/test_harvest_hermes.py with sanitized synthetic state.db fixture: * 6 core harvest tests (engine-session filtering, scope, since_iso, limit, digest content, missing db) * 7 schema-drift variants (no_title, no_tool_name, extra_columns, no_messages_table, no_sessions_table, schema_version_25, text_epochs) - Fix harvest_hermes.py: graceful handling of missing tables/columns (_fetch_messages catches OperationalError for missing tool_name or table; harvest_hermes catches missing sessions table and missing title column) - Register pytest 'slow' marker in pyproject.toml
1 parent 038595a commit 7df4e24

4 files changed

Lines changed: 776 additions & 33 deletions

File tree

‎pyproject.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ include = ["skillopt", "skillopt.*", "skillopt_sleep", "skillopt_sleep.*", "skil
8080
line-length = 120
8181
target-version = "py310"
8282

83+
[tool.pytest.ini_options]
84+
markers = ["slow: opt-in tests that invoke the real Hermes CLI or network"]
85+
8386
[tool.ruff.lint]
8487
select = ["E", "F", "I", "W"]
8588
ignore = ["E501"]

‎skillopt_sleep/harvest_hermes.py‎

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,36 @@ def _filter_engine_sessions(sessions: List[Dict[str, Any]]) -> List[Dict[str, An
3838

3939

4040
def _fetch_messages(db_path: str, session_id: str) -> List[Dict[str, Any]]:
41-
"""Return all messages for a session, ordered by id."""
41+
"""Return all messages for a session, ordered by id.
42+
43+
Gracefully handles the ``tool_name`` column or whole ``messages`` table
44+
being absent (schema drift).
45+
"""
4246
conn = sqlite3.connect(db_path)
4347
conn.row_factory = sqlite3.Row
4448
cursor = conn.cursor()
45-
cursor.execute(
46-
"""SELECT role, content, tool_name, timestamp
47-
FROM messages
48-
WHERE session_id = ? AND role IN ('user', 'assistant')
49-
ORDER BY id""",
50-
(session_id,),
51-
)
49+
try:
50+
try:
51+
cursor.execute(
52+
"""SELECT role, content, tool_name, timestamp
53+
FROM messages
54+
WHERE session_id = ? AND role IN ('user', 'assistant')
55+
ORDER BY id""",
56+
(session_id,),
57+
)
58+
except sqlite3.OperationalError:
59+
# Column or table mismatch — retry without tool_name
60+
cursor.execute(
61+
"""SELECT role, content, timestamp
62+
FROM messages
63+
WHERE session_id = ? AND role IN ('user', 'assistant')
64+
ORDER BY id""",
65+
(session_id,),
66+
)
67+
except sqlite3.OperationalError:
68+
# Table missing entirely
69+
conn.close()
70+
return []
5271
rows = [dict(r) for r in cursor.fetchall()]
5372
conn.close()
5473
return rows
@@ -202,13 +221,28 @@ def harvest_hermes(
202221
where += " AND ended_at >= ?"
203222
params.append(since_epoch)
204223

205-
cursor.execute(
206-
f"""SELECT id, cwd, title, started_at, ended_at, model
207-
FROM sessions
208-
{where}
209-
ORDER BY ended_at DESC""",
210-
params,
211-
)
224+
try:
225+
cursor.execute(
226+
f"""SELECT id, cwd, title, started_at, ended_at, model
227+
FROM sessions
228+
{where}
229+
ORDER BY ended_at DESC""",
230+
params,
231+
)
232+
except sqlite3.OperationalError:
233+
# Table missing or column mismatch (e.g. no 'title' column) —
234+
# retry without title, or bail out if sessions table is absent.
235+
try:
236+
cursor.execute(
237+
f"""SELECT id, cwd, started_at, ended_at, model
238+
FROM sessions
239+
{where}
240+
ORDER BY ended_at DESC""",
241+
params,
242+
)
243+
except sqlite3.OperationalError:
244+
conn.close()
245+
return []
212246

213247
sessions = [dict(r) for r in cursor.fetchall()]
214248
conn.close()

0 commit comments

Comments
 (0)