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
78 lines
3.4 KiB
PHP
78 lines
3.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
|
||
|
||
use LIT\MCP\Abilities\Rev;
|
||
use LIT\MCP\Tests\Support\DiviFixtures;
|
||
|
||
final class DiviMoveAbilityTest extends DiviWriteAbilityTestCase
|
||
{
|
||
/** Ruling S14: move there and back restores the exact bytes (full-content assertSame, no strlen). */
|
||
public function testMoveThereAndBackIsByteExact(): void
|
||
{
|
||
$moved = $this->move()->execute([
|
||
'post_id' => 7, 'tree_rev' => $this->treeRev(), 'id' => 's0.r0.c1.m2', 'parent' => 's1.r0.c0',
|
||
]);
|
||
self::assertSame([['id' => 's1.r0.c0.m1', 'type' => 'divi/button', 'action' => 'moved', 'label' => 'Hero – Button']], $moved['changed']);
|
||
self::assertStringContainsString('Leistungen', (string) $moved['outline'], 'the outline is embedded in the result');
|
||
|
||
$back = $this->move()->execute([
|
||
'post_id' => 7, 'tree_rev' => Rev::of($this->saved[0]), 'id' => 's1.r0.c0.m1', 'parent' => 's0.r0.c1',
|
||
]);
|
||
self::assertSame([['id' => 's0.r0.c1.m2', 'type' => 'divi/button', 'action' => 'moved', 'label' => 'Hero – Button']], $back['changed']);
|
||
self::assertSame($this->original, $this->saved[1], 'move there and back restores the exact bytes (S14)');
|
||
}
|
||
|
||
/** StructureRules apply on move: a module directly into a section, or into a specialty column. */
|
||
public function testMoveRefusedByStructureRules(): void
|
||
{
|
||
$mixed = DiviFixtures::markup('synthetic-mixed.html');
|
||
$this->post->post_content = $mixed;
|
||
$treeRev = Rev::of($mixed);
|
||
|
||
// s1 is a regular section: it holds rows, not modules directly.
|
||
$intoSection = $this->move()->execute([
|
||
'post_id' => 7, 'tree_rev' => $treeRev, 'id' => 's0.c0.m0', 'parent' => 's1',
|
||
]);
|
||
self::assertToolError($intoSection, 'validation_failed');
|
||
self::assertStringContainsString('Zeilen (row)', self::clientError($intoSection)['fields']['position']);
|
||
|
||
// s0.c1 is a specialty column (specialtyColumns set, holds a row-inner): refuses every child.
|
||
$intoSpecialtyColumn = $this->move()->execute([
|
||
'post_id' => 7, 'tree_rev' => $treeRev, 'id' => 's0.c0.m0', 'parent' => 's0.c1',
|
||
]);
|
||
self::assertToolError($intoSpecialtyColumn, 'validation_failed');
|
||
self::assertStringContainsString('Spezialspalten', self::clientError($intoSpecialtyColumn)['fields']['position']);
|
||
|
||
self::assertSame([], $this->saved);
|
||
self::assertSame($mixed, $this->post->post_content);
|
||
}
|
||
|
||
public function testMoveIntoItsOwnDescendantIsRefused(): void
|
||
{
|
||
$result = $this->move()->execute([
|
||
'post_id' => 7, 'tree_rev' => $this->treeRev(), 'id' => 's0', 'parent' => 's0.r0.c1',
|
||
]);
|
||
|
||
self::assertToolError($result, 'validation_failed');
|
||
self::assertStringContainsString(
|
||
'nicht in sich selbst verschoben',
|
||
self::clientError($result)['fields']['position']
|
||
);
|
||
self::assertSame([], $this->saved);
|
||
}
|
||
|
||
public function testMoveRefusesAStaleTreeRev(): void
|
||
{
|
||
$result = $this->move()->execute([
|
||
'post_id' => 7, 'tree_rev' => '000000000000', 'id' => 's0.r0.c1.m2', 'parent' => 's1.r0.c0',
|
||
]);
|
||
|
||
self::assertToolError($result, 'rev_mismatch');
|
||
self::assertSame('rev_mismatch', self::clientError($result)['code'], 'the client sees the code without lit_');
|
||
self::assertSame([], $this->saved);
|
||
}
|
||
}
|