feat: manifest checks, Divi compatibility from manifests, reference fingerprints

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-30 01:37:51 +02:00
co-authored by Claude Opus 5.5
parent 1a215fd7bf
commit 6b93df94c3
15 changed files with 1295 additions and 97 deletions
@@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release;
use ArrayObject;
use Brain\Monkey\Functions;
use LIT\MCP\Integrations\Divi\Release\Call;
use LIT\MCP\Integrations\Divi\Release\Manifest;
use LIT\MCP\Integrations\Divi\Release\ManifestCheck;
use LIT\MCP\Integrations\Divi\Release\Mirror;
use LIT\MCP\Tests\Unit\TestCase;
use RuntimeException;
final class ManifestCheckTest extends TestCase
{
private function check(?\Closure $current = null): ManifestCheck
{
$manifests = [
'A' => new Manifest(
Manifest::PAGES,
calls: [
new Call('strlen', 1),
new Call('lit_mcp_missing_function', 0),
new Call(ArrayObject::class . '::count', 0),
new Call('Missing\Klass::run', 0),
new Call('str_replace'),
],
constants: ['PHP_VERSION', 'LIT_MCP_MISSING_CONSTANT'],
postTypes: ['et_pb_layout'],
taxonomies: ['scope'],
blockTypes: ['divi/global-layout'],
mirrors: [
new Mirror('same', 'a.php', 'a', null, 'gleich'),
new Mirror('changed', 'b.php', 'b', null, 'geändert'),
new Mirror('unreadable', 'c.php', 'c', null, 'fehlt'),
new Mirror('no-expectation', 'd.php', 'd', null, 'ohne Soll'),
]
),
];
$currentHashes = ['same' => 'h1', 'changed' => 'hX', 'no-expectation' => 'h4'];
return new ManifestCheck(
$manifests,
['same' => 'h1', 'changed' => 'h2', 'unreadable' => 'h3'],
$current ?? static fn (Mirror $mirror): ?string => $currentHashes[$mirror->id] ?? null,
static fn (string $name): bool => false
);
}
public function testCodeChecksCoverExistenceAndArgumentCount(): void
{
self::assertSame(
[
'Seiten: strlen()',
'Seiten: lit_mcp_missing_function()',
'Seiten: ArrayObject::count()',
'Seiten: Klass::run()',
'Seiten: str_replace()',
'Seiten: Konstante PHP_VERSION',
'Seiten: Konstante LIT_MCP_MISSING_CONSTANT',
],
array_keys($this->check()->codeChecks())
);
self::assertSame(
[
'Seiten: lit_mcp_missing_function()',
'Seiten: Klass::run()',
'Seiten: Konstante LIT_MCP_MISSING_CONSTANT',
],
ManifestCheck::failed($this->check()->codeChecks())
);
}
public function testAcceptsChecksTheArgumentCountTheAdapterPasses(): void
{
self::assertTrue(ManifestCheck::accepts(new Call('strlen', 1)));
self::assertFalse(ManifestCheck::accepts(new Call('strlen', 2)), 'strlen takes one argument');
self::assertFalse(ManifestCheck::accepts(new Call('str_replace', 2)), 'str_replace needs three');
self::assertTrue(ManifestCheck::accepts(new Call('sprintf', 5)), 'variadic');
self::assertTrue(ManifestCheck::accepts(new Call('str_replace')), 'existence only');
}
public function testMirrorChecksCompareWithTheExpectedFingerprints(): void
{
$checks = $this->check()->mirrorChecks();
self::assertSame(
[
'Seiten: Nachbau von a() – gleich',
'Seiten: Nachbau von b() – geändert',
'Seiten: Nachbau von c() – fehlt',
'Seiten: Nachbau von d() – ohne Soll',
],
array_keys($checks)
);
self::assertSame(
[
'Seiten: Nachbau von b() – geändert',
'Seiten: Nachbau von c() – fehlt',
'Seiten: Nachbau von d() – ohne Soll',
],
ManifestCheck::failed($checks)
);
}
public function testUnreadableMirrorIsAFailedCheck(): void
{
$check = $this->check(static function (): ?string {
throw new RuntimeException('Divi file gone');
});
self::assertCount(4, ManifestCheck::failed($check->mirrorChecks()));
}
public function testRegistrationChecksAskWordPressAndDivi(): void
{
Functions\when('post_type_exists')->justReturn(true);
Functions\when('taxonomy_exists')->justReturn(false);
$checks = $this->check()->registrationChecks();
self::assertSame(
['Seiten: Post-Typ et_pb_layout', 'Seiten: Taxonomie scope', 'Seiten: Block-Typ divi/global-layout'],
array_keys($checks)
);
self::assertSame(
['Seiten: Taxonomie scope', 'Seiten: Block-Typ divi/global-layout'],
ManifestCheck::failed($checks)
);
}
}