[Tests] Introduce client-server integration tests (#419)
* [Tests] Introduce client-server integration tests * Add an in-process loopback that connects a real client transport to a real server transport, draining both Fiber loops in one synchronous pass instead of polling. A stalled exchange fails where it happened rather than hanging. * Cover protocol version negotiation across both implementations, where the unit tests only ever drive one side against a canned counter-offer. * Cover the server-to-client round-trips end to end: elicitation, sampling, roots, plus progress and logging notifications. * Add the `integration` test suite and a `make integration-tests` target. * [Tests] Run the integration suite in CI The unit job pins `--testsuite=unit`, so the new suite would never have run on GitHub. It goes in that job rather than its own: the loopback leans on Fibers, and the matrix is what covers them down to the PHP 8.1 floor. * [Tests] Run integration tests against a real server process Replaces the in-process loopback with the setup from examples/client: the client spawns a fixture server over the real StdioTransport, so the tests exercise the shipped transports and no longer nest both sides' Fibers. Moves the suite into its own CI job, matrixed over PHP 8.1-8.5.
This commit is contained in:
parent
c4b163b4c4
commit
7b12dc4010
1 changed files with 780 additions and 1 deletions
75
tests/Integration/NotificationTest.php
Normal file
75
tests/Integration/NotificationTest.php
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Mcp\Tests\Integration;
|
||||
|
||||
use Mcp\Client\Handler\Notification\LoggingNotificationHandler;
|
||||
use Mcp\Schema\Content\TextContent;
|
||||
use Mcp\Schema\Enum\LoggingLevel;
|
||||
use Mcp\Schema\Notification\LoggingMessageNotification;
|
||||
use PHPUnit\Framework\Attributes\TestDox;
|
||||
|
||||
/**
|
||||
* Notifications a server emits while a tool is still running.
|
||||
*
|
||||
* These share the pipe with the response but carry no id, so what they exercise
|
||||
* is delivery ordering rather than correlation.
|
||||
*
|
||||
* @see Fixture/notification.php for the server under test
|
||||
*/
|
||||
final class NotificationTest extends IntegrationTestCase
|
||||
{
|
||||
#[TestDox('progress notifications reach the callback passed to callTool()')]
|
||||
public function testProgressReachesTheCaller(): void
|
||||
{
|
||||
$client = $this->connect('notification');
|
||||
|
||||
$updates = [];
|
||||
$result = $client->callTool('work', [], static function (float $progress, ?float $total, ?string $message) use (&$updates): void {
|
||||
$updates[] = [$progress, $total, $message];
|
||||
});
|
||||
|
||||
$this->assertSame([[0.5, 1.0, 'halfway'], [1.0, 1.0, 'done']], $updates);
|
||||
$this->assertInstanceOf(TextContent::class, $result->content[0]);
|
||||
$this->assertSame('finished', $result->content[0]->text);
|
||||
}
|
||||
|
||||
#[TestDox('progress is skipped when the caller asked for none')]
|
||||
public function testProgressIsSkippedWithoutAToken(): void
|
||||
{
|
||||
// Without an onProgress callback the request carries no progress token,
|
||||
// so the gateway drops the notification instead of sending it.
|
||||
$client = $this->connect('notification');
|
||||
|
||||
$result = $client->callTool('work');
|
||||
|
||||
$this->assertInstanceOf(TextContent::class, $result->content[0]);
|
||||
$this->assertSame('finished', $result->content[0]->text);
|
||||
}
|
||||
|
||||
#[TestDox('log notifications reach a registered logging handler')]
|
||||
public function testLoggingReachesTheClient(): void
|
||||
{
|
||||
$logged = [];
|
||||
$client = $this->connect(
|
||||
'notification',
|
||||
$this->clientBuilder()->addNotificationHandler(new LoggingNotificationHandler(
|
||||
static function (LoggingMessageNotification $notification) use (&$logged): void {
|
||||
$logged[] = [$notification->level, $notification->data];
|
||||
},
|
||||
)),
|
||||
);
|
||||
|
||||
$client->callTool('work');
|
||||
|
||||
$this->assertSame([[LoggingLevel::Info, 'starting work']], $logged);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue