Skip to content

Commit 56edb8a

Browse files
[MCP SDK] Add StreamTransportTest for existing behavior
1 parent 22a3555 commit 56edb8a

File tree

4 files changed

+166
-2
lines changed

4 files changed

+166
-2
lines changed

src/mcp-sdk/phpunit.xml.dist

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
colors="true"
66
executionOrder="depends,defects"
77
beStrictAboutOutputDuringTests="true"
8-
failOnRisky="true"
9-
failOnWarning="true">
8+
failOnRisky="true"
9+
failOnWarning="true"
10+
bootstrap="tests/bootstrap.php">
1011

1112
<testsuites>
1213
<testsuite name="default">
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\McpSdk\Server\Transport\Sse;
13+
14+
use Symfony\AI\McpSdk\Tests\Server\Transport\Sse\StreamTransportTest;
15+
16+
if (!\function_exists(__NAMESPACE__.'\\connection_aborted')) {
17+
function connection_aborted(): int
18+
{
19+
return StreamTransportTest::$connectionAborted;
20+
}
21+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\McpSdk\Tests\Server\Transport\Sse;
13+
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\Attributes\Small;
16+
use PHPUnit\Framework\TestCase;
17+
use Symfony\AI\McpSdk\Server\Transport\Sse\StoreInterface;
18+
use Symfony\AI\McpSdk\Server\Transport\Sse\StreamTransport;
19+
use Symfony\Component\Uid\Uuid;
20+
21+
#[Small]
22+
#[CoversClass(StreamTransport::class)]
23+
class StreamTransportTest extends TestCase
24+
{
25+
public static int $connectionAborted = 0;
26+
27+
protected function tearDown(): void
28+
{
29+
parent::tearDown();
30+
self::$connectionAborted = 0;
31+
}
32+
33+
public function testInitializeEmitsEndpointEvent(): void
34+
{
35+
$endpoint = 'https://example.test/mcp/messages';
36+
$store = $this->createMock(StoreInterface::class);
37+
$id = Uuid::v7();
38+
$transport = new StreamTransport($endpoint, $store, $id);
39+
$actual = $this->capture(fn () => $transport->initialize());
40+
41+
$expected = 'event: endpoint'.\PHP_EOL.'data: '.$endpoint.\PHP_EOL.\PHP_EOL;
42+
$this->assertSame($expected, $actual);
43+
}
44+
45+
public function testReceiveYieldsSingleMessageFromStore(): void
46+
{
47+
$id = Uuid::v7();
48+
$store = $this->createMock(StoreInterface::class);
49+
$store->expects($this->once())
50+
->method('pop')
51+
->with($id)
52+
->willReturn('hello');
53+
54+
$transport = new StreamTransport('x', $store, $id);
55+
$generator = $transport->receive();
56+
57+
$this->assertInstanceOf(\Generator::class, $generator);
58+
$this->assertSame('hello', $generator->current());
59+
$generator->next();
60+
$this->assertFalse($generator->valid());
61+
}
62+
63+
public function testSendEmitsMessageEventAndFlushes(): void
64+
{
65+
$store = $this->createMock(StoreInterface::class);
66+
$id = Uuid::v7();
67+
$transport = new StreamTransport('x', $store, $id);
68+
$actual = $this->capture(fn () => $transport->send('payload'));
69+
70+
$expected = 'event: message'.\PHP_EOL.'data: payload'.\PHP_EOL.\PHP_EOL;
71+
$this->assertSame($expected, $actual);
72+
}
73+
74+
public function testMultipleSendsProduceMultipleResponses(): void
75+
{
76+
$store = $this->createMock(StoreInterface::class);
77+
$id = Uuid::v7();
78+
$transport = new StreamTransport('x', $store, $id);
79+
$first = $this->capture(fn () => $transport->send('one'));
80+
$second = $this->capture(fn () => $transport->send('two'));
81+
82+
$this->assertSame([
83+
'event: message'.\PHP_EOL.'data: one'.\PHP_EOL.\PHP_EOL,
84+
'event: message'.\PHP_EOL.'data: two'.\PHP_EOL.\PHP_EOL,
85+
], [$first, $second]);
86+
}
87+
88+
public function testCloseRemovesSessionFromStore(): void
89+
{
90+
$id = Uuid::v7();
91+
$store = $this->createMock(StoreInterface::class);
92+
$store->expects($this->once())
93+
->method('remove')
94+
->with($id);
95+
96+
$transport = new StreamTransport('x', $store, $id);
97+
$transport->close();
98+
}
99+
100+
public function testIsConnectedRespectsConnectionAbortedPolyfill(): void
101+
{
102+
$id = Uuid::v7();
103+
$store = $this->createMock(StoreInterface::class);
104+
$transport = new StreamTransport('x', $store, $id);
105+
106+
self::$connectionAborted = 0;
107+
$this->assertTrue($transport->isConnected());
108+
109+
self::$connectionAborted = 1;
110+
$this->assertFalse($transport->isConnected());
111+
112+
self::$connectionAborted = 0; // reset for other tests
113+
}
114+
115+
private function capture(callable $fn): string
116+
{
117+
$buffer = '';
118+
ob_start(function (string $chunk) use (&$buffer) {
119+
$buffer .= $chunk;
120+
121+
return '';
122+
});
123+
124+
$fn();
125+
126+
ob_end_flush();
127+
128+
return $buffer;
129+
}
130+
}

src/mcp-sdk/tests/bootstrap.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
require __DIR__.'/Fixtures/Sse/connection_aborted_polyfill.php';

0 commit comments

Comments
 (0)