diff --git a/clawmetry/sync.py b/clawmetry/sync.py index 7fe5470..dd08cc3 100644 --- a/clawmetry/sync.py +++ b/clawmetry/sync.py @@ -950,12 +950,16 @@ def sync_sessions_recent( # Build subagent map (same logic as sync_sessions) # Cache sessions.json for 60 seconds to avoid re-parsing every call + global _sessions_json_cache file_to_subagent_id: dict[str, str] = {} index_path = os.path.join(sessions_dir, "sessions.json") if os.path.isfile(index_path): try: current_mtime = os.path.getmtime(index_path) - if _sessions_json_cache["data"] is not None and _sessions_json_cache["mtime"] == current_mtime: + if ( + _sessions_json_cache["data"] is not None + and _sessions_json_cache["mtime"] == current_mtime + ): file_to_subagent_id = _sessions_json_cache["data"] else: with open(index_path) as _fi: @@ -964,8 +968,14 @@ def sync_sessions_recent( if ":subagent:" in _k and isinstance(_meta, dict): _sf = _meta.get("sessionFile", "") if _sf: - file_to_subagent_id[os.path.basename(_sf)] = _k.split(":")[-1] - _sessions_json_cache = {"ts": time.time(), "data": file_to_subagent_id.copy(), "mtime": current_mtime} + file_to_subagent_id[os.path.basename(_sf)] = _k.split(":")[ + -1 + ] + _sessions_json_cache = { + "ts": time.time(), + "data": file_to_subagent_id.copy(), + "mtime": current_mtime, + } except Exception: pass @@ -2976,8 +2986,6 @@ def _build_gateway_data(paths: dict = None) -> dict: time.sleep(15) - - def run_daemon() -> None: """Run the sync daemon - main loop for continuous synchronization.""" config = load_config() diff --git a/tests/test_sync_sessions_recent.py b/tests/test_sync_sessions_recent.py new file mode 100644 index 0000000..3fb7da3 --- /dev/null +++ b/tests/test_sync_sessions_recent.py @@ -0,0 +1,70 @@ +""" +Tests for sync_sessions_recent function - GH #007 + +Validates: +- sync_sessions_recent properly declares _sessions_json_cache as global +- Cache works correctly across multiple calls +""" + +from __future__ import annotations + +import ast +import json +import os +import tempfile +import unittest + + +os.environ["CLAWMETRY_NO_INTERCEPT"] = "1" + + +class TestSyncSessionsRecentCache(unittest.TestCase): + def test_sync_sessions_recent_has_global_declaration(self): + """sync_sessions_recent should declare _sessions_json_cache as global.""" + from clawmetry import sync + + source = open(sync.__file__).read() + tree = ast.parse(source) + + func_name = "sync_sessions_recent" + has_global = False + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef) and node.name == func_name: + for stmt in ast.walk(node): + if isinstance(stmt, ast.Global): + if "_sessions_json_cache" in stmt.names: + has_global = True + break + + self.assertTrue( + has_global, + "sync_sessions_recent is missing 'global _sessions_json_cache' declaration", + ) + + def test_sync_sessions_recent_cache_functional(self): + """sync_sessions_recent should work with sessions.json cache.""" + from clawmetry.sync import sync_sessions_recent + + with tempfile.TemporaryDirectory() as tmpdir: + sessions_dir = os.path.join(tmpdir, "sessions") + os.makedirs(sessions_dir) + + sessions_json = os.path.join(sessions_dir, "sessions.json") + with open(sessions_json, "w") as f: + json.dump({}, f) + + config = { + "api_key": "test-key", + "node_id": "test-node", + } + state = {} + paths = { + "sessions_dir": sessions_dir, + } + + result = sync_sessions_recent(config, state, paths, minutes=60) + self.assertIsInstance(result, int) + + +if __name__ == "__main__": + unittest.main()