Files
WP-LIT-MCP/tests/Unit/Abilities/CompatibilityGatesTest.php
T
Jan-Niclas LoosenandClaude Opus 5.5 7d6a97a54e refactor(compat): Freigabe-Bausteine generisch
ReleaseStore, CheckReportStore, CheckReport, CheckStep und CompatibilityCheck liegen im Kern und arbeiten je Profil (Optionen lit_mcp_<id>_releases|_check_report; Divi behält Namen und Schlüssel divi_version). Divi hängt Katalog-, Format- und Schreibtest über Profile::extraSteps an. Ein roter Prüfbericht für den aktuellen Stand sperrt LockOnFailure-Profile bis zur nächsten grünen Prüfung; Sperrgründe nennen nur Schritt-Labels, Ausnahmen gehen ins Error-Log. Profile bekommt testedVersions, stateKey, extraSteps, manifestCheck und reportVersionKey und wird mit benannten Argumenten gebaut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:51:03 +01:00

114 lines
4.0 KiB
PHP

<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Abilities;
use Brain\Monkey\Functions;
use LIT\MCP\Abilities\Ability;
use LIT\MCP\Abilities\AbilityRegistrar;
use LIT\MCP\Abilities\DeclaresRequirements;
use LIT\MCP\Compatibility\CapabilityGate;
use LIT\MCP\Compatibility\Compatibility;
use LIT\MCP\Compatibility\Profile;
use LIT\MCP\Compatibility\Tier;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use LIT\MCP\Integrations\Divi\Release\DiviFeatureGate;
use LIT\MCP\Integrations\Divi\Release\DiviProfile;
use LIT\MCP\Integrations\Integration;
use LIT\MCP\Integrations\IntegrationRegistry;
use LIT\MCP\Integrations\Pods\PodsCompatibility;
use LIT\MCP\Server\RequestContext;
use LIT\MCP\Tests\Support\DiviFeatureFixtures;
use LIT\MCP\Tests\Support\FakeCapability;
use LIT\MCP\Tests\Support\FakeChangeLog;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
use WP_Error;
/**
* The two guards Plugin hands to AbilityRegistrar, [DiviFeatureGate, CapabilityGate], through the real
* permission callback: each ID is judged by exactly one of them and none passes both unjudged.
*/
final class CompatibilityGatesTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
RequestContext::reset(true);
Functions\when('current_user_can')->justReturn(true);
}
protected function tearDown(): void
{
RequestContext::reset();
parent::tearDown();
}
/** @param list<string> $ids */
private function permission(array $ids): bool|WP_Error
{
$ability = Mockery::mock(Ability::class, DeclaresRequirements::class);
$ability->allows('name')->andReturn('lit/test');
$ability->allows('label')->andReturn('Test');
$ability->allows('description')->andReturn('Test');
$ability->allows('inputSchema')->andReturn(['type' => 'object', 'properties' => []]);
$ability->allows('outputSchema')->andReturn(null);
$ability->allows('annotations')->andReturn(['readonly' => true]);
$ability->allows('isDirect')->andReturn(true);
$ability->allows('requirements')->andReturn($ids);
$ability->allows('permission')->andReturn(true);
$free = new Profile(id: 'fake', label: 'Fake Plugin', tier: Tier::LockOnFailure, capabilities: [FakeCapability::Read]);
$compat = new Compatibility([DiviProfile::profile(), PodsCompatibility::profile(), $free]);
$integration = Mockery::mock(Integration::class);
$integration->allows('id')->andReturn('core');
$integration->allows('isAvailable')->andReturn(true);
$integration->allows('abilities')->andReturn([$ability]);
$registrar = new AbilityRegistrar(
new IntegrationRegistry($integration),
null,
[new DiviFeatureGate(FakeChangeLog::forbidden()), new CapabilityGate($compat, FakeChangeLog::forbidden())]
);
return ($registrar->argsFor($ability)['permission_callback'])([]);
}
public function testAFreeNonDiviIdPassesTheDiviGateAndTheCapabilityGate(): void
{
self::assertTrue($this->permission(['fake-read']));
}
public function testALockedPodsIdPassesTheDiviGateAndIsRefusedByTheCapabilityGate(): void
{
DiviFeatureFixtures::useStates([]);
$error = $this->permission(['pods-field-read']);
self::assertToolError($error, 'compat_incompatible');
}
public function testAnUnknownIdIsRefusedByTheCapabilityGate(): void
{
DiviFeatureFixtures::useStates([]);
$payload = self::assertToolError($this->permission(['page-write', 'nobody-knows-me']), 'compat_incompatible');
self::assertSame(['nobody-knows-me'], $payload['unknown']);
}
public function testAReleasedDiviIdPassesBothGates(): void
{
DiviFeatureFixtures::useStates([]);
self::assertTrue($this->permission(['page-write']));
}
public function testALockedDiviIdIsRefusedByTheDiviGateAloneNotTwice(): void
{
DiviFeatureFixtures::lock(DiviFeature::PageWrite);
self::assertToolError($this->permission(['page-write']), 'divi_untested');
}
}