Skip to content

Commit d4c5a90

Browse files
committed
Connect and disconnect
1 parent cf4b8e6 commit d4c5a90

3 files changed

Lines changed: 106 additions & 5 deletions

File tree

src/ephys_link/back_end/server.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@
3333
from ephys_link.__about__ import __version__
3434
from ephys_link.back_end.platform_handler import PlatformHandler
3535
from ephys_link.front_end.console import Console
36-
from ephys_link.utils.constants import PORT, PROXY_CLIENT_NOT_INITIALIZED_ERROR, SERVER_NOT_INITIALIZED_ERROR
36+
from ephys_link.utils.constants import (
37+
PORT,
38+
PROXY_CLIENT_NOT_INITIALIZED_ERROR,
39+
SERVER_NOT_INITIALIZED_ERROR,
40+
cannot_connect_as_client_is_already_connected_error,
41+
client_disconnected_without_being_connected_error,
42+
)
3743

3844
# Server message generic types.
3945
INPUT_TYPE = TypeVar("INPUT_TYPE", bound=VBLBaseModel)
@@ -197,7 +203,7 @@ async def connect(self, sid: str, _: str) -> bool:
197203
return True
198204

199205
self._console.error_print(
200-
"CONNECTION REFUSED", f"Cannot connect {sid} as {self._client_sid} is already connected."
206+
"CONNECTION REFUSED", cannot_connect_as_client_is_already_connected_error(sid, self._client_sid)
201207
)
202208
return False
203209

@@ -207,13 +213,14 @@ async def disconnect(self, sid: str) -> None:
207213
Args:
208214
sid: Socket session ID.
209215
"""
210-
self._console.info_print("DISCONNECTED", sid)
216+
self._console.info_print("DISCONNECTION REQUEST", sid)
211217

212218
# Reset client SID if it matches.
213219
if self._client_sid == sid:
214220
self._client_sid = ""
221+
self._console.info_print("DISCONNECTED", sid)
215222
else:
216-
self._console.error_print("DISCONNECTION", f"Client {sid} disconnected without being connected.")
223+
self._console.error_print("DISCONNECTION", client_disconnected_without_being_connected_error(sid))
217224

218225
async def platform_event_handler(self, event: str, *args: tuple[Any]) -> str: # pyright: ignore [reportExplicitAny]
219226
"""Handle events from the server.

src/ephys_link/utils/constants.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,24 @@ def did_not_reach_target_depth_error(request: SetDepthRequest, final_unified_dep
6161

6262
SERVER_NOT_INITIALIZED_ERROR = "Server not initialized."
6363
PROXY_CLIENT_NOT_INITIALIZED_ERROR = "Proxy client not initialized."
64+
65+
66+
def cannot_connect_as_client_is_already_connected_error(new_client_sid: str, current_client_sid: str) -> str:
67+
"""Generate an error message for when the client is already connected.
68+
Args:
69+
new_client_sid: The SID of the new client.
70+
current_client_sid: The SID of the current client.
71+
Returns:
72+
str: The error message.
73+
"""
74+
return f"Cannot connect {new_client_sid} as {current_client_sid} is already connected."
75+
76+
77+
def client_disconnected_without_being_connected_error(client_sid: str) -> str:
78+
"""Generate an error message for when the client is disconnected without being connected.
79+
Args:
80+
client_sid: The SID of the client.
81+
Returns:
82+
str: The error message.
83+
"""
84+
return f"Client {client_sid} disconnected without being connected."

tests/back_end/test_server.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010
from ephys_link.back_end.platform_handler import PlatformHandler
1111
from ephys_link.back_end.server import Server
1212
from ephys_link.front_end.console import Console
13-
from ephys_link.utils.constants import PROXY_CLIENT_NOT_INITIALIZED_ERROR, SERVER_NOT_INITIALIZED_ERROR
13+
from ephys_link.utils.constants import (
14+
PROXY_CLIENT_NOT_INITIALIZED_ERROR,
15+
SERVER_NOT_INITIALIZED_ERROR,
16+
cannot_connect_as_client_is_already_connected_error,
17+
client_disconnected_without_being_connected_error,
18+
)
1419
from tests.conftest import DUMMY_STRING, DUMMY_STRING_LIST
1520

1621

@@ -176,3 +181,71 @@ def run_coroutine(coroutine: Awaitable[None]) -> None:
176181
assert init_error.value.args[0] == PROXY_CLIENT_NOT_INITIALIZED_ERROR
177182
patched_connect.assert_not_awaited() # pyright: ignore[reportUnusedCallResult]
178183
patched_wait.assert_not_awaited() # pyright: ignore[reportUnusedCallResult]
184+
185+
@pytest.mark.asyncio
186+
async def test_connect_success(self, server: Server, console: Console, mocker: MockerFixture) -> None:
187+
"""Server should allow connection if there is no existing connection."""
188+
# Spy console.
189+
spied_info_print = mocker.spy(console, "info_print")
190+
191+
# Act.
192+
result = await server.connect(DUMMY_STRING, DUMMY_STRING)
193+
194+
# Assert.
195+
spied_info_print.assert_any_call("CONNECTION REQUEST", DUMMY_STRING)
196+
assert server._client_sid == DUMMY_STRING # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
197+
spied_info_print.assert_any_call("CONNECTION GRANTED", DUMMY_STRING)
198+
assert result
199+
200+
@pytest.mark.asyncio
201+
async def test_connect_failure(self, server: Server, console: Console, mocker: MockerFixture) -> None:
202+
"""Server should not allow connection if there is an existing connection."""
203+
# Spy console.
204+
spied_info_print = mocker.spy(console, "info_print")
205+
spied_error_print = mocker.spy(console, "error_print")
206+
207+
# Mock client sid.
208+
_ = mocker.patch.object(server, "_client_sid", new=DUMMY_STRING)
209+
210+
# Act.
211+
result = await server.connect(DUMMY_STRING, DUMMY_STRING)
212+
213+
# Assert.
214+
spied_info_print.assert_any_call("CONNECTION REQUEST", DUMMY_STRING)
215+
spied_error_print.assert_called_once_with(
216+
"CONNECTION REFUSED", cannot_connect_as_client_is_already_connected_error(DUMMY_STRING, DUMMY_STRING)
217+
)
218+
assert not result
219+
220+
@pytest.mark.asyncio
221+
async def test_disconnect_success(self, server: Server, console: Console, mocker: MockerFixture) -> None:
222+
"""Server should allow disconnection if the SID matches the existing connection."""
223+
# Spy console.
224+
spied_info_print = mocker.spy(console, "info_print")
225+
226+
# Mock client sid.
227+
_ = mocker.patch.object(server, "_client_sid", new=DUMMY_STRING)
228+
229+
# Act.
230+
await server.disconnect(DUMMY_STRING)
231+
232+
# Assert.
233+
spied_info_print.assert_any_call("DISCONNECTION REQUEST", DUMMY_STRING)
234+
assert server._client_sid == "" # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
235+
spied_info_print.assert_any_call("DISCONNECTED", DUMMY_STRING)
236+
237+
@pytest.mark.asyncio
238+
async def test_disconnect_failure(self, server: Server, console: Console, mocker: MockerFixture) -> None:
239+
"""Server should not allow disconnection if there is no existing connection."""
240+
# Spy console.
241+
spied_info_print = mocker.spy(console, "info_print")
242+
spied_error_print = mocker.spy(console, "error_print")
243+
244+
# Act.
245+
await server.disconnect(DUMMY_STRING)
246+
247+
# Assert.
248+
spied_info_print.assert_any_call("DISCONNECTION REQUEST", DUMMY_STRING)
249+
spied_error_print.assert_called_once_with(
250+
"DISCONNECTION", client_disconnected_without_being_connected_error(DUMMY_STRING)
251+
)

0 commit comments

Comments
 (0)