From 41901dd0da2b2faf518d6abfe70af48611f0b6dc Mon Sep 17 00:00:00 2001 From: Farukest Date: Wed, 4 Mar 2026 06:41:46 +0300 Subject: [PATCH] fix(gateway): detect prior sessions from disk in has_any_sessions() --- gateway/session.py | 7 ++- tests/gateway/test_session.py | 88 +++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/gateway/session.py b/gateway/session.py index b59196b81c45..b84ba9a4c967 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -296,6 +296,7 @@ def __init__(self, sessions_dir: Path, config: GatewayConfig, self.config = config self._entries: Dict[str, SessionEntry] = {} self._loaded = False + self._loaded_from_disk = False self._has_active_processes_fn = has_active_processes_fn self._on_auto_reset = on_auto_reset # callback(old_entry) before auto-reset @@ -321,9 +322,11 @@ def _ensure_loaded(self) -> None: data = json.load(f) for key, entry_data in data.items(): self._entries[key] = SessionEntry.from_dict(entry_data) + if data: + self._loaded_from_disk = True except Exception as e: print(f"[gateway] Warning: Failed to load sessions: {e}") - + self._loaded = True def _save(self) -> None: @@ -392,7 +395,7 @@ def _should_reset(self, entry: SessionEntry, source: SessionSource) -> bool: def has_any_sessions(self) -> bool: """Check if any sessions have ever been created (across all platforms).""" self._ensure_loaded() - return len(self._entries) > 1 # >1 because the current new session is already in _entries + return self._loaded_from_disk or len(self._entries) > 1 def get_or_create_session( self, diff --git a/tests/gateway/test_session.py b/tests/gateway/test_session.py index 97e610da1c32..15aa5b3751f2 100644 --- a/tests/gateway/test_session.py +++ b/tests/gateway/test_session.py @@ -314,6 +314,94 @@ def test_rewrite_with_empty_list(self, store): assert reloaded == [] +class TestHasAnySessions: + """Regression: has_any_sessions() must detect prior sessions even for + single-platform users where _entries has only one key after a reset.""" + + @pytest.fixture() + def store(self, tmp_path): + config = GatewayConfig() + with patch("gateway.session.SessionStore._ensure_loaded"): + s = SessionStore(sessions_dir=tmp_path, config=config) + s._db = None + s._loaded = True + return s + + def test_fresh_install_no_sessions(self, store): + """Brand-new install with no entries → False.""" + store._entries = {} + store._loaded_from_disk = False + assert store.has_any_sessions() is False + + def test_single_platform_first_session(self, store): + """First session just created, not yet saved to disk → False.""" + store._entries = {"agent:main:telegram:dm": MagicMock()} + store._loaded_from_disk = False + assert store.has_any_sessions() is False + + def test_single_platform_after_reset(self, store): + """Single-platform user after session reset — sessions.json was loaded + from disk so we know they have used the bot before → True.""" + store._entries = {"agent:main:telegram:dm": MagicMock()} + store._loaded_from_disk = True + assert store.has_any_sessions() is True + + def test_multi_platform_always_detected(self, store): + """Multiple session keys → True regardless of disk state.""" + store._entries = { + "agent:main:telegram:dm": MagicMock(), + "agent:main:discord:dm": MagicMock(), + } + store._loaded_from_disk = False + assert store.has_any_sessions() is True + + def test_loaded_from_disk_flag_set_on_load(self, tmp_path): + """_ensure_loaded sets _loaded_from_disk when sessions.json exists.""" + sessions_file = tmp_path / "sessions.json" + sessions_file.write_text(json.dumps({ + "agent:main:telegram:dm": { + "session_key": "agent:main:telegram:dm", + "session_id": "20260304_120000_abc", + "created_at": "2026-03-04T12:00:00", + "updated_at": "2026-03-04T12:05:00", + } + })) + config = GatewayConfig() + store = SessionStore.__new__(SessionStore) + store.sessions_dir = tmp_path + store.config = config + store._entries = {} + store._loaded = False + store._loaded_from_disk = False + store._has_active_processes_fn = None + store._on_auto_reset = None + store._db = None + + store._ensure_loaded() + + assert store._loaded_from_disk is True + assert store.has_any_sessions() is True + + def test_loaded_from_disk_flag_false_on_empty_file(self, tmp_path): + """Empty sessions.json → _loaded_from_disk stays False.""" + sessions_file = tmp_path / "sessions.json" + sessions_file.write_text(json.dumps({})) + config = GatewayConfig() + store = SessionStore.__new__(SessionStore) + store.sessions_dir = tmp_path + store.config = config + store._entries = {} + store._loaded = False + store._loaded_from_disk = False + store._has_active_processes_fn = None + store._on_auto_reset = None + store._db = None + + store._ensure_loaded() + + assert store._loaded_from_disk is False + + class TestSessionStoreEntriesAttribute: """Regression: /reset must access _entries, not _sessions."""