Skip to content

Commit b12cac1

Browse files
authored
Merge pull request #9 from Zsailer/server-shutdown
2 parents 794fd5c + ecda48f commit b12cac1

File tree

2 files changed

+83
-3
lines changed

2 files changed

+83
-3
lines changed

jupyter_ai_persona_manager/extension.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,14 @@ async def _stop_extension(self):
189189
stopping.
190190
"""
191191
# Clean up persona managers
192-
for room_id, persona_manager in self.persona_managers_by_room.items():
192+
persona_managers_by_room = self.serverapp.web_app.settings.get('jupyter-ai', {}).get('persona-managers', {})
193+
for room_id, persona_manager in persona_managers_by_room.items():
193194
try:
194195
await persona_manager.shutdown_personas()
195196
except Exception as e:
196197
self.log.error(f"Error cleaning up persona manager for room {room_id}: {e}")
197-
198-
self.persona_managers_by_room.clear()
198+
199+
persona_managers_by_room.clear()
199200

200201
def _link_jupyter_server_extension(self, server_app: ServerApp):
201202
"""Setup custom config needed by this extension."""
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
Tests for the PersonaManagerExtension class.
3+
"""
4+
5+
import pytest
6+
from unittest.mock import Mock, AsyncMock
7+
from jupyter_ai_persona_manager.extension import PersonaManagerExtension
8+
9+
10+
@pytest.fixture
11+
def extension(mock_server_app):
12+
"""Create a PersonaManagerExtension instance for testing."""
13+
ext = PersonaManagerExtension()
14+
ext.serverapp = mock_server_app
15+
ext.log = mock_server_app.log
16+
return ext
17+
18+
19+
@pytest.mark.asyncio
20+
async def test_stop_extension_with_no_persona_managers(extension, mock_server_app):
21+
"""Test that stop_extension works when no persona managers exist."""
22+
# Setup: ensure persona-managers dict exists but is empty
23+
mock_server_app.web_app.settings['jupyter-ai']['persona-managers'] = {}
24+
25+
# Should not raise an exception
26+
await extension.stop_extension()
27+
28+
29+
@pytest.mark.asyncio
30+
async def test_stop_extension_with_persona_managers(extension, mock_server_app):
31+
"""Test that stop_extension properly cleans up persona managers."""
32+
# Setup: create mock persona managers
33+
mock_pm1 = Mock()
34+
mock_pm1.shutdown_personas = AsyncMock()
35+
mock_pm2 = Mock()
36+
mock_pm2.shutdown_personas = AsyncMock()
37+
38+
mock_server_app.web_app.settings['jupyter-ai']['persona-managers'] = {
39+
'room1': mock_pm1,
40+
'room2': mock_pm2
41+
}
42+
43+
# Act
44+
await extension.stop_extension()
45+
46+
# Assert: shutdown_personas was called for each manager
47+
mock_pm1.shutdown_personas.assert_called_once()
48+
mock_pm2.shutdown_personas.assert_called_once()
49+
50+
# Assert: the dictionary was cleared
51+
assert len(mock_server_app.web_app.settings['jupyter-ai']['persona-managers']) == 0
52+
53+
54+
@pytest.mark.asyncio
55+
async def test_stop_extension_with_failing_shutdown(extension, mock_server_app):
56+
"""Test that stop_extension handles exceptions during shutdown gracefully."""
57+
# Setup: create a persona manager that fails on shutdown
58+
mock_pm = Mock()
59+
mock_pm.shutdown_personas = AsyncMock(side_effect=Exception("Shutdown failed"))
60+
61+
mock_server_app.web_app.settings['jupyter-ai']['persona-managers'] = {
62+
'room1': mock_pm
63+
}
64+
65+
# Should not raise an exception, but should log the error
66+
await extension.stop_extension()
67+
68+
# Assert: error was logged
69+
assert extension.log.error.called
70+
71+
72+
@pytest.mark.asyncio
73+
async def test_stop_extension_without_jupyter_ai_settings(extension, mock_server_app):
74+
"""Test that stop_extension handles missing jupyter-ai settings gracefully."""
75+
# Setup: remove jupyter-ai from settings
76+
mock_server_app.web_app.settings.pop('jupyter-ai', None)
77+
78+
# Should not raise an exception
79+
await extension.stop_extension()

0 commit comments

Comments
 (0)