generated from coulomb/repo-seed
session-memory Phase 0: normalized schema (T01) + Claude adapter (T02)
- session_memory/core/schema.py: Session/SessionEvent/Cost dataclasses, flavor-prefixed uids, watermarks, kind/outcome validation (T01) - session_memory/adapters/claude.py: JSONL -> Normalized bundle, turn DAG via uuid/parentUuid, kind mapping, cost from message.usage (T02) - tests: schema round-trip + adapter (synthetic + real local session) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
97
tests/test_schema.py
Normal file
97
tests/test_schema.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Round-trip + validation tests for the normalized schema (T01)."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from session_memory.core.schema import ( # noqa: E402
|
||||
SCHEMA_VERSION,
|
||||
Cost,
|
||||
Session,
|
||||
SessionEvent,
|
||||
)
|
||||
|
||||
|
||||
def _sample_session() -> Session:
|
||||
return Session(
|
||||
session_uid=Session.make_uid("claude", "abc-123"),
|
||||
flavor="claude",
|
||||
native_session_id="abc-123",
|
||||
repo="agentic-resources",
|
||||
domain="helix_forge",
|
||||
cwd="/home/worsch/agentic-resources",
|
||||
git_branch="main",
|
||||
model="claude-opus-4-8",
|
||||
started_at="2026-06-06T10:00:00Z",
|
||||
ended_at="2026-06-06T10:15:00Z",
|
||||
outcome="success",
|
||||
cost=Cost(input_tokens=100, output_tokens=50, turns=3, retries=1),
|
||||
task_ref="AGENTIC-WP-0002-T01",
|
||||
source_path="~/.claude/projects/x/abc-123.jsonl",
|
||||
source_bytes=2048,
|
||||
ingested_at="2026-06-06T10:16:00Z",
|
||||
)
|
||||
|
||||
|
||||
def test_session_round_trip():
|
||||
s = _sample_session()
|
||||
restored = Session.from_json(s.to_json())
|
||||
assert restored == s
|
||||
assert restored.cost == s.cost
|
||||
assert restored.schema_version == SCHEMA_VERSION
|
||||
|
||||
|
||||
def test_session_uid_helper_and_prefix_enforced():
|
||||
assert Session.make_uid("grok", "z9") == "grok:z9"
|
||||
with pytest.raises(ValueError):
|
||||
Session(session_uid="codex:wrong", flavor="claude", native_session_id="wrong")
|
||||
|
||||
|
||||
def test_unknown_flavor_and_outcome_rejected():
|
||||
with pytest.raises(ValueError):
|
||||
Session(session_uid="x:1", flavor="x", native_session_id="1")
|
||||
with pytest.raises(ValueError):
|
||||
Session(
|
||||
session_uid="claude:1",
|
||||
flavor="claude",
|
||||
native_session_id="1",
|
||||
outcome="bogus",
|
||||
)
|
||||
|
||||
|
||||
def test_is_evictable_requires_analyzed_not_evicted():
|
||||
s = _sample_session()
|
||||
assert s.is_evictable is False # not analyzed yet
|
||||
s.analyzed_at = "2026-06-06T10:17:00Z"
|
||||
assert s.is_evictable is True
|
||||
s.evicted_at = "2026-06-06T11:00:00Z"
|
||||
assert s.is_evictable is False # already evicted
|
||||
|
||||
|
||||
def test_event_round_trip_and_kind_validation():
|
||||
e = SessionEvent(
|
||||
session_uid="claude:abc-123",
|
||||
seq=4,
|
||||
parent_seq=3,
|
||||
ts="2026-06-06T10:01:00Z",
|
||||
kind="tool_call",
|
||||
role="assistant",
|
||||
tool="Bash",
|
||||
summary="ran pytest -q",
|
||||
payload_ref="blob://abc-123/4",
|
||||
tokens=12,
|
||||
)
|
||||
assert SessionEvent.from_json(e.to_json()) == e
|
||||
with pytest.raises(ValueError):
|
||||
SessionEvent(session_uid="claude:1", seq=0, kind="not_a_kind")
|
||||
|
||||
|
||||
def test_from_dict_ignores_unknown_fields():
|
||||
d = _sample_session().to_dict()
|
||||
d["future_field"] = "ignored"
|
||||
d["cost"]["future_cost"] = 999
|
||||
restored = Session.from_dict(d)
|
||||
assert restored.repo == "agentic-resources"
|
||||
Reference in New Issue
Block a user