Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
126 lines
5.0 KiB
PHP
126 lines
5.0 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Tests\Unit\Integrations\Divi;
|
||
|
||
use Brain\Monkey\Functions;
|
||
use LIT\MCP\Abilities\Ability;
|
||
use LIT\MCP\Integrations\Divi\DiviIntegration;
|
||
use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer;
|
||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||
use LIT\MCP\Tests\Support\LibraryFixtures;
|
||
use LIT\MCP\Tests\Unit\TestCase;
|
||
use Mockery;
|
||
use WP_Error;
|
||
|
||
final class DiviIntegrationTest extends TestCase
|
||
{
|
||
/** Grows with every task that adds Divi abilities – keep in the order of abilities(). */
|
||
private const EXPECTED = [
|
||
'lit/divi-outline',
|
||
'lit/divi-node-get',
|
||
'lit/divi-catalog',
|
||
'lit/divi-design-system',
|
||
'lit/divi-insert',
|
||
'lit/divi-update',
|
||
'lit/divi-move',
|
||
'lit/divi-delete',
|
||
'lit/divi-duplicate-node',
|
||
'lit/divi-replace-text',
|
||
'lit/divi-node-copy',
|
||
];
|
||
|
||
public function testProvidesTheDiviAbilitiesOnlyWithDivi5(): void
|
||
{
|
||
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||
|
||
self::assertSame('divi', $integration->id());
|
||
self::assertFalse($integration->isAvailable(), 'no Divi classes in unit tests');
|
||
self::assertSame(
|
||
self::EXPECTED,
|
||
array_map(static fn (Ability $ability): string => $ability->name(), $integration->abilities())
|
||
);
|
||
|
||
foreach ($integration->abilities() as $ability) {
|
||
self::assertSame('object', $ability->inputSchema()['type'], $ability->name());
|
||
self::assertFalse($ability->inputSchema()['additionalProperties'], $ability->name());
|
||
self::assertNotNull($ability->outputSchema(), $ability->name());
|
||
}
|
||
}
|
||
|
||
public function testProvidesTheLibraryGroup(): void
|
||
{
|
||
Functions\when('__')->returnArg();
|
||
$library = (new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden()))->library();
|
||
|
||
self::assertSame('divi-library', $library->id());
|
||
self::assertSame('Divi-Bibliothek', $library->label());
|
||
self::assertFalse($library->isAvailable(), 'no Divi classes in unit tests');
|
||
self::assertSame(
|
||
['lit/library-list', 'lit/library-save', 'lit/library-insert'],
|
||
array_map(static fn (Ability $ability): string => $ability->name(), $library->abilities())
|
||
);
|
||
foreach ($library->abilities() as $ability) {
|
||
self::assertFalse($ability->inputSchema()['additionalProperties'], $ability->name());
|
||
self::assertNotNull($ability->outputSchema(), $ability->name());
|
||
}
|
||
}
|
||
|
||
/** Ruling P9 (E9): lit/divi-node-copy follows lit/divi-insert's Tools-tab state. */
|
||
public function testNodeCopyFollowsLitDiviInsert(): void
|
||
{
|
||
$integration = new DiviIntegration(
|
||
Mockery::mock(ContentSchema::class),
|
||
FakeChangeLog::forbidden(),
|
||
diviWritable: static fn (): bool => false
|
||
);
|
||
$copy = null;
|
||
foreach ($integration->abilities() as $ability) {
|
||
if ($ability->name() === 'lit/divi-node-copy') {
|
||
$copy = $ability;
|
||
}
|
||
}
|
||
|
||
self::assertNotNull($copy);
|
||
self::assertToolError($copy->permission(['post_id' => 7, 'source_post_id' => 8]), 'unsupported');
|
||
}
|
||
|
||
public function testGivesContentCreateTheDiviShortForm(): void
|
||
{
|
||
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||
|
||
$composer = $integration->contentComposer();
|
||
|
||
self::assertInstanceOf(DiviContentComposer::class, $composer);
|
||
self::assertFalse($composer->available(), 'no Divi classes in unit tests');
|
||
}
|
||
|
||
/** E1/E3: the Divi read tools reach library entries; ruling P9: a hidden library leaves them not found. */
|
||
public function testTheReadToolsReachTheLibraryUnlessItIsHidden(): void
|
||
{
|
||
LibraryFixtures::given([LibraryFixtures::item(1055)]);
|
||
$schema = Mockery::mock(ContentSchema::class);
|
||
$schema->allows('isManaged')->andReturn(false);
|
||
$outline = static fn (DiviIntegration $divi): mixed => $divi->abilities()[0]->permission(['post_id' => 1055]);
|
||
|
||
// No Divi in unit tests: DiviRoles allows nothing, so the entry is not found either way (ruling P12);
|
||
// the wiring shows in which gate the integration asks.
|
||
$asked = 0;
|
||
$visible = new DiviIntegration($schema, FakeChangeLog::forbidden(), null, static function () use (&$asked): bool {
|
||
++$asked;
|
||
|
||
return true;
|
||
});
|
||
self::assertToolError($outline($visible), 'not_found');
|
||
self::assertSame(1, $asked);
|
||
self::assertToolError($outline(new DiviIntegration($schema, FakeChangeLog::forbidden())), 'not_found');
|
||
|
||
$hidden = new DiviIntegration($schema, FakeChangeLog::forbidden(), libraryVisible: static fn (): bool => false);
|
||
$refusal = $outline($hidden);
|
||
self::assertInstanceOf(WP_Error::class, $refusal);
|
||
self::assertToolError($refusal, 'not_found');
|
||
}
|
||
}
|