Skip to content

Commit af6d5c0

Browse files
committed
Backport PR jupyter-server#288 on branch 2.x (Fix tests)
1 parent 8f8a1ca commit af6d5c0

File tree

5 files changed

+79
-16
lines changed

5 files changed

+79
-16
lines changed

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ test = [
3030
"pre-commit",
3131
"pytest",
3232
"pytest-asyncio",
33-
"websockets >=10.0",
33+
"httpx-ws >=0.5.2",
34+
"hypercorn >=0.16.0",
3435
"pycrdt-websocket >=0.15.0,<0.16.0",
3536
]
3637
docs = [

tests/conftest.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
import json
55
import subprocess
6+
from functools import partial
67
from pathlib import Path
78

89
import pytest
9-
from pycrdt_websocket import WebsocketServer
10-
from websockets import serve # type: ignore
10+
from anyio import Event, create_task_group
11+
from hypercorn import Config
12+
from hypercorn.asyncio import serve
13+
from pycrdt_websocket import ASGIServer, WebsocketServer
14+
from utils import ensure_server_running
1115

1216
# workaround until these PRs are merged:
1317
# - https://github.com/yjs/y-websocket/pull/104
@@ -27,22 +31,33 @@ def update_json_file(path: Path, d: dict):
2731

2832

2933
@pytest.fixture
30-
async def yws_server(request):
34+
async def yws_server(request, unused_tcp_port):
3135
try:
32-
kwargs = request.param
33-
except Exception:
34-
kwargs = {}
35-
websocket_server = WebsocketServer(**kwargs)
36-
try:
37-
async with websocket_server, serve(websocket_server.serve, "localhost", 1234):
38-
yield websocket_server
36+
async with create_task_group() as tg:
37+
try:
38+
kwargs = request.param
39+
except Exception:
40+
kwargs = {}
41+
websocket_server = WebsocketServer(**kwargs)
42+
app = ASGIServer(websocket_server)
43+
config = Config()
44+
config.bind = [f"localhost:{unused_tcp_port}"]
45+
shutdown_event = Event()
46+
async with websocket_server as websocket_server:
47+
tg.start_soon(
48+
partial(serve, app, config, shutdown_trigger=shutdown_event.wait, mode="asgi")
49+
)
50+
await ensure_server_running("localhost", unused_tcp_port)
51+
pytest.port = unused_tcp_port
52+
yield unused_tcp_port, websocket_server
53+
shutdown_event.set()
3954
except Exception:
4055
pass
4156

4257

4358
@pytest.fixture
4459
def yjs_client(request):
4560
client_id = request.param
46-
p = subprocess.Popen(f"yarn node {here / 'yjs_client_'}{client_id}.js", shell=True)
61+
p = subprocess.Popen(["node", f"{here / 'yjs_client_'}{client_id}.js", str(pytest.port)])
4762
yield p
4863
p.kill()

tests/test_pycrdt_yjs.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
import pytest
88
from anyio import Event, create_task_group, move_on_after
9+
from httpx_ws import aconnect_ws
910
from pycrdt import Doc, Map
1011
from pycrdt_websocket import WebsocketProvider
11-
from websockets import connect # type: ignore
12+
from utils import Websocket
1213

1314
from jupyter_ydoc import YNotebook
1415
from jupyter_ydoc.utils import cast_all
@@ -61,10 +62,12 @@ def source(self):
6162
@pytest.mark.asyncio
6263
@pytest.mark.parametrize("yjs_client", "0", indirect=True)
6364
async def test_ypy_yjs_0(yws_server, yjs_client):
65+
port, _ = yws_server
6466
ydoc = Doc()
6567
ynotebook = YNotebook(ydoc)
66-
async with connect("ws://localhost:1234/my-roomname") as websocket, WebsocketProvider(
67-
ydoc, websocket
68+
room_name = "my-roomname"
69+
async with aconnect_ws(f"http://localhost:{port}/{room_name}") as websocket, WebsocketProvider(
70+
ydoc, Websocket(websocket, room_name)
6871
):
6972
nb = stringify_source(json.loads((files_dir / "nb0.ipynb").read_text()))
7073
ynotebook.source = nb

tests/utils.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
from anyio import Lock, connect_tcp
5+
6+
7+
class Websocket:
8+
def __init__(self, websocket, path: str):
9+
self._websocket = websocket
10+
self._path = path
11+
self._send_lock = Lock()
12+
13+
@property
14+
def path(self) -> str:
15+
return self._path
16+
17+
def __aiter__(self):
18+
return self
19+
20+
async def __anext__(self) -> bytes:
21+
try:
22+
message = await self.recv()
23+
except Exception:
24+
raise StopAsyncIteration()
25+
return message
26+
27+
async def send(self, message: bytes):
28+
async with self._send_lock:
29+
await self._websocket.send_bytes(message)
30+
31+
async def recv(self) -> bytes:
32+
b = await self._websocket.receive_bytes()
33+
return bytes(b)
34+
35+
36+
async def ensure_server_running(host: str, port: int) -> None:
37+
while True:
38+
try:
39+
await connect_tcp(host, port)
40+
except OSError:
41+
pass
42+
else:
43+
break

tests/yjs_client_0.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import { YNotebook } from '@jupyter/ydoc'
77
import { WebsocketProvider } from 'y-websocket'
88

9+
const port = process.argv[2]
910
const notebook = new YNotebook()
1011
const ytest = notebook.ydoc.getMap('_test')
1112
import ws from 'ws'
1213

1314
const wsProvider = new WebsocketProvider(
14-
'ws://localhost:1234', 'my-roomname',
15+
`ws://127.0.0.1:${port}`, 'my-roomname',
1516
notebook.ydoc,
1617
{ WebSocketPolyfill: ws }
1718
)

0 commit comments

Comments
 (0)