#!/usr/bin/env python3 """ tests/test_b1_tier3_identity.py — B-1 tier-3 identity cache regression suite (V-1..V-8). """ import os import shutil import sqlite3 import json import subprocess import tempfile import unittest import pytest REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) LIB_SH = os.path.join(REPO_ROOT, ".agents", "skills", "lib.sh") @pytest.fixture def split_sandbox(tmp_path): """ Creates a sandbox where workspace directory != state file directory, breaking the coincidence in mam_sandbox where /.mam/ matches AGENT_SESSIONS_YAML. """ shutil.copytree(os.path.join(REPO_ROOT, ".agents", "skills"), tmp_path / ".agents" / "skills") ws = tmp_path / "ws" (ws / ".mam").mkdir(parents=True) state_dir = tmp_path / "state" state_dir.mkdir() home = tmp_path / "home" (home / ".claude" / "projects").mkdir(parents=True) (home / ".gemini" / "antigravity-cli" / "conversations").mkdir(parents=True) yaml_file = state_dir / "agent-sessions.yaml" db_file = state_dir / "agent-sessions.db" return { "root": tmp_path, "ws": ws, "yaml": yaml_file, "db": db_file, "home": home, "lib": tmp_path / ".agents" / "skills" / "lib.sh" } def run_find_workspace_uuid(sandbox, workspace, agent, env_extra=None, target=None): env = dict(os.environ) env["AGENT_SESSIONS_YAML"] = str(sandbox["yaml"]) env["YAML_PATH"] = str(sandbox["yaml"]) env["WORKSPACE_ROOT"] = str(sandbox["root"]) env["HOME_DIR"] = str(sandbox["home"]) env["CLAUDE_PROJECT_DIR"] = str(sandbox["home"] / ".claude" / "projects") if env_extra: env.update(env_extra) target_arg = f" --target '{target}'" if target else "" cmd = f"source {sandbox['lib']} && find_workspace_uuid '{workspace}' '{agent}'{target_arg}" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env=env) return res class TestB1Tier3Identity(unittest.TestCase): def setUp(self): self.tmp_dir = tempfile.mkdtemp(prefix="mam_b1_test_") self.tmp_path = tempfile.TemporaryDirectory() self.sandbox = { "root": self.tmp_dir, "ws": os.path.join(self.tmp_dir, "ws"), "yaml": os.path.join(self.tmp_dir, "state", "agent-sessions.yaml"), "db": os.path.join(self.tmp_dir, "state", "agent-sessions.db"), "home": os.path.join(self.tmp_dir, "home"), "lib": LIB_SH } os.makedirs(self.sandbox["ws"], exist_ok=True) os.makedirs(os.path.join(self.sandbox["tmp_dir"] if "tmp_dir" in self.sandbox else self.tmp_dir, "state"), exist_ok=True) os.makedirs(os.path.join(self.sandbox["home"], ".claude", "projects"), exist_ok=True) def tearDown(self): shutil.rmtree(self.tmp_dir, ignore_errors=True) def test_b1_tier3_honours_agent_sessions_yaml_path(split_sandbox): """V-1: D1 — verify tier-3 honours AGENT_SESSIONS_YAML path when ws != state dir.""" ws = str(split_sandbox["ws"]) uuid = "b1-test-uuid-0001" # Create mock agy conversation DB db_file = split_sandbox["home"] / ".gemini" / "antigravity-cli" / "conversations" / f"{uuid}.db" conn = sqlite3.connect(db_file) conn.execute("CREATE TABLE steps (id INT)") conn.execute("INSERT INTO steps VALUES (1)") conn.commit() conn.close() # Write identity info into yaml at state_dir, NOT ws/.mam/ yaml_content = { "herdr_sessions": [], "agent_identities": { "agy": { "project_cwd": ws, "conversation_id": uuid } } } import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) res = run_find_workspace_uuid(split_sandbox, ws, "agy") assert res.returncode == 0 assert res.stdout.strip() == uuid def test_b1_tier3_db_branch_survives_missing_pyyaml(split_sandbox): """V-2: D3 — verify tier-3 DB branch works even when PyYAML module is absent.""" ws = str(split_sandbox["ws"]) uuid = "b1-test-uuid-0002" # Create mock agy conversation DB db_file = split_sandbox["home"] / ".gemini" / "antigravity-cli" / "conversations" / f"{uuid}.db" conn_conv = sqlite3.connect(db_file) conn_conv.execute("CREATE TABLE steps (id INT)") conn_conv.execute("INSERT INTO steps VALUES (1)") conn_conv.commit() conn_conv.close() # Create SQLite DB with state data conn = sqlite3.connect(split_sandbox["db"]) conn.execute("CREATE TABLE state (id INTEGER PRIMARY KEY, data TEXT)") state_data = json.dumps({ "herdr_sessions": [], "agent_identities": { "agy": { "project_cwd": ws, "conversation_id": uuid } } }) conn.execute("INSERT INTO state (id, data) VALUES (1, ?)", (state_data,)) conn.commit() conn.close() # Stub out PyYAML via PYTHONPATH override with broken yaml package stub_dir = split_sandbox["root"] / "noyaml" / "yaml" stub_dir.mkdir(parents=True) with open(stub_dir / "__init__.py", "w") as f: f.write("raise ImportError('No module named yaml')\n") env_extra = {"PYTHONPATH": str(split_sandbox["root"] / "noyaml")} res = run_find_workspace_uuid(split_sandbox, ws, "agy", env_extra=env_extra) assert res.returncode == 0 assert res.stdout.strip() == uuid def test_b1_tier3_corrupt_identities_still_exits_zero(split_sandbox): """V-3: D4 — verify non-dict agent_identities handles gracefully and exits 0.""" ws = str(split_sandbox["ws"]) yaml_content = { "herdr_sessions": [], "agent_identities": "corrupted_string_not_dict" } import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) # Also write to ws/.mam/ to trigger HEAD's path-guessing code path with open(split_sandbox["ws"] / ".mam" / "agent-sessions.yaml", "w") as f: yaml.dump(yaml_content, f) res = run_find_workspace_uuid(split_sandbox, ws, "agy") assert res.returncode == 0 assert res.stdout.strip() == "" def test_b1_tier3_hermes_conversation_id_fallback(split_sandbox): """V-4: D5 — verify hermes tier-3 fallback reads conversation_id from ai_agent.""" ws = str(split_sandbox["ws"]) uuid = "b1-test-uuid-hermes-0004" # Create mock hermes state DB hermes_dir = split_sandbox["home"] / ".hermes" hermes_dir.mkdir(parents=True, exist_ok=True) conn_h = sqlite3.connect(hermes_dir / "state.db") conn_h.execute("CREATE TABLE sessions (id TEXT, cwd TEXT, started_at TEXT)") conn_h.execute("INSERT INTO sessions VALUES (?, ?, ?)", (uuid, ws, "2026-08-05")) conn_h.commit() conn_h.close() yaml_content = { "herdr_sessions": [], "agent_identities": { "hermes": { "project_cwd": ws, "conversation_id": uuid } } } import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) with open(split_sandbox["ws"] / ".mam" / "agent-sessions.yaml", "w") as f: yaml.dump(yaml_content, f) res = run_find_workspace_uuid(split_sandbox, ws, "hermes") assert res.returncode == 0 assert res.stdout.strip() == uuid def test_b1_tier3_refuses_foreign_workspace_identity(split_sandbox): """V-5: Verify tier-3 identity is ignored if project_cwd does not match workspace.""" ws = str(split_sandbox["ws"]) foreign_ws = str(split_sandbox["root"] / "other_ws") uuid = "b1-test-uuid-0005" yaml_content = { "herdr_sessions": [], "agent_identities": { "agy": { "project_cwd": foreign_ws, "conversation_id": uuid } } } import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) res = run_find_workspace_uuid(split_sandbox, ws, "agy") assert res.returncode == 0 assert res.stdout.strip() == "" def test_b1_tier3_absent_identities_is_silent(split_sandbox): """V-6: Verify tier-3 gracefully returns empty string when agent_identities is absent.""" ws = str(split_sandbox["ws"]) yaml_content = {"herdr_sessions": []} import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) res = run_find_workspace_uuid(split_sandbox, ws, "agy") assert res.returncode == 0 assert res.stdout.strip() == "" def test_b1_load_state_json_surfaces_agent_identities(split_sandbox): """V-7: Verify load_state_json preserves agent_identities in state blob.""" ws = str(split_sandbox["ws"]) uuid = "b1-test-uuid-0007" yaml_content = { "herdr_sessions": [], "agent_identities": { "agy": { "project_cwd": ws, "conversation_id": uuid } } } import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) env = dict(os.environ) env["AGENT_SESSIONS_YAML"] = str(split_sandbox["yaml"]) env["YAML_PATH"] = str(split_sandbox["yaml"]) cmd = f"source {split_sandbox['lib']} && load_state_json" res = subprocess.run(["bash", "-c", cmd], capture_output=True, text=True, env=env) assert res.returncode == 0 state = json.loads(res.stdout.strip()) assert state.get("agent_identities", {}).get("agy", {}).get("conversation_id") == uuid def test_b1_tier3_does_not_read_yaml_mirror_behind_the_db(split_sandbox): """V-8: Verify tier-3 does not prioritize YAML mirror when DB exists without identity.""" ws = str(split_sandbox["ws"]) uuid = "b1-test-uuid-0008" # DB has no agent_identities conn = sqlite3.connect(split_sandbox["db"]) conn.execute("CREATE TABLE state (id INTEGER PRIMARY KEY, data TEXT)") state_data = json.dumps({"herdr_sessions": []}) conn.execute("INSERT INTO state (id, data) VALUES (1, ?)", (state_data,)) conn.commit() conn.close() # YAML has agent_identities yaml_content = { "herdr_sessions": [], "agent_identities": { "agy": { "project_cwd": ws, "conversation_id": uuid } } } import yaml with open(split_sandbox["yaml"], "w") as f: yaml.dump(yaml_content, f) res = run_find_workspace_uuid(split_sandbox, ws, "agy") assert res.returncode == 0 # Must miss because DB is authority and state row lacks identity assert res.stdout.strip() == ""