CultLib/packages/cultmesh-py/tests/test_server_bind.py
Libby cff68d5528 cultmesh-py: bind test proves only the UDP number was taken
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 19:28:50 +00:00

69 lines
2.6 KiB
Python

"""A server asked for any port finds a TCP+UDP pair; one asked for a port gets that port or an honest failure."""
from __future__ import annotations
import errno
import socket
import threading
import unittest
from unittest.mock import patch
from cultmesh_py import create_node
from cultmesh_py.server import CultMeshLocalServer
def server(**kwargs) -> CultMeshLocalServer:
return CultMeshLocalServer(node=create_node(runtime_id="bind-test"), host="127.0.0.1", **kwargs)
class ServerBindTests(unittest.TestCase):
def test_port_zero_retries_with_a_fresh_port_when_another_process_holds_the_udp_number(self) -> None:
real_bind = socket.socket.bind
refused: list[int] = []
def bind(sock: socket.socket, address) -> None:
# The first two UDP binds find the number taken, as a UDP listener of another process would.
if sock.type == socket.SOCK_DGRAM and len(refused) < 2:
refused.append(address[1])
raise OSError(errno.EADDRINUSE, "Address already in use")
real_bind(sock, address)
with patch.object(socket.socket, "bind", bind):
started = server().start()
try:
self.assertEqual(len(refused), 2)
self.assertNotIn(started.port, refused[:1], "the retry used a fresh TCP port")
probe = socket.create_connection(("127.0.0.1", started.port), timeout=2.0)
probe.close()
finally:
started.stop()
def test_an_explicit_port_whose_udp_number_is_taken_fails_and_leaves_nothing_running(self) -> None:
holder = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
holder.bind(("127.0.0.1", 0))
port = holder.getsockname()[1]
free = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
free.bind(("127.0.0.1", port)) # the TCP number is free: only the UDP one is taken
finally:
free.close()
threads_before = threading.active_count()
try:
failing = server(port=port)
with self.assertRaises(OSError) as raised:
failing.start()
self.assertEqual(raised.exception.errno, errno.EADDRINUSE)
self.assertIsNone(failing._socket)
self.assertEqual(threading.active_count(), threads_before, "no accept thread was left running")
# The TCP number was released, not left listening.
free = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
free.bind(("127.0.0.1", port))
finally:
free.close()
finally:
holder.close()
if __name__ == "__main__":
unittest.main()