Skip to content

Commit 5a66dc0

Browse files
abnegateclaude
andcommitted
(fix): use raw coroutine socket for echo backend in integration test
CoServer with port 0 has timing issues. Use Coroutine\Socket directly for reliable ephemeral port binding and echo behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 13e536b commit 5a66dc0

File tree

1 file changed

+23
-24
lines changed

1 file changed

+23
-24
lines changed

tests/Integration/TCPServerTest.php

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PHPUnit\Framework\TestCase;
66
use Swoole\Coroutine;
77
use Swoole\Coroutine\Client;
8-
use Swoole\Coroutine\Server as CoServer;
98
use Utopia\Proxy\Adapter;
109
use Utopia\Proxy\Adapter\TCP as TCPAdapter;
1110
use Utopia\Proxy\Protocol;
@@ -33,26 +32,30 @@ public function testTCPAdapterGetConnectionAndClose(): void
3332
$error = null;
3433

3534
Coroutine\run(function () use (&$result, &$error): void {
36-
// Start a simple echo backend
37-
$backend = new CoServer('127.0.0.1', 0);
38-
$backend->set(['open_eof_check' => false]);
39-
$backendPort = $backend->port;
40-
41-
Coroutine::create(function () use ($backend): void {
42-
$backend->handle(function (Coroutine\Server\Connection $connection): void {
43-
while (true) {
44-
$data = $connection->recv();
45-
if (! \is_string($data) || $data === '') {
46-
break;
47-
}
48-
$connection->send($data);
35+
// Bind an echo server on an ephemeral port
36+
$listener = new Coroutine\Socket(\AF_INET, \SOCK_STREAM, 0);
37+
$listener->bind('127.0.0.1', 0);
38+
$listener->listen(128);
39+
$backendPort = $listener->getsockname()['port'];
40+
41+
// Accept one connection and echo in a separate coroutine
42+
Coroutine::create(function () use ($listener): void {
43+
$peer = $listener->accept();
44+
if ($peer === false) {
45+
return;
46+
}
47+
while (true) {
48+
$data = $peer->recvAll(4096, 2.0);
49+
if ($data === '' || $data === false) {
50+
break;
4951
}
50-
$connection->close();
51-
});
52+
$peer->sendAll($data);
53+
}
54+
$peer->close();
55+
$listener->close();
5256
});
5357

54-
// Give the backend a moment to start
55-
Coroutine::sleep(0.05);
58+
Coroutine::sleep(0.01);
5659

5760
try {
5861
$resolver = new Fixed("127.0.0.1:{$backendPort}");
@@ -61,9 +64,7 @@ public function testTCPAdapterGetConnectionAndClose(): void
6164
$adapter->setTimeout(5.0);
6265
$adapter->setConnectTimeout(2.0);
6366

64-
// getConnection dials the backend and caches by fd.
65-
// The $data arg is routing input (passed to route()),
66-
// not sent to the backend — the server does that.
67+
// getConnection dials the backend and caches by fd
6768
$client = $adapter->getConnection('routing-data', 1);
6869
$this->assertInstanceOf(Client::class, $client);
6970
$this->assertTrue($client->isConnected());
@@ -74,7 +75,7 @@ public function testTCPAdapterGetConnectionAndClose(): void
7475

7576
// Send/recv through the established connection
7677
$client->send('ping');
77-
$response = $client->recv(1.0);
78+
$response = $client->recv(2.0);
7879
$this->assertSame('ping', $response);
7980

8081
// closeConnection cleans up
@@ -89,8 +90,6 @@ public function testTCPAdapterGetConnectionAndClose(): void
8990
$result = true;
9091
} catch (\Throwable $e) {
9192
$error = $e;
92-
} finally {
93-
$backend->shutdown();
9493
}
9594
});
9695

0 commit comments

Comments
 (0)