Skip to content

Commit a88244b

Browse files
committed
Improves session management and debugging
Refactors session handling to use dependency injection for agent creation and retrieval, improving testability and maintainability. Adds new GET endpoints for session creation and verification, aligning with the updated frontend session lifecycle. Isolates debug events per session, ensuring correct debug information is displayed for each active session.
1 parent 9abdffc commit a88244b

File tree

4 files changed

+166
-78
lines changed

4 files changed

+166
-78
lines changed

tests/test_debug_api.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from api.app import create_app
66
from core.debug_capture import get_debug_capture_instance, _debug_sessions, clear_all_debug_events
7+
from agent import get_agent_instance
78

89
@pytest.fixture
910
def client():
@@ -130,19 +131,36 @@ def test_clear_debug_events_by_session(self, client, auth_headers):
130131
def test_ask_endpoint_sets_session_id(self, client, auth_headers):
131132
# This test is not as relevant with per-session captures
132133
# since session management is handled differently now
133-
with patch('src.agent.get_agent_instance') as mock_get_agent:
134-
mock_agent = MagicMock()
135-
mock_agent.process_query.return_value = ("Test response", [])
136-
mock_get_agent.return_value = mock_agent
134+
mock_agent = MagicMock()
135+
136+
# Mock the async process_query method to return an awaitable
137+
async def mock_process_query(query):
138+
return ("Test response", [])
139+
140+
mock_agent.process_query = mock_process_query
141+
142+
# Mock the dependency injection by overriding the app dependency
143+
async def mock_get_agent_instance(session_id: str = None):
144+
return mock_agent
137145

146+
client.app.dependency_overrides[get_agent_instance] = mock_get_agent_instance
147+
148+
try:
138149
response = client.post(
139150
"/api/test_session/ask",
140151
json={"query": "test query"},
141152
headers=auth_headers
142153
)
143154

144155
# The main goal is that the request succeeds
145-
assert response.status_code == 200 or response.status_code == 422 # Depends on API structure
156+
assert response.status_code == 200
157+
158+
data = response.json()
159+
assert data["response"] == "Test response"
160+
assert data["used_tools"] == []
161+
finally:
162+
# Clean up the dependency override
163+
client.app.dependency_overrides.clear()
146164

147165
def test_unauthorized_access(self, client):
148166
response = client.get("/api/test_session/debug")

tests/test_frontend_debug_session_isolation.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,64 @@
11
import pytest
22
from unittest.mock import Mock, patch, AsyncMock
33
from core.debug_capture import DebugCapture, get_debug_capture_instance, _debug_sessions
4-
from api.routes import session_router, api_router
4+
from api.app import create_app
5+
from agent import get_agent_instance
56
from fastapi.testclient import TestClient
6-
from fastapi import FastAPI
77

88

99
class TestDebugSessionIsolation:
1010
"""Test that debug events are properly isolated per session in the frontend-backend integration"""
1111

12+
def setup_method(self):
13+
# Clear all debug sessions between tests
14+
_debug_sessions.clear()
15+
1216
@pytest.fixture
1317
def app(self):
14-
app = FastAPI()
15-
app.include_router(session_router)
16-
app.include_router(api_router)
17-
return app
18+
return create_app()
1819

1920
@pytest.fixture
2021
def client(self, app):
2122
return TestClient(app)
2223

2324
@pytest.fixture
24-
def mock_agent_deps(self):
25-
with patch("api.routes.get_agent_instance") as mock_get_agent, \
26-
patch("api.routes.get_debug_capture_instance") as mock_get_debug:
25+
def auth_headers(self):
26+
return {"X-API-Key": "test_12345"}
27+
28+
@pytest.fixture
29+
def mock_agent_deps(self, app):
30+
# Mock the Agent class constructor to prevent MCP initialization
31+
with patch('agent.Agent') as mock_agent_class, \
32+
patch('agent.MCPSessionManager') as mock_mcp_manager:
33+
34+
# Create a mock agent instance
2735
mock_agent = Mock()
36+
mock_agent.session_id = "test_session"
2837
mock_agent.get_tools.return_value = []
29-
mock_agent.process_query = AsyncMock(return_value=("Test response", set()))
38+
39+
async def mock_process_query(query):
40+
return ("Test response", set())
41+
42+
mock_agent.process_query = mock_process_query
3043
mock_agent.initialize_mcp_tools = AsyncMock()
31-
mock_get_agent.return_value = mock_agent
3244

33-
mock_debug = Mock()
34-
mock_get_debug.return_value = mock_debug
45+
# Make the Agent constructor return our mock
46+
mock_agent_class.return_value = mock_agent
47+
48+
# Mock MCPSessionManager
49+
mock_mcp_instance = Mock()
50+
mock_mcp_instance.discovery = AsyncMock()
51+
mock_mcp_instance.tools = []
52+
mock_mcp_manager.return_value = mock_mcp_instance
53+
3554
yield mock_agent
3655

3756
@pytest.fixture
3857
def mock_session_manager(self):
3958
# No longer needed since route doesn't use MCPSessionManager
4059
yield None
4160

