[+!] all but small

some small modifications, tests added
This commit is contained in:
2026-10-07 22:42:46 +03:00
parent fac12b2ea8
commit c16ed7915a
21 changed files with 1190 additions and 100 deletions

View File

@@ -1,4 +1,4 @@
"""Integration checks using the same client as src/.test/start_client.bat."""
"""Integration checks using the same client as examples/client.py."""
import asyncio
from copy import deepcopy
@@ -12,9 +12,13 @@ class RunningServer:
self.clients = []
self.handlers = []
self.watchers = []
self.records = []
self.errors = []
self.expected_errors = []
self.peer_errors = {}
async def __aenter__(self):
self.initial_tasks = asyncio.all_tasks()
# Lazy imports keep client-only tests runnable if the server has an error.
from router import Router
from session import Sessions
@@ -22,11 +26,14 @@ class RunningServer:
errors = self.errors
watchers = self.watchers
records = self.records
peer_errors = self.peer_errors
class ObservedSessions(Sessions):
async def new(self, connection):
uid = await super().new(connection)
watchers.append(self[uid].watcher_task)
records.append(self[uid])
return uid
async def feed(self, uid, packet):
@@ -36,6 +43,7 @@ class RunningServer:
# Preserve the original error before the handler enters
# its delayed cleanup and closes the client's transport.
errors.append(error)
peer_errors.setdefault(asyncio.current_task(), []).append(error)
raise
self.router = Router()
@@ -55,6 +63,17 @@ class RunningServer:
if not task.cancelled() and task.exception() is not None:
self.errors.append(task.exception())
def accept_peer_rejection(self, task, error):
# Accept only the checked exception from this particular client handler.
assert task in self.handlers and task.done() and not task.cancelled()
assert task.exception() is error or any(error is item for item in self.peer_errors.get(task, []))
self.expected_errors.append(error)
def assert_no_unexpected_errors(self):
for error in self.errors:
if not any(error is expected for expected in self.expected_errors):
raise error
async def __aexit__(self, exc_type, exc, traceback):
self.server.close()
await self.server.wait_closed()
@@ -63,17 +82,23 @@ class RunningServer:
)
# Stop pending recovery-window waits after assertions, without modifying
# the production timer or waiting 60 seconds for each test connection.
tasks = self.handlers + self.watchers
tasks = set(self.handlers + self.watchers)
tasks.update(asyncio.all_tasks() - self.initial_tasks)
for task in tasks:
if not task.done():
task.cancel()
results = await asyncio.gather(*tasks, return_exceptions=True)
# A cancelled handler can enter finally and start the recovery wait.
# Let it close its transport, then cancel that new wait as well.
await asyncio.sleep(0)
for task in tasks:
if not task.done():
task.cancel()
results = await asyncio.wait_for(asyncio.gather(*tasks, return_exceptions=True), 1)
for result in results:
if isinstance(result, BaseException) and not isinstance(result, asyncio.CancelledError):
self.errors.append(result)
if exc_type is None and self.errors:
raise self.errors[0]
if exc_type is None:
self.assert_no_unexpected_errors()
for result in close_results:
if isinstance(result, BaseException):
raise result