Kokkoja: first public assembly
Assembled from the development repository by tools/make_release.sh. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
95c80d04c9
39 files changed
+9905
No files matched your search
@@ -0,0 +1,129 @@
|
||||
"""Tests of the relay. Run from the project directory:
|
||||
|
||||
.venv/bin/python -m unittest discover tests
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from websockets.asyncio.client import connect
|
||||
from websockets.asyncio.server import serve
|
||||
from websockets.exceptions import ConnectionClosed, InvalidStatus
|
||||
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / "relay"))
|
||||
import kokkoja_relay as relay # noqa: E402
|
||||
|
||||
ROOM_A = "A" * 22
|
||||
ROOM_B = "B" * 22
|
||||
|
||||
|
||||
def to_peer(destination, body):
|
||||
return destination.to_bytes(4, "big") + body
|
||||
|
||||
|
||||
class RelayTest(unittest.IsolatedAsyncioTestCase):
|
||||
async def asyncSetUp(self):
|
||||
relay.rooms.clear()
|
||||
self.server = await serve(relay.handle, "127.0.0.1", 0,
|
||||
process_request=relay.check_path,
|
||||
max_size=relay.MAX_MESSAGE_BYTES)
|
||||
port = self.server.sockets[0].getsockname()[1]
|
||||
self.address = f"ws://127.0.0.1:{port}"
|
||||
|
||||
async def asyncTearDown(self):
|
||||
self.server.close()
|
||||
await self.server.wait_closed()
|
||||
|
||||
async def join(self, room):
|
||||
connection = await connect(f"{self.address}/room/{room}")
|
||||
self.addAsyncCleanup(connection.close)
|
||||
welcome = json.loads(await connection.recv())
|
||||
self.assertEqual(welcome["type"], "welcome")
|
||||
return connection, welcome
|
||||
|
||||
async def test_welcome_join_leave(self):
|
||||
first, welcome = await self.join(ROOM_A)
|
||||
self.assertEqual(welcome, {"type": "welcome", "peer": 1, "peers": []})
|
||||
second, welcome = await self.join(ROOM_A)
|
||||
self.assertEqual(welcome, {"type": "welcome", "peer": 2, "peers": [1]})
|
||||
self.assertEqual(json.loads(await first.recv()), {"type": "join", "peer": 2})
|
||||
await second.close()
|
||||
self.assertEqual(json.loads(await first.recv()), {"type": "leave", "peer": 2})
|
||||
|
||||
async def test_broadcast_reaches_the_others_only(self):
|
||||
first, _ = await self.join(ROOM_A)
|
||||
second, _ = await self.join(ROOM_A)
|
||||
third, _ = await self.join(ROOM_A)
|
||||
await first.recv() # join 2
|
||||
await first.recv() # join 3
|
||||
await second.recv() # join 3
|
||||
await second.send(to_peer(0, b"hello"))
|
||||
self.assertEqual(await first.recv(), to_peer(2, b"hello"))
|
||||
self.assertEqual(await third.recv(), to_peer(2, b"hello"))
|
||||
# The sender gets nothing back: the next thing it receives is this.
|
||||
await first.send(to_peer(0, b"next"))
|
||||
self.assertEqual(await second.recv(), to_peer(1, b"next"))
|
||||
|
||||
async def test_directed_message_reaches_one_peer(self):
|
||||
first, _ = await self.join(ROOM_A)
|
||||
second, _ = await self.join(ROOM_A)
|
||||
third, _ = await self.join(ROOM_A)
|
||||
await first.recv()
|
||||
await first.recv()
|
||||
await second.recv()
|
||||
await first.send(to_peer(3, b"for three"))
|
||||
await first.send(to_peer(0, b"for all"))
|
||||
self.assertEqual(await third.recv(), to_peer(1, b"for three"))
|
||||
self.assertEqual(await third.recv(), to_peer(1, b"for all"))
|
||||
self.assertEqual(await second.recv(), to_peer(1, b"for all"))
|
||||
|
||||
async def test_rooms_are_separate(self):
|
||||
in_a, _ = await self.join(ROOM_A)
|
||||
in_b, welcome = await self.join(ROOM_B)
|
||||
self.assertEqual(welcome["peers"], [])
|
||||
other_a, _ = await self.join(ROOM_A)
|
||||
await in_a.recv() # join
|
||||
await in_b.send(to_peer(0, b"b only"))
|
||||
await other_a.send(to_peer(0, b"a only"))
|
||||
self.assertEqual(await in_a.recv(), to_peer(2, b"a only"))
|
||||
|
||||
async def test_room_is_forgotten_when_empty(self):
|
||||
first, _ = await self.join(ROOM_A)
|
||||
await first.close()
|
||||
await first.wait_closed()
|
||||
for _ in range(50):
|
||||
if not relay.rooms:
|
||||
break
|
||||
await asyncio.sleep(0.01)
|
||||
self.assertEqual(relay.rooms, {})
|
||||
|
||||
async def test_bad_path_is_refused(self):
|
||||
for path in ("/", "/room/short", f"/room/{ROOM_A}/extra", "/room/" + "!" * 22):
|
||||
with self.assertRaises(InvalidStatus):
|
||||
await connect(self.address + path)
|
||||
|
||||
async def test_text_message_closes_the_connection(self):
|
||||
first, _ = await self.join(ROOM_A)
|
||||
await first.send("text is not allowed")
|
||||
with self.assertRaises(ConnectionClosed) as caught:
|
||||
await first.recv()
|
||||
self.assertEqual(caught.exception.rcvd.code, relay.CLOSE_BAD_MESSAGE)
|
||||
|
||||
async def test_room_full(self):
|
||||
relay.MAX_PEERS_PER_ROOM, saved = 2, relay.MAX_PEERS_PER_ROOM
|
||||
try:
|
||||
await self.join(ROOM_A)
|
||||
await self.join(ROOM_A)
|
||||
extra = await connect(f"{self.address}/room/{ROOM_A}")
|
||||
with self.assertRaises(ConnectionClosed) as caught:
|
||||
await extra.recv()
|
||||
self.assertEqual(caught.exception.rcvd.code, relay.CLOSE_ROOM_FULL)
|
||||
finally:
|
||||
relay.MAX_PEERS_PER_ROOM = saved
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user