From 6d7af596e1ecf620ff8479c8c4c508b818db59e5 Mon Sep 17 00:00:00 2001 From: Fabian Martinez <46371672+famarting@users.noreply.github.com> Date: Mon, 19 Oct 2026 16:09:12 +0200 Subject: [PATCH] test(actor): retry the app-channel listener test on a taken port test_owns_app_channel_listener picks a port by binding 127.0.0.1:0 and releasing it, then has the host bind [::] on that number. Anything can take the port in that gap, which failed the macOS 3.13 job with "Failed to bind to address [::]:49271". The host must bind the port it is given, so the test now retries on a fresh port when the bind fails. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com> --- tests/actor/test_grpc_host.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/actor/test_grpc_host.py b/tests/actor/test_grpc_host.py index c5f5c668c..60d00781c 100644 --- a/tests/actor/test_grpc_host.py +++ b/tests/actor/test_grpc_host.py @@ -570,14 +570,30 @@ def test_resolve_app_port_precedence(self): with mock.patch.dict('os.environ', {}, clear=True): self.assertIsNone(ActorGrpcHost._resolve_app_port(None)) + async def _start_host_on_free_app_port(self, attempts: int = 5) -> ActorGrpcHost: + """Starts a host whose app-channel listener binds a free port. + + _free_port releases the port before the host binds it, so another + socket can take it in between; a failed bind retries on a fresh port. + """ + for attempt in range(attempts): + host = ActorGrpcHost(address=f'localhost:{self.grpc_port}', app_port=_free_port()) + await host.register_actor(HostTestActor) + try: + await host.start() + except RuntimeError as error: + if 'Failed to bind' not in str(error) or attempt == attempts - 1: + raise + continue + return host + raise AssertionError('attempts must be at least 1') + async def test_owns_app_channel_listener(self): # When an app port is set, the host starts the empty app-channel # listener daprd needs and tears it down on close. - host = ActorGrpcHost(address=f'localhost:{self.grpc_port}', app_port=_free_port()) self._fake_dapr_server.actor_stream_plans.append(_plan()) - await host.register_actor(HostTestActor) + host = await self._start_host_on_free_app_port() try: - await host.start() self.assertIsNotNone(host._app_server) finally: await host.close()