feat(harness): manifest archived/group fields + session mutation helpers

This commit is contained in:
2026-06-29 12:16:52 +02:00
parent 8167d41621
commit 60fa665d73
4 changed files with 111 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
"""Tests for session manifest mutations (rename, group, archive, delete)."""
import pytest
from tht.config import DatabaseConfig
from tht.session.store import (
SessionError,
create_session,
delete_session,
load_session,
set_archived,
set_group,
set_name,
)
def _db():
return DatabaseConfig(
database="testdb", user="u", password="p", # noqa: S106
**{"schema": "public"},
)
def test_manifest_defaults(tmp_path):
m = create_session("domanda", _db(), tmp_path)
assert m.archived is False
assert m.group is None
assert m.name is None
def test_set_name(tmp_path):
m = create_session("domanda", _db(), tmp_path)
set_name(m.id, "Pazienti 2024", tmp_path)
assert load_session(m.id, tmp_path).name == "Pazienti 2024"
def test_set_name_empty_clears(tmp_path):
m = create_session("domanda", _db(), tmp_path)
set_name(m.id, "x", tmp_path)
set_name(m.id, "", tmp_path)
assert load_session(m.id, tmp_path).name is None
def test_set_group_and_clear(tmp_path):
m = create_session("domanda", _db(), tmp_path)
set_group(m.id, "Aritmologia", tmp_path)
assert load_session(m.id, tmp_path).group == "Aritmologia"
set_group(m.id, "", tmp_path)
assert load_session(m.id, tmp_path).group is None
def test_set_archived(tmp_path):
m = create_session("domanda", _db(), tmp_path)
set_archived(m.id, True, tmp_path)
assert load_session(m.id, tmp_path).archived is True
set_archived(m.id, False, tmp_path)
assert load_session(m.id, tmp_path).archived is False
def test_delete_session(tmp_path):
m = create_session("domanda", _db(), tmp_path)
delete_session(m.id, tmp_path)
assert not (tmp_path / m.id).exists()
with pytest.raises(SessionError):
load_session(m.id, tmp_path)
def test_delete_missing_raises(tmp_path):
with pytest.raises(SessionError):
delete_session("nope", tmp_path)
+3
View File
@@ -44,6 +44,9 @@ def _list_sessions(sessions_root: Path) -> list[dict]:
"created_at": m.created_at.isoformat(),
"updated_at": m.updated_at.isoformat() if m.updated_at else None,
"author": m.author,
"name": m.name,
"group": m.group,
"archived": m.archived,
})
out.sort(key=lambda r: r["created_at"], reverse=True)
return out
+2
View File
@@ -43,6 +43,8 @@ class SessionManifest(_YamlModel):
model: str | None = None
thinking: str | None = None
name: str | None = None
archived: bool = False
group: str | None = None
class Candidate(BaseModel):
+37
View File
@@ -1,4 +1,5 @@
import os
import shutil
from datetime import UTC, datetime
from pathlib import Path
@@ -127,3 +128,39 @@ def close_session(session_id: str, sessions_root: Path) -> SessionManifest:
manifest.updated_by = current_author()
manifest.to_yaml(sessions_root / session_id / MANIFEST)
return manifest
def _save_touched(manifest: SessionManifest, sessions_root: Path) -> SessionManifest:
"""Persist `manifest` updating updated_at/updated_by (single save path for mutations)."""
manifest.updated_at = datetime.now(UTC)
manifest.updated_by = current_author()
manifest.to_yaml(sessions_root / manifest.id / MANIFEST)
return manifest
def set_name(session_id: str, name: str | None, sessions_root: Path) -> SessionManifest:
"""Set the descriptive name (empty/blank clears it back to None)."""
manifest = load_session(session_id, sessions_root)
manifest.name = (name or "").strip() or None
return _save_touched(manifest, sessions_root)
def set_group(session_id: str, group: str | None, sessions_root: Path) -> SessionManifest:
"""Set the group (empty/blank clears it back to None)."""
manifest = load_session(session_id, sessions_root)
manifest.group = (group or "").strip() or None
return _save_touched(manifest, sessions_root)
def set_archived(session_id: str, archived: bool, sessions_root: Path) -> SessionManifest:
"""Flip the archived flag. Archiving does NOT change resumability (a finalized
session stays read-only); unarchive only moves it back to the active list."""
manifest = load_session(session_id, sessions_root)
manifest.archived = archived
return _save_touched(manifest, sessions_root)
def delete_session(session_id: str, sessions_root: Path) -> None:
"""Hard-delete the session directory. SessionError if it does not exist."""
load_session(session_id, sessions_root) # raises SessionError if absent
shutil.rmtree(sessions_root / session_id)