Files
WP-LIT-MCP/tests/Unit/Abilities/ToolAvailabilityTest.php
T

105 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Abilities;
use LIT\MCP\Abilities\Ability;
use LIT\MCP\Abilities\ExposurePolicy;
use LIT\MCP\Abilities\ToolAvailability;
use LIT\MCP\Integrations\Integration;
use LIT\MCP\Integrations\IntegrationRegistry;
use LogicException;
use Mockery;
use PHPUnit\Framework\TestCase;
final class ToolAvailabilityTest extends TestCase
{
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
private static function registry(string ...$names): IntegrationRegistry
{
$abilities = array_map(static function (string $name): Ability {
$ability = Mockery::mock(Ability::class);
$ability->allows('name')->andReturn($name);
$ability->allows('isDirect')->andReturn(false);
return $ability;
}, $names);
$integration = Mockery::mock(Integration::class);
$integration->allows('id')->andReturn('divi');
$integration->allows('isAvailable')->andReturn(true);
$integration->allows('abilities')->andReturn($abilities);
return new IntegrationRegistry($integration);
}
/** The temporal coupling of the former use-by-reference closures: they answered false until Plugin set $policy. */
public function testAQuestionBeforeBindIsAWiringErrorNotFalse(): void
{
$tools = new ToolAvailability();
foreach ([
static fn (): bool => $tools->groupEnabled('divi-library'),
static fn (): bool => $tools->groupWritable('divi-library'),
static fn (): bool => $tools->allows('lit/divi-insert'),
] as $ask) {
try {
$ask();
self::fail('answered before bind()');
} catch (LogicException $e) {
self::assertSame('ToolAvailability asked before Plugin bound it.', $e->getMessage());
}
}
}
/** A gate built before bind() (as DiviIntegration builds its gates) answers from the policy bound later. */
public function testAGateBuiltBeforeBindResolvesLazily(): void
{
$tools = new ToolAvailability();
$libraryVisible = static fn (): bool => $tools->groupEnabled('divi-library');
$libraryWritable = static fn (): bool => $tools->groupWritable('divi-library');
$diviWritable = static fn (): bool => $tools->allows('lit/divi-insert');
$tools->bind(self::registry('lit/divi-insert'), new ExposurePolicy(['@readonly:divi-library' => true]));
self::assertTrue($libraryVisible());
self::assertFalse($libraryWritable());
self::assertTrue($diviWritable());
}
public function testFollowsTheBoundPolicy(): void
{
$tools = new ToolAvailability();
$tools->bind(
self::registry('lit/divi-insert', 'lit/library-insert'),
new ExposurePolicy([
'@group:divi-theme-builder' => ExposurePolicy::DISABLED,
'lit/library-insert' => ExposurePolicy::DISABLED,
])
);
self::assertFalse($tools->groupEnabled('divi-theme-builder'));
self::assertFalse($tools->groupWritable('divi-theme-builder'));
self::assertTrue($tools->groupEnabled('divi-library'));
self::assertTrue($tools->groupWritable('divi-library'));
self::assertTrue($tools->allows('lit/divi-insert'));
self::assertFalse($tools->allows('lit/library-insert'));
self::assertFalse($tools->allows('lit/unknown'), 'not registered = not allowed (ExposurePolicy::allows)');
}
public function testBindsOnlyOnce(): void
{
$tools = new ToolAvailability();
$tools->bind(self::registry(), new ExposurePolicy());
$this->expectException(LogicException::class);
$this->expectExceptionMessage('ToolAvailability is already bound.');
$tools->bind(self::registry(), new ExposurePolicy());
}
}