feat: structure and content fingerprints, lit/library-find-similar

Fingerprint::structure()/content() (E11) and lit/library-find-similar
(indirect, group Divi-Bibliothek): at most 200 posts per call (cursor),
editable entries only (own posts without edit_others_posts), contents
primed in chunks of 50, stops at 200 matches or 5000 compared nodes with
a note; library entries and Theme Builder layouts are never scanned.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 21:13:33 +02:00
co-authored by Claude Opus 5.5
parent 6be2e65432
commit f82964f617
7 changed files with 758 additions and 5 deletions
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\BlockTree;
use LIT\MCP\Integrations\Divi\BlockTree\Block;
use LIT\MCP\Integrations\Divi\BlockTree\Fingerprint;
use LIT\MCP\Integrations\Divi\BlockTree\LibraryContent;
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
use LIT\MCP\Tests\Support\DiviFixtures;
use PHPUnit\Framework\TestCase;
final class FingerprintTest extends TestCase
{
/** The section of library entry 1055: section > row > column (24_24) > blurb. */
private static function section(): Block
{
$content = DiviFixtures::markup('stage3/library--library-abschnitt.html');
return LibraryContent::roots(LibraryContent::load(new Parser(), $content))[0];
}
private static function blurbOf(Block $section): Block
{
return $section->children()[0]->children()[0]->children()[0];
}
public function testAdminLabelsAndBuilderVersionDoNotCount(): void
{
$a = self::section();
$b = $a->copy();
$attrs = $b->attrs();
$attrs['module']['meta']['adminLabel']['desktop']['value'] = 'Anderes Label';
$attrs['builderVersion'] = '5.3.0';
$b->setAttrs($attrs);
self::assertSame(16, strlen(Fingerprint::content($a)));
self::assertSame(Fingerprint::content($a), Fingerprint::content($b));
self::assertSame(Fingerprint::structure($a), Fingerprint::structure($b));
}
public function testOtherTextsKeepTheStructureButNotTheContent(): void
{
$a = self::section();
$b = $a->copy();
$blurb = self::blurbOf($b);
$attrs = $blurb->attrs();
$attrs['title']['innerContent']['desktop']['value'] = 'Anderer Titel';
$blurb->setAttrs($attrs);
self::assertSame(Fingerprint::structure($a), Fingerprint::structure($b));
self::assertNotSame(Fingerprint::content($a), Fingerprint::content($b));
}
public function testColumnLayoutAndModuleTypesChangeTheStructure(): void
{
$a = self::section();
$b = $a->copy();
$column = $b->children()[0]->children()[0];
$attrs = $column->attrs();
$attrs['module']['decoration']['sizing']['desktop']['value']['flexType'] = '12_24';
$column->setAttrs($attrs);
$c = Block::create('divi/section', [], [Block::create('divi/row', [], [Block::create('divi/column', [], [
Block::create('divi/text'),
])])]);
self::assertNotSame(Fingerprint::structure($a), Fingerprint::structure($b));
self::assertNotSame(Fingerprint::structure($a), Fingerprint::structure($c));
}
}