Skip to content

Commit 468b9ff

Browse files
feat: Fix test compatibility with new session management architecture
1 parent 1536db2 commit 468b9ff

File tree

8 files changed

+152
-77
lines changed

8 files changed

+152
-77
lines changed

src/Server/Transport/InMemoryTransport.php

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,65 @@
1212
namespace Mcp\Server\Transport;
1313

1414
use Mcp\Server\TransportInterface;
15+
use Symfony\Component\Uid\Uuid;
1516

1617
/**
1718
* @author Tobias Nyholm <[email protected]>
1819
*/
1920
class InMemoryTransport implements TransportInterface
2021
{
2122
private bool $connected = true;
23+
private $messageListener = null;
24+
private $sessionDestroyListener = null;
25+
private ?Uuid $sessionId = null;
2226

2327
/**
2428
* @param list<string> $messages
2529
*/
2630
public function __construct(
2731
private readonly array $messages = [],
28-
) {
29-
}
32+
) {}
33+
34+
public function initialize(): void {}
3035

31-
public function initialize(): void
36+
public function onMessage(callable $listener): void
3237
{
38+
$this->messageListener = $listener;
3339
}
3440

35-
public function isConnected(): bool
41+
public function send(string $data, array $context): void
3642
{
37-
return $this->connected;
43+
if (isset($context['session_id'])) {
44+
$this->sessionId = $context['session_id'];
45+
}
3846
}
3947

40-
public function receive(): \Generator
48+
public function listen(): mixed
4149
{
42-
yield from $this->messages;
50+
foreach ($this->messages as $message) {
51+
if (is_callable($this->messageListener)) {
52+
call_user_func($this->messageListener, $message, $this->sessionId);
53+
}
54+
}
55+
4356
$this->connected = false;
57+
58+
if (is_callable($this->sessionDestroyListener) && $this->sessionId !== null) {
59+
call_user_func($this->sessionDestroyListener, $this->sessionId);
60+
}
61+
62+
return null;
4463
}
4564

46-
public function send(string $data): void
65+
public function onSessionEnd(callable $listener): void
4766
{
67+
$this->sessionDestroyListener = $listener;
4868
}
4969

5070
public function close(): void
5171
{
72+
if (is_callable($this->sessionDestroyListener) && $this->sessionId !== null) {
73+
call_user_func($this->sessionDestroyListener, $this->sessionId);
74+
}
5275
}
5376
}

src/Server/Transport/StdioTransport.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function listen(): mixed
7575

7676
$this->logger->info('StdioTransport finished listening.');
7777