42-
def test_debug_events_isolated_per_session(self, client, mock_agent_deps, mock_session_manager):
61+
def test_debug_events_isolated_per_session(self, client, auth_headers, mock_agent_deps, mock_session_manager):
4362
"""Test that debug events are properly isolated between different sessions"""
4463

4564
# Create two sessions
@@ -67,7 +86,7 @@ def test_debug_events_isolated_per_session(self, client, mock_agent_deps, mock_s
6786
debug2.capture_event("test_event_2", "Session 2 event", {"session": 2, "data": "session2_data"})
6887

6988
# Verify session 1 debug endpoint only returns session 1 events
70-
debug_response1 = client.get(f"/api/{session1_id}/debug", headers={"X-API-Key": "test_12345"})
89+
debug_response1 = client.get(f"/api/{session1_id}/debug", headers=auth_headers)
7190
assert debug_response1.status_code == 200
7291
events1 = debug_response1.json()["events"]
7392

tests/test_frontend_debug_session_switching.py

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,64 @@
11
import pytest
22
from unittest.mock import Mock, AsyncMock, patch, call
33
from core.debug_capture import get_debug_capture_instance, _debug_sessions
4-
from api.routes import session_router, api_router
4+
from api.app import create_app
5+
from agent import get_agent_instance
56
from fastapi.testclient import TestClient
6-
from fastapi import FastAPI
77

88

99
class TestFrontendDebugSessionSwitching:
1010
"""Test the frontend integration for debug session switching"""
1111

12+
def setup_method(self):
13+
# Clear all debug sessions between tests
14+
_debug_sessions.clear()
15+
1216
@pytest.fixture
1317
def app(self):
14-
app = FastAPI()
15-
app.include_router(session_router)
16-
app.include_router(api_router)
17-
return app
18+
return create_app()
1819

1920
@pytest.fixture
2021
def client(self, app):
2122
return TestClient(app)
2223

2324
@pytest.fixture
24-
def mock_agent_deps(self):
25-
with patch("api.routes.get_agent_instance") as mock_get_agent, \
26-
patch("api.routes.get_debug_capture_instance") as mock_get_debug:
25+
def auth_headers(self):
26+
return {"X-API-Key": "test_12345"}
27+
28+
@pytest.fixture
29+
def mock_agent_deps(self, app):
30+
# Mock the Agent class constructor to prevent MCP initialization
31+
with patch('agent.Agent') as mock_agent_class, \
32+
patch('agent.MCPSessionManager') as mock_mcp_manager:
33+
34+
# Create a mock agent instance
2735
mock_agent = Mock()
36+
mock_agent.session_id = "test_session"
2837
mock_agent.get_tools.return_value = []
29-
mock_agent.process_query = AsyncMock(return_value=("Test response", set()))
38+
39+
async def mock_process_query(query):
40+
return ("Test response", set())
41+
42+
mock_agent.process_query = mock_process_query
3043
mock_agent.initialize_mcp_tools = AsyncMock()
31-
mock_get_agent.return_value = mock_agent
3244

33-
mock_debug = Mock()
34-
mock_get_debug.return_value = mock_debug
45+
# Make the Agent constructor return our mock
46+
mock_agent_class.return_value = mock_agent
47+
48+
# Mock MCPSessionManager
49+
mock_mcp_instance = Mock()
50+
mock_mcp_instance.discovery = AsyncMock()
51+
mock_mcp_instance.tools = []
52+
mock_mcp_manager.return_value = mock_mcp_instance
53+
3554
yield mock_agent
3655

3756
@pytest.fixture
3857
def mock_session_manager(self):
3958
# No longer needed since route doesn't use MCPSessionManager
4059
yield None
4160

42-
def test_debug_panel_shows_session_specific_events(self, client, mock_agent_deps, mock_session_manager):
61+
def test_debug_panel_shows_session_specific_events(self, client, auth_headers, mock_agent_deps):
4362
"""Test that the debug panel shows only events for the current session"""
4463

4564
# Create two sessions with different debug events
@@ -65,7 +84,7 @@ def test_debug_panel_shows_session_specific_events(self, client, mock_agent_deps
6584
debug2.capture_event("tool_result", "News results retrieved", {"results": ["News item 1", "News item 2"]})
6685

6786
# Test: Debug endpoint for session 1 should only show session 1 events
68-
debug_response1 = client.get(f"/api/{session1_id}/debug", headers={"X-API-Key": "test_12345"})
87+
debug_response1 = client.get(f"/api/{session1_id}/debug", headers=auth_headers)
6988
events1 = debug_response1.json()["events"]
7089

7190
assert len(events1) == 2
@@ -74,7 +93,7 @@ def test_debug_panel_shows_session_specific_events(self, client, mock_agent_deps
7493
assert events1[1]["event_type"] == "llm_response"
7594

7695
# Test: Debug endpoint for session 2 should only show session 2 events
77-
debug_response2 = client.get(f"/api/{session2_id}/debug", headers={"X-API-Key": "test_12345"})
96+
debug_response2 = client.get(f"/api/{session2_id}/debug", headers=auth_headers)
7897
events2 = debug_response2.json()["events"]
7998

8099
assert len(events2) == 3
@@ -83,7 +102,7 @@ def test_debug_panel_shows_session_specific_events(self, client, mock_agent_deps
83102
assert events2[1]["event_type"] == "tool_call"
84103
assert events2[2]["event_type"] == "tool_result"
85104

86-
def test_debug_state_persists_across_session_switches(self, client, mock_agent_deps, mock_session_manager):
105+
def test_debug_state_persists_across_session_switches(self, client, auth_headers, mock_agent_deps):
87106
"""Test that debug enabled/disabled state is maintained per session"""
88107

89108
# Create two sessions
@@ -100,8 +119,8 @@ def test_debug_state_persists_across_session_switches(self, client, mock_agent_d
100119
debug1.enable()
101120
debug2.enable()
102121

103-
debug_status1 = client.get(f"/api/{session1_id}/debug", headers={"X-API-Key": "test_12345"})
104-
debug_status2 = client.get(f"/api/{session2_id}/debug", headers={"X-API-Key": "test_12345"})
122+
debug_status1 = client.get(f"/api/{session1_id}/debug", headers=auth_headers)
123+
debug_status2 = client.get(f"/api/{session2_id}/debug", headers=auth_headers)
105124

106125
assert debug_status1.json()["enabled"] is True
107126
assert debug_status2.json()["enabled"] is True
@@ -110,13 +129,13 @@ def test_debug_state_persists_across_session_switches(self, client, mock_agent_d
110129
toggle_response = client.post(
111130
f"/api/{session1_id}/debug/toggle",
112131
json={"enabled": False},
113-
headers={"X-API-Key": "test_12345"}
132+
headers=auth_headers
114133
)
115134
assert toggle_response.json()["enabled"] is False
116135

117136
# Verify session 1 is disabled, session 2 still enabled
118-
debug_status1_after = client.get(f"/api/{session1_id}/debug", headers={"X-API-Key": "test_12345"})
119-
debug_status2_after = client.get(f"/api/{session2_id}/debug", headers={"X-API-Key": "test_12345"})
137+
debug_status1_after = client.get(f"/api/{session1_id}/debug", headers=auth_headers)
138+
debug_status2_after = client.get(f"/api/{session2_id}/debug", headers=auth_headers)
120139

121140
assert debug_status1_after.json()["enabled"] is False
122141
assert debug_status2_after.json()["enabled"] is True
@@ -129,27 +148,27 @@ def test_debug_state_persists_across_session_switches(self, client, mock_agent_d
129148
debug2.capture_event("test_event", "Should be captured", {"test": "data2"})
130149

131150
# Verify only session 2 captures events (since session 1 is disabled, session 2 still enabled)
132-
events1 = client.get(f"/api/{session1_id}/debug", headers={"X-API-Key": "test_12345"}).json()["events"]
133-
events2 = client.get(f"/api/{session2_id}/debug", headers={"X-API-Key": "test_12345"}).json()["events"]
151+
events1 = client.get(f"/api/{session1_id}/debug", headers=auth_headers).json()["events"]
152+
events2 = client.get(f"/api/{session2_id}/debug", headers=auth_headers).json()["events"]
134153

135154
assert len(events1) == 0 # Session 1 debug is disabled
136155
assert len(events2) == 1 # Session 2 debug is enabled
137156

138-
def test_empty_session_shows_empty_debug_panel(self, client, mock_agent_deps, mock_session_manager):
157+
def test_empty_session_shows_empty_debug_panel(self, client, auth_headers, mock_agent_deps):
139158
"""Test that a new session with no events shows an empty debug panel"""
140159

141160
# Create a new session
142161
response = client.get("/api/session/new")
143162
session_id = response.json()["session_id"]
144163

145164
# Debug endpoint should return enabled=False but no events by default
146-
debug_response = client.get(f"/api/{session_id}/debug", headers={"X-API-Key": "test_12345"})
165+
debug_response = client.get(f"/api/{session_id}/debug", headers=auth_headers)
147166
debug_data = debug_response.json()
148167

149168
assert debug_data["enabled"] is False
150169
assert len(debug_data["events"]) == 0
151170

152-
def test_debug_events_ordering_by_timestamp(self, client, mock_agent_deps, mock_session_manager):
171+
def test_debug_events_ordering_by_timestamp(self, client, auth_headers, mock_agent_deps):
153172
"""Test that debug events are returned in chronological order"""
154173

155174
# Create a session
@@ -170,7 +189,7 @@ def test_debug_events_ordering_by_timestamp(self, client, mock_agent_deps, mock_
170189
debug_capture.capture_event("event_3", "Third event", {"order": 3})
171190

172191
# Get events from API
173-
debug_response = client.get(f"/api/{session_id}/debug", headers={"X-API-Key": "test_12345"})
192+
debug_response = client.get(f"/api/{session_id}/debug", headers=auth_headers)
174193
events = debug_response.json()["events"]
175194

176195
assert len(events) == 3

0 commit comments

Comments
 (0)