allows('name')->andReturn('lit/guide'); $direct->allows('isDirect')->andReturn(true); $indirect = Mockery::mock(Ability::class); $indirect->allows('name')->andReturn('lit/rare-tool'); $indirect->allows('isDirect')->andReturn(false); $core = Mockery::mock(Integration::class); $core->allows('id')->andReturn('core'); $core->allows('isAvailable')->andReturn(true); $core->allows('abilities')->andReturn([$direct, $indirect]); return new IntegrationRegistry($core); } private function instructions(): InstructionsBuilder { // InstructionsBuilder is final (not mockable) and pure – use the real one. return new InstructionsBuilder($this->registry()); } public function testServerUsesOauthRouteDirectToolsPlusMetaToolsAndInstructions(): void { $args = (new ServerRegistrar($this->registry(), $this->instructions(), static fn () => true))->serverArguments(); self::assertSame('mcp-oauth-server', $args['id']); self::assertSame('mcp', $args['namespace']); self::assertSame('mcp-oauth-server', $args['route']); self::assertStringContainsString('Aktive Integrationen: core', $args['description']); self::assertSame( ['lit/guide', 'mcp-adapter/discover-abilities', 'mcp-adapter/get-ability-info', 'mcp-adapter/execute-ability'], $args['tools'] ); self::assertNotContains('lit/rare-tool', $args['tools']); } public function testNoServerWhenOauthIsDisabled(): void { $registrar = new ServerRegistrar($this->registry(), $this->instructions(), static fn () => false); self::assertNull($registrar->serverArguments()); } public function testCreateServerOutsideAdapterInitDoesNothing(): void { Functions\when('doing_action')->justReturn(false); $oauthChecked = false; $registrar = new ServerRegistrar( $this->registry(), $this->instructions(), static function () use (&$oauthChecked): bool { $oauthChecked = true; return true; } ); $registrar->createServer(); self::assertFalse($oauthChecked, 'createServer must bail out before touching the adapter'); } public function testUnhookOAuthDefaultServerRemovesOnlyTheMcpOauthCallback(): void { $oauthRegistrar = Mockery::mock(OAuthServerRegistrar::class); $oauthCallback = [$oauthRegistrar, 'register_server']; $unrelatedCallback = static function (): void { }; $hook = new class { /** @var array> */ public array $callbacks = []; }; $hook->callbacks = [ 5 => ['unrelated-id' => ['function' => $unrelatedCallback, 'accepted_args' => 0]], 10 => ['oauth-id' => ['function' => $oauthCallback, 'accepted_args' => 0]], ]; $GLOBALS['wp_filter']['mcp_adapter_init'] = $hook; Functions\expect('remove_action') ->once() ->with('mcp_adapter_init', $oauthCallback, 10); (new ServerRegistrar($this->registry(), $this->instructions()))->unhookOAuthDefaultServer(); } public function testUnhookOAuthDefaultServerIsNoOpWhenHookIsMissing(): void { unset($GLOBALS['wp_filter']['mcp_adapter_init']); Functions\expect('remove_action')->never(); (new ServerRegistrar($this->registry(), $this->instructions()))->unhookOAuthDefaultServer(); } }