refactor: ToolAvailability ersetzt die sechs Referenz-Closures in Plugin (Frage vor der Verdrahtung wirft statt still false); DiviIntegration bekommt den Tools-Tab-Zustand als Objekt; Plugin::integrations() mit Verdrahtungstest für Reihenfolge, einmaliges Booten und spätes Binden
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6c59934caa
commit
e6e0f914ba
@@ -0,0 +1,104 @@
|
||||
<?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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user