Skip to content

Commit cf4b8e6

Browse files
committed
Test proxy client launch and init
1 parent cbdd159 commit cf4b8e6

4 files changed

Lines changed: 102 additions & 10 deletions

File tree

src/ephys_link/back_end/platform_handler.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"""
99

1010
from typing import final
11-
from uuid import uuid4
1211

1312
from vbl_aquarium.models.ephys_link import (
1413
AngularResponse,
@@ -54,9 +53,6 @@ def __init__(self, binding: BaseBinding, console: Console) -> None:
5453
# Record which IDs are inside the brain.
5554
self._inside_brain: set[str] = set()
5655

57-
# Generate a Pinpoint ID for proxy usage.
58-
self._pinpoint_id = str(uuid4())[:8]
59-
6056
# Platform metadata.
6157

6258
def get_display_name(self) -> str:

src/ephys_link/back_end/server.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
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, SERVER_NOT_INITIALIZED_ERROR
36+
from ephys_link.utils.constants import PORT, PROXY_CLIENT_NOT_INITIALIZED_ERROR, SERVER_NOT_INITIALIZED_ERROR
3737

3838
# Server message generic types.
3939
INPUT_TYPE = TypeVar("INPUT_TYPE", bound=VBLBaseModel)
@@ -100,9 +100,8 @@ def launch(self) -> None:
100100
async def connect_proxy() -> None:
101101
# Exit if _sio is not a proxy client.
102102
if not isinstance(self._sio, AsyncClient):
103-
error = "Proxy client not initialized."
104-
self._console.critical_print(error)
105-
raise TypeError(error)
103+
self._console.critical_print(PROXY_CLIENT_NOT_INITIALIZED_ERROR)
104+
raise TypeError(PROXY_CLIENT_NOT_INITIALIZED_ERROR)
106105

107106
# noinspection HttpUrlsUsage
108107
await self._sio.connect(f"http://{self._options.proxy_address}:{PORT}") # pyright: ignore [reportUnknownMemberType]

src/ephys_link/utils/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ def did_not_reach_target_depth_error(request: SetDepthRequest, final_unified_dep
6060
EMERGENCY_STOP_MESSAGE = "Emergency Stopping All Manipulators..."
6161

6262
SERVER_NOT_INITIALIZED_ERROR = "Server not initialized."
63+
PROXY_CLIENT_NOT_INITIALIZED_ERROR = "Proxy client not initialized."

tests/back_end/test_server.py

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
import asyncio
2+
from collections.abc import Awaitable
3+
14
import pytest
25
from pytest_mock import MockerFixture
3-
from socketio import AsyncServer # pyright: ignore[reportMissingTypeStubs]
6+
from socketio import AsyncClient, AsyncServer # pyright: ignore[reportMissingTypeStubs]
47
from vbl_aquarium.models.ephys_link import EphysLinkOptions, GetManipulatorsResponse
58

69
import ephys_link.back_end.server
710
from ephys_link.back_end.platform_handler import PlatformHandler
811
from ephys_link.back_end.server import Server
912
from ephys_link.front_end.console import Console
10-
from ephys_link.utils.constants import SERVER_NOT_INITIALIZED_ERROR
13+
from ephys_link.utils.constants import PROXY_CLIENT_NOT_INITIALIZED_ERROR, SERVER_NOT_INITIALIZED_ERROR
1114
from tests.conftest import DUMMY_STRING, DUMMY_STRING_LIST
1215

1316

@@ -52,6 +55,7 @@ def test_launch_server(
5255
) -> None:
5356
"""Server should print info then start."""
5457
# Add mocks and spies.
58+
# noinspection DuplicatedCode
5559
spied_info_print = mocker.spy(console, "info_print")
5660

5761
patched_get_display_name = mocker.patch.object(platform_handler, "get_display_name", return_value=DUMMY_STRING)
@@ -80,3 +84,95 @@ def test_launch_server(
8084
spied_info_print.assert_any_call("PLATFORM", platform_handler.get_display_name())
8185
spied_info_print.assert_any_call("MANIPULATORS", str(DUMMY_STRING_LIST))
8286
mocked_run_app.assert_called_once()
87+
88+
def test_launch_proxy_client(
89+
self, proxy_client: Server, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture
90+
) -> None:
91+
"""Proxy client should print info then start."""
92+
# Add mocks and spies.
93+
# noinspection DuplicatedCode
94+
spied_info_print = mocker.spy(console, "info_print")
95+
96+
patched_get_display_name = mocker.patch.object(platform_handler, "get_display_name", return_value=DUMMY_STRING)
97+
98+
# Mock out get manipulators.
99+
patched_get_manipulators = mocker.patch.object(platform_handler, "get_manipulators", new=mocker.Mock())
100+
asyncio_loop = mocker.Mock()
101+
patched_run_until_complete = mocker.patch.object(
102+
asyncio_loop, "run_until_complete", return_value=GetManipulatorsResponse(manipulators=DUMMY_STRING_LIST)
103+
)
104+
patched_get_event_loop = mocker.patch.object(
105+
ephys_link.back_end.server, "get_event_loop", return_value=asyncio_loop
106+
)
107+
108+
# Mock out run.
109+
def run_coroutine(coroutine: Awaitable[None]) -> None:
110+
"""Run the coroutine."""
111+
asyncio.new_event_loop().run_until_complete(coroutine)
112+
113+
_ = mocker.patch.object(ephys_link.back_end.server, "run", new=run_coroutine)
114+
patched_connect = mocker.patch.object(AsyncClient, "connect", new_callable=mocker.AsyncMock)
115+
patched_wait = mocker.patch.object(AsyncClient, "wait", new_callable=mocker.AsyncMock)
116+
117+
# Act.
118+
proxy_client.launch()
119+
120+
# Assert.
121+
patched_get_display_name.assert_called_once()
122+
patched_get_manipulators.assert_called_once()
123+
patched_run_until_complete.assert_called_once()
124+
patched_get_event_loop.assert_called_once()
125+
spied_info_print.assert_any_call("PLATFORM", platform_handler.get_display_name())
126+
spied_info_print.assert_any_call("MANIPULATORS", str(DUMMY_STRING_LIST))
127+
spied_info_print.assert_any_call("PINPOINT ID", mocker.ANY) # pyright: ignore[reportAny]
128+
patched_connect.assert_awaited_once() # pyright: ignore[reportUnusedCallResult]
129+
patched_wait.assert_awaited_once() # pyright: ignore[reportUnusedCallResult]
130+
131+
def test_launch_proxy_client_failed_init(
132+
self, platform_handler: PlatformHandler, console: Console, mocker: MockerFixture
133+
) -> None:
134+
"""Proxy client should print info then start."""
135+
# Add mocks and spies.
136+
# noinspection DuplicatedCode
137+
spied_info_print = mocker.spy(console, "info_print")
138+
139+
patched_get_display_name = mocker.patch.object(platform_handler, "get_display_name", return_value=DUMMY_STRING)
140+
141+
# Mock out get manipulators.
142+
patched_get_manipulators = mocker.patch.object(platform_handler, "get_manipulators", new=mocker.Mock())
143+
asyncio_loop = mocker.Mock()
144+
patched_run_until_complete = mocker.patch.object(
145+
asyncio_loop, "run_until_complete", return_value=GetManipulatorsResponse(manipulators=DUMMY_STRING_LIST)
146+
)
147+
patched_get_event_loop = mocker.patch.object(
148+
ephys_link.back_end.server, "get_event_loop", return_value=asyncio_loop
149+
)
150+
151+
# Mock out run.
152+
def run_coroutine(coroutine: Awaitable[None]) -> None:
153+
"""Run the coroutine."""
154+
asyncio.new_event_loop().run_until_complete(coroutine)
155+
156+
_ = mocker.patch.object(ephys_link.back_end.server, "run", new=run_coroutine)
157+
patched_connect = mocker.patch.object(AsyncClient, "connect", new_callable=mocker.AsyncMock)
158+
patched_wait = mocker.patch.object(AsyncClient, "wait", new_callable=mocker.AsyncMock)
159+
160+
# Mock out the AsyncServer init.
161+
patched_async_server = mocker.patch.object(AsyncClient, "__new__")
162+
163+
# Act
164+
with pytest.raises(TypeError) as init_error:
165+
Server(EphysLinkOptions(use_proxy=True), platform_handler, console).launch()
166+
167+
# Assert.
168+
patched_async_server.assert_called_once()
169+
patched_get_display_name.assert_called_once()
170+
patched_get_manipulators.assert_called_once()
171+
patched_run_until_complete.assert_called_once()
172+
patched_get_event_loop.assert_called_once()
173+
spied_info_print.assert_any_call("PLATFORM", platform_handler.get_display_name())
174+
spied_info_print.assert_any_call("MANIPULATORS", str(DUMMY_STRING_LIST))
175+
spied_info_print.assert_any_call("PINPOINT ID", mocker.ANY) # pyright: ignore[reportAny]
176+
assert init_error.value.args[0] == PROXY_CLIENT_NOT_INITIALIZED_ERROR
177+
patched_connect.assert_not_awaited() # pyright: ignore[reportUnusedCallResult]
178+
patched_wait.assert_not_awaited() # pyright: ignore[reportUnusedCallResult]

0 commit comments

Comments
 (0)