From 538697f81b1c58975f9a78f4edcb50c5346a76f3 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Tue, 10 Nov 2026 00:48:29 +0100 Subject: [PATCH 1/2] [Server] Allow for a sleep that ends early in the StatelessResponder test --- tests/Unit/Server/Transport/Http/StatelessResponderTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Unit/Server/Transport/Http/StatelessResponderTest.php b/tests/Unit/Server/Transport/Http/StatelessResponderTest.php index 068b37bd..aaf015d1 100644 --- a/tests/Unit/Server/Transport/Http/StatelessResponderTest.php +++ b/tests/Unit/Server/Transport/Http/StatelessResponderTest.php @@ -46,7 +46,8 @@ public function testReadingTheStreamWritesTheFrames(): void $elapsed = microtime(true) - $start; $this->assertSame(['data: {"method":"first"}'."\n\n", ': keep-alive'."\n\n", 'data: {"id":1}'."\n\n"], $flushed, 'Each frame is flushed on its own.'); - $this->assertGreaterThanOrEqual(0.25, $elapsed, 'The idle turn waits before the next poll.'); + // The turn sleeps 250 ms, which Windows can end slightly early. + $this->assertGreaterThanOrEqual(0.2, $elapsed, 'The idle turn waits before the next poll.'); $this->assertLessThan(1.0, $elapsed, 'The idle turn waits once.'); } From b8571634ecdd03ccf8c3bff69d488e79df90d868 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Tue, 10 Nov 2026 00:52:45 +0100 Subject: [PATCH 2/2] [Server] Show why a concurrent writer fails in the FileSessionStore test --- .../Server/Session/FileSessionStoreTest.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Server/Session/FileSessionStoreTest.php b/tests/Unit/Server/Session/FileSessionStoreTest.php index bf771dd1..87f13e7e 100644 --- a/tests/Unit/Server/Session/FileSessionStoreTest.php +++ b/tests/Unit/Server/Session/FileSessionStoreTest.php @@ -385,7 +385,12 @@ public function testConcurrentWritesNeverPublishAPartialPayload(): void // The writers finish in whatever order they like. ksort($exitCodes); - $this->assertSame([0, 0, 0], $exitCodes, 'The concurrent writers did not all run to completion.'); + $errors = ''; + foreach (array_keys($writers) as $i) { + $errors .= file_get_contents($this->directory.\DIRECTORY_SEPARATOR.'writer-'.$i.'.err'); + } + + $this->assertSame([0, 0, 0], $exitCodes, 'The concurrent writers did not all run to completion: '.$errors); foreach (array_keys($writers) as $i) { $this->assertSame('', file_get_contents($this->directory.\DIRECTORY_SEPARATOR.'writer-'.$i.'.err')); @@ -406,12 +411,20 @@ private function spawnWriter(UuidV4 $id, int $index) require $argv[1]; - $store = new Mcp\Server\Session\FileSessionStore($argv[2]); + // The store logs why a write failed, which ends up in this writer's error file. + $logger = new class extends Psr\Log\AbstractLogger { + public function log($level, $message, array $context = []): void + { + fwrite(\STDERR, $level.': '.$message.' '.json_encode($context).\PHP_EOL); + } + }; + $store = new Mcp\Server\Session\FileSessionStore($argv[2], logger: $logger); $id = Symfony\Component\Uid\Uuid::fromString($argv[3]); $payload = json_encode(['queue' => array_fill(0, 200, str_repeat('x', 200))]); for ($i = 0; $i < 200; ++$i) { if (!$store->write($id, $payload)) { + fwrite(\STDERR, sprintf('Write %d of 200 failed.', $i + 1).\PHP_EOL); exit(1); } }