Files
WP-LIT-MCP/tests/Unit/Integrations/Divi/Pages/DiviDeleteAbilityTest.php
T
janniclas.loosenandClaude Opus 5.5 7963aa19bd test: split DiviWriteAbilitiesTest per tool (T5)
One test class per Divi write tool on a shared DiviWriteAbilityTestCase;
the 30 tests move verbatim (per-test assertion counts identical). Adds the
ledger T4 test for a TreeEditor::move() StructureRules refusal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 18:27:59 +02:00

106 lines
5.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
use LIT\MCP\Abilities\Rev;
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
use LIT\MCP\Tests\Support\DiviFixtures;
final class DiviDeleteAbilityTest extends DiviWriteAbilityTestCase
{
/** The stored content equals the original minus exactly that block's bytes; revision_id is the restore point. */
public function testDeleteAModuleRemovesExactlyItsBytes(): void
{
$buttonBytes = '<!-- wp:divi/button {"button":{"innerContent":{"desktop":{"value":{"text":"Termin buchen",'
. '"linkUrl":"/kontakt","linkTarget":"off"}}}},"module":{"meta":{"adminLabel":{"desktop":{"value":"Hero –'
. ' Button"}}}},"builderVersion":"5.2.1"} --><!-- /wp:divi/button -->';
self::assertStringContainsString($buttonBytes, $this->original, 'precondition: exact bytes as expected');
$result = $this->delete()->execute([
'post_id' => 7, 'tree_rev' => $this->treeRev(), 'ids' => ['s0.r0.c1.m2'], 'confirm' => true,
]);
self::assertSame([['id' => 's0.r0.c1.m2', 'type' => 'divi/button', 'action' => 'deleted', 'label' => 'Hero – Button']], $result['changed']);
self::assertSame(str_replace($buttonBytes, '', $this->original), $this->saved[0]);
self::assertSame(811, $result['revision_id'], 'the restore-point revision');
self::assertStringContainsString('Hero', (string) $result['outline'], 'the outline is embedded in the result');
}
/** An opaque/global module is removed as a whole – it never gets descended into. */
public function testDeleteRemovesAnOpaqueGlobalModuleAsAWhole(): void
{
$mixed = DiviFixtures::markup('synthetic-mixed.html');
$this->post->post_content = $mixed;
$globalRow = '<!-- wp:divi/row {"globalModule":"913","module":{"meta":{"adminLabel":{"desktop":{"value":'
. '"Globale Zeile"}}}}} --><!-- wp:divi/column {"builderVersion":"5.2.1"} --><!-- wp:divi/text '
. '{"content":{"innerContent":{"desktop":{"value":"lokal"}}}} /--><!-- /wp:divi/column --><!-- /wp:divi/row'
. ' -->';
self::assertStringContainsString($globalRow, $mixed, 'precondition: exact bytes as expected');
$result = $this->delete()->execute([
'post_id' => 7, 'tree_rev' => Rev::of($mixed), 'ids' => ['s1.g1'], 'confirm' => true,
]);
self::assertSame([['id' => 's1.g1', 'type' => 'divi/row', 'action' => 'deleted', 'label' => 'Globale Zeile']], $result['changed']);
self::assertSame(str_replace($globalRow, '', $mixed), $this->saved[0]);
}
public function testDeletingTheLastChildLeavesAValidTree(): void
{
$mixed = DiviFixtures::markup('synthetic-mixed.html');
$this->post->post_content = $mixed;
$result = $this->delete()->execute([
'post_id' => 7, 'tree_rev' => Rev::of($mixed), 'ids' => ['s0.c1.r0.c0.m0'], 'confirm' => true,
]);
self::assertTrue($result['ok'], (string) json_encode($result));
$index = NodeIndex::build((new Parser())->parse($this->saved[0]));
$emptyColumn = $index->find('s0.c1.r0.c0');
self::assertNotNull($emptyColumn, 'the now-empty column-inner is still a valid, findable node');
self::assertSame([], $emptyColumn->children());
}
public function testDeleteUnknownIdReturnsNotFound(): void
{
$result = $this->delete()->execute([
'post_id' => 7, 'tree_rev' => $this->treeRev(), 'ids' => ['s9'], 'confirm' => true,
]);
self::assertToolError($result, 'not_found');
self::assertSame('not_found', self::clientError($result)['code']);
self::assertSame([], $this->saved);
}
/** Same annotations as the existing destructive tools (e.g. MediaDeleteAbility). */
public function testDeleteAnnotationsMatchExistingDestructiveTools(): void
{
self::assertSame(
['readonly' => false, 'destructive' => true, 'idempotent' => false],
$this->delete()->annotations()
);
}
/** Ruling S14 (Task 7): the deleted node's own confirm question, unaffected by other tests. */
public function testDeleteAsksBeforeRemovingNestedNodesOnlyOnce(): void
{
$duplicated = $this->duplicate()->execute(['post_id' => 7, 'tree_rev' => $this->treeRev(), 'id' => 's1']);
self::assertSame('s2', $duplicated['changed'][0]['id']);
$delete = $this->delete();
$ask = $delete->execute(['post_id' => 7, 'tree_rev' => Rev::of($this->saved[0]), 'ids' => ['s2', 's2.r0']]);
self::assertToolError($ask, 'confirm_required');
self::assertSame(
'1 Divi-Knoten würden samt Inhalt entfernt: s2 section "Leistungen (Kopie)" (1 Kind-Knoten).',
self::clientError($ask)['message']
);
$deleted = $delete->execute(['post_id' => 7, 'tree_rev' => Rev::of($this->saved[0]), 'ids' => ['s2'], 'confirm' => true]);
self::assertSame([['id' => 's2', 'type' => 'divi/section', 'action' => 'deleted', 'label' => 'Leistungen (Kopie)']], $deleted['changed']);
self::assertSame($this->original, $this->saved[1], 'deleting the copy restores the exact bytes');
}
}