78-
if (is_callable($this->sessionEndListener)) {
78+
if (is_callable($this->sessionEndListener) && $this->sessionId !== null) {
7979
call_user_func($this->sessionEndListener, $this->sessionId);
8080
}
8181

@@ -89,7 +89,7 @@ public function onSessionEnd(callable $listener): void
8989

9090
public function close(): void
9191
{
92-
if (is_callable($this->sessionEndListener)) {
92+
if (is_callable($this->sessionEndListener) && $this->sessionId !== null) {
9393
call_user_func($this->sessionEndListener, $this->sessionId);
9494
}
9595

tests/JsonRpc/HandlerTest.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515
use Mcp\JsonRpc\MessageFactory;
1616
use Mcp\Schema\JsonRpc\Response;
1717
use Mcp\Server\MethodHandlerInterface;
18+
use Mcp\Server\Session\SessionFactoryInterface;
19+
use Mcp\Server\Session\SessionStoreInterface;
20+
use Mcp\Server\Session\SessionInterface;
1821
use PHPUnit\Framework\Attributes\TestDox;
1922
use PHPUnit\Framework\TestCase;
23+
use Psr\Log\NullLogger;
24+
use Symfony\Component\Uid\Uuid;
2025

2126
class HandlerTest extends TestCase
2227
{
@@ -44,9 +49,24 @@ public function testHandleMultipleNotifications()
4449
$handlerC->method('supports')->willReturn(true);
4550
$handlerC->expects($this->once())->method('handle');
4651

47-
$jsonRpc = new Handler(MessageFactory::make(), [$handlerA, $handlerB, $handlerC]);
52+
$sessionFactory = $this->createMock(SessionFactoryInterface::class);
53+
$sessionStore = $this->createMock(SessionStoreInterface::class);
54+
$session = $this->createMock(SessionInterface::class);
55+
56+
$sessionFactory->method('create')->willReturn($session);
57+
$sessionFactory->method('createWithId')->willReturn($session);
58+
$sessionStore->method('exists')->willReturn(true);
59+
60+
$jsonRpc = new Handler(
61+
MessageFactory::make(),
62+
$sessionFactory,
63+
$sessionStore,
64+
[$handlerA, $handlerB, $handlerC]
65+
);
66+
$sessionId = Uuid::v4();
4867
$result = $jsonRpc->process(
49-
'{"jsonrpc": "2.0", "method": "notifications/initialized"}'
68+
'{"jsonrpc": "2.0", "method": "notifications/initialized"}',
69+
$sessionId
5070
);
5171
iterator_to_array($result);
5272
}
@@ -75,9 +95,24 @@ public function testHandleMultipleRequests()
7595
$handlerC->method('supports')->willReturn(true);
7696
$handlerC->expects($this->never())->method('handle');
7797

78-
$jsonRpc = new Handler(MessageFactory::make(), [$handlerA, $handlerB, $handlerC]);
98+
$sessionFactory = $this->createMock(SessionFactoryInterface::class);
99+
$sessionStore = $this->createMock(SessionStoreInterface::class);
100+
$session = $this->createMock(SessionInterface::class);
101+
102+
$sessionFactory->method('create')->willReturn($session);
103+
$sessionFactory->method('createWithId')->willReturn($session);
104+
$sessionStore->method('exists')->willReturn(true);
105+
106+
$jsonRpc = new Handler(
107+
MessageFactory::make(),
108+
$sessionFactory,
109+
$sessionStore,
110+
[$handlerA, $handlerB, $handlerC]
111+
);
112+
$sessionId = Uuid::v4();
79113
$result = $jsonRpc->process(
80-
'{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'
114+
'{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}',
115+
$sessionId
81116
);
82117
iterator_to_array($result);
83118
}

tests/Server/RequestHandler/CallToolHandlerTest.php

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,26 @@
2020
use Mcp\Schema\Request\CallToolRequest;
2121
use Mcp\Schema\Result\CallToolResult;
2222
use Mcp\Server\RequestHandler\CallToolHandler;
23+
use Mcp\Server\Session\SessionInterface;
2324
use PHPUnit\Framework\MockObject\MockObject;
2425
use PHPUnit\Framework\TestCase;
2526
use Psr\Log\LoggerInterface;
2627

2728
class CallToolHandlerTest extends TestCase
2829
{
2930
private CallToolHandler $handler;
30-
private ToolCallerInterface|MockObject $toolExecutor;
31+
private ToolCallerInterface|MockObject $toolCaller;
3132
private LoggerInterface|MockObject $logger;
33+
private SessionInterface|MockObject $session;
3234

3335
protected function setUp(): void
3436
{
35-
$this->toolExecutor = $this->createMock(ToolCallerInterface::class);
37+
$this->toolCaller = $this->createMock(ToolCallerInterface::class);
3638
$this->logger = $this->createMock(LoggerInterface::class);
39+
$this->session = $this->createMock(SessionInterface::class);
3740

3841
$this->handler = new CallToolHandler(
39-
$this->toolExecutor,
42+
$this->toolCaller,
4043
$this->logger,
4144
);
4245
}
@@ -53,7 +56,7 @@ public function testHandleSuccessfulToolCall(): void
5356
$request = $this->createCallToolRequest('greet_user', ['name' => 'John']);
5457
$expectedResult = new CallToolResult([new TextContent('Hello, John!')]);
5558

56-
$this->toolExecutor
59+
$this->toolCaller
5760
->expects($this->once())
5861
->method('call')
5962
->with($request)
@@ -63,7 +66,7 @@ public function testHandleSuccessfulToolCall(): void
6366
->expects($this->never())
6467
->method('error');
6568

66-
$response = $this->handler->handle($request);
69+
$response = $this->handler->handle($request, $this->session);
6770

6871
$this->assertInstanceOf(Response::class, $response);
6972
$this->assertEquals($request->getId(), $response->id);
@@ -75,13 +78,13 @@ public function testHandleToolCallWithEmptyArguments(): void
7578
$request = $this->createCallToolRequest('simple_tool', []);
7679
$expectedResult = new CallToolResult([new TextContent('Simple result')]);
7780

78-
$this->toolExecutor
81+
$this->toolCaller
7982
->expects($this->once())
8083
->method('call')
8184
->with($request)
8285
->willReturn($expectedResult);
8386

84-
$response = $this->handler->handle($request);
87+
$response = $this->handler->handle($request, $this->session);
8588

8689
$this->assertInstanceOf(Response::class, $response);
8790
$this->assertSame($expectedResult, $response->result);
@@ -99,13 +102,13 @@ public function testHandleToolCallWithComplexArguments(): void
99102
$request = $this->createCallToolRequest('complex_tool', $arguments);
100103
$expectedResult = new CallToolResult([new TextContent('Complex result')]);
101104

102-
$this->toolExecutor
105+
$this->toolCaller
103106
->expects($this->once())
104107
->method('call')
105108
->with($request)
106109
->willReturn($expectedResult);
107110

108-
$response = $this->handler->handle($request);
111+
$response = $this->handler->handle($request, $this->session);
109112

110113
$this->assertInstanceOf(Response::class, $response);
111114
$this->assertSame($expectedResult, $response->result);
@@ -116,7 +119,7 @@ public function testHandleToolNotFoundExceptionReturnsError(): void
116119
$request = $this->createCallToolRequest('nonexistent_tool', ['param' => 'value']);
117120
$exception = new ToolNotFoundException($request);
118121

119-
$this->toolExecutor
122+
$this->toolCaller
120123
->expects($this->once())
121124
->method('call')
122125
->with($request)
@@ -133,7 +136,7 @@ public function testHandleToolNotFoundExceptionReturnsError(): void
133136
],
134137
);
135138

136-
$response = $this->handler->handle($request);
139+
$response = $this->handler->handle($request, $this->session);
137140

138141
$this->assertInstanceOf(Error::class, $response);
139142
$this->assertEquals($request->getId(), $response->id);
@@ -146,7 +149,7 @@ public function testHandleToolExecutionExceptionReturnsError(): void
146149
$request = $this->createCallToolRequest('failing_tool', ['param' => 'value']);
147150
$exception = new ToolCallException($request, new \RuntimeException('Tool execution failed'));
148151

149-
$this->toolExecutor
152+
$this->toolCaller
150153
->expects($this->once())
151154
->method('call')
152155
->with($request)
@@ -163,7 +166,7 @@ public function testHandleToolExecutionExceptionReturnsError(): void
163166
],
164167
);
165168

166-
$response = $this->handler->handle($request);
169+
$response = $this->handler->handle($request, $this->session);
167170

168171
$this->assertInstanceOf(Error::class, $response);
169172
$this->assertEquals($request->getId(), $response->id);
@@ -176,13 +179,13 @@ public function testHandleWithNullResult(): void
176179
$request = $this->createCallToolRequest('null_tool', []);
177180
$expectedResult = new CallToolResult([]);
178181

179-
$this->toolExecutor
182+
$this->toolCaller
180183
->expects($this->once())
181184
->method('call')
182185
->with($request)
183186
->willReturn($expectedResult);
184187

185-
$response = $this->handler->handle($request);
188+
$response = $this->handler->handle($request, $this->session);
186189

187190
$this->assertInstanceOf(Response::class, $response);
188191
$this->assertSame($expectedResult, $response->result);
@@ -193,13 +196,13 @@ public function testHandleWithErrorResult(): void
193196
$request = $this->createCallToolRequest('error_tool', []);
194197
$expectedResult = CallToolResult::error([new TextContent('Tool error occurred')]);
195198

196-
$this->toolExecutor
199+
$this->toolCaller
197200
->expects($this->once())
198201
->method('call')
199202
->with($request)
200203
->willReturn($expectedResult);
201204

202-
$response = $this->handler->handle($request);
205+
$response = $this->handler->handle($request, $this->session);
203206

204207
$this->assertInstanceOf(Response::class, $response);
205208
$this->assertSame($expectedResult, $response->result);
@@ -208,7 +211,7 @@ public function testHandleWithErrorResult(): void
208211

209212
public function testConstructorWithDefaultLogger(): void
210213
{
211-
$handler = new CallToolHandler($this->toolExecutor);
214+
$handler = new CallToolHandler($this->toolCaller);
212215

213216
$this->assertInstanceOf(CallToolHandler::class, $handler);
214217
}
@@ -218,7 +221,7 @@ public function testHandleLogsErrorWithCorrectParameters(): void
218221
$request = $this->createCallToolRequest('test_tool', ['key1' => 'value1', 'key2' => 42]);
219222
$exception = new ToolCallException($request, new \RuntimeException('Custom error message'));
220223

221-
$this->toolExecutor
224+
$this->toolCaller
222225
->expects($this->once())
223226
->method('call')
224227
->willThrowException($exception);
@@ -234,21 +237,21 @@ public function testHandleLogsErrorWithCorrectParameters(): void
234237
],
235238
);
236239

237-
$this->handler->handle($request);
240+
$this->handler->handle($request, $this->session);
238241
}
239242

240243
public function testHandleWithSpecialCharactersInToolName(): void
241244
{
242245
$request = $this->createCallToolRequest('tool-with_special.chars', []);
243246
$expectedResult = new CallToolResult([new TextContent('Special tool result')]);
244247

245-
$this->toolExecutor
248+
$this->toolCaller
246249
->expects($this->once())
247250
->method('call')
248251
->with($request)
249252
->willReturn($expectedResult);
250253

251-
$response = $this->handler->handle($request);
254+
$response = $this->handler->handle($request, $this->session);
252255

253256
$this->assertInstanceOf(Response::class, $response);
254257
$this->assertSame($expectedResult, $response->result);
@@ -264,13 +267,13 @@ public function testHandleWithSpecialCharactersInArguments(): void
264267
$request = $this->createCallToolRequest('unicode_tool', $arguments);
265268
$expectedResult = new CallToolResult([new TextContent('Unicode handled')]);
266269

267-
$this->toolExecutor
270+
$this->toolCaller
268271
->expects($this->once())
269272
->method('call')
270273
->with($request)
271274
->willReturn($expectedResult);
272275

273-
$response = $this->handler->handle($request);
276+
$response = $this->handler->handle($request, $this->session);
274277

275278
$this->assertInstanceOf(Response::class, $response);
276279
$this->assertSame($expectedResult, $response->result);
@@ -284,7 +287,7 @@ private function createCallToolRequest(string $name, array $arguments): CallTool
284287
return CallToolRequest::fromArray([
285288
'jsonrpc' => '2.0',
286289
'method' => CallToolRequest::getMethod(),
287-
'id' => 'test-request-'.uniqid(),
290+
'id' => 'test-request-' . uniqid(),
288291
'params' => [
289292
'name' => $name,
290293
'arguments' => $arguments,

0 commit comments

Comments
 (0)