Files
WP-LIT-MCP/tests/Unit/Cli/DiviCheckCommandTest.php
T

222 lines
8.7 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\Cli;
use Brain\Monkey\Functions;
use LIT\MCP\Cli\DiviCheckCommand;
use LIT\MCP\Integrations\Divi\Release\CheckReport;
use LIT\MCP\Integrations\Divi\Release\CheckReportStore;
use LIT\MCP\Integrations\Divi\Release\CheckStep;
use LIT\MCP\Integrations\Divi\Release\CompatibilityCheck;
use LIT\MCP\Integrations\Divi\Release\DiviReference;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
final class DiviCheckCommandTest extends TestCase
{
/** @var list<string> */
private array $lines = [];
private string $themeDir = '';
private string $sourceDir = '';
private string $expectedFile = '';
protected function setUp(): void
{
parent::setUp();
$this->lines = [];
$base = sys_get_temp_dir() . '/lit-cli-' . uniqid();
$this->themeDir = $base . '/Divi';
$this->sourceDir = $base . '/reference';
mkdir($this->themeDir . '/includes/builder', 0777, true);
mkdir($this->sourceDir, 0777, true);
$this->expectedFile = $base . '/fingerprints.json';
Functions\when('current_user_can')->justReturn(true);
Functions\when('get_current_user_id')->justReturn(1);
Functions\when('delete_transient')->justReturn(true);
}
protected function tearDown(): void
{
exec('rm -rf ' . escapeshellarg(dirname($this->themeDir)));
parent::tearDown();
}
/**
* @param array<string, list<string>> $problems Step key => problems.
* @param bool $expectSave True asserts save() is called exactly once (the green happy path); otherwise it is
* merely allowed, since some tests (e.g. no admin) never reach the save() call.
*/
private function command(
array $problems = [],
bool $expectSave = false,
bool $noSourceDir = false
): DiviCheckCommand {
$steps = array_map(
static fn (string $key): CheckStep => new CheckStep($key, $problems[$key] ?? []),
[CheckStep::MANIFEST, CheckStep::MIRRORS, CheckStep::CATALOG, CheckStep::FORMAT, CheckStep::WRITE]
);
$check = Mockery::mock(CompatibilityCheck::class);
$check->allows('run')->andReturn(new CheckReport('r1', 'd', 1, '5.3.0', '0.6.0', 'k', $steps));
$reports = Mockery::mock(CheckReportStore::class);
if ($expectSave) {
$reports->expects('save')->once();
} else {
$reports->allows('save');
}
return new DiviCheckCommand(
static fn () => $check,
$reports,
function (string $line): void {
$this->lines[] = $line;
},
fn (): ?string => $this->themeDir,
$this->expectedFile,
$noSourceDir ? static fn (): ?string => null : fn (): ?string => $this->sourceDir
);
}
public function testNeedsAnAdministrator(): void
{
Functions\when('current_user_can')->justReturn(false);
self::assertSame(1, $this->command()->run([]));
self::assertStringContainsString('--user=', implode("\n", $this->lines));
}
public function testPrintsTheStepsAndSucceedsWhenGreen(): void
{
self::assertSame(0, $this->command(expectSave: true)->run([]));
self::assertContains('✓ Schreibtest auf einer Wegwerfseite', $this->lines);
}
public function testFailsWhenAStepIsRed(): void
{
self::assertSame(1, $this->command([CheckStep::CATALOG => ['divi/text: unbekanntes Modul']])->run([]));
self::assertContains('✗ Modulkatalog (Kurzform-Pfade)', $this->lines);
self::assertContains(' divi/text: unbekanntes Modul', $this->lines);
}
public function testUpdateReferenceRefusesWhenMoreThanFingerprintsIsRed(): void
{
$code = $this->command([CheckStep::FORMAT => ['kaputt']])->run(['update-reference' => true]);
self::assertSame(1, $code);
self::assertFileDoesNotExist($this->expectedFile);
}
public function testUpdateReferenceRefusesWithoutADevelopmentSourceDir(): void
{
$code = $this->command(noSourceDir: true)->run(['update-reference' => true]);
self::assertSame(1, $code);
self::assertStringContainsString(
'Referenz nicht aktualisiert: nur in einer Entwicklungskopie mit tests/ möglich.',
implode("\n", $this->lines)
);
self::assertFileDoesNotExist($this->expectedFile);
}
public function testDiffShowsWhatChangedInAMirroredFunction(): void
{
file_put_contents(
$this->themeDir . '/includes/builder/shortcode-core.php',
"<?php\nfunction et_pb_submit_layout( \$args ) {\n\t\$a = 1;\n\treturn 2;\n}\n"
);
DiviReference::writeExpected(['library-submit-layout' => str_repeat('0', 64)], $this->expectedFile);
DiviReference::writeSource(
'library-submit-layout',
"function et_pb_submit_layout( \$args ) {\n\t\$a = 1;\n\treturn 1;\n}",
$this->sourceDir
);
$this->command([CheckStep::MIRRORS => ['Bibliothek: Nachbau von et_pb_submit_layout() – Terme']])
->run(['diff' => true]);
$output = implode("\n", $this->lines);
self::assertStringContainsString('library-submit-layout', $output);
self::assertStringContainsString("- \treturn 1;", $output);
self::assertStringContainsString("+ \treturn 2;", $output);
}
public function testUpdateReferenceWritesNothingWhenAMirrorIsUnreadable(): void
{
file_put_contents(
$this->themeDir . '/includes/builder/shortcode-core.php',
"<?php\nfunction et_pb_submit_layout( \$args ) {\n\treturn 2;\n}\n"
);
$code = $this->command([CheckStep::MIRRORS => ['geändert']])->run(['update-reference' => true]);
// The other mirrored Divi files are missing in the temp theme.
self::assertSame(1, $code);
self::assertStringContainsString('nicht lesbar', implode("\n", $this->lines));
self::assertFileDoesNotExist($this->expectedFile, 'all or nothing');
}
public function testUpdateReferenceWritesNothingWhenASourceIsUnwritable(): void
{
$this->writeDivi();
chmod($this->sourceDir, 0500);
try {
$code = $this->command([CheckStep::MIRRORS => ['geändert']])->run(['update-reference' => true]);
self::assertSame(1, $code);
self::assertStringContainsString(
'Referenz nicht vollständig aktualisiert:',
implode("\n", $this->lines)
);
self::assertFileDoesNotExist($this->expectedFile, 'sources are written before fingerprints.json');
} finally {
chmod($this->sourceDir, 0777);
}
}
public function testUpdateReferenceWritesFingerprintsAndSources(): void
{
$this->writeDivi();
$code = $this->command([CheckStep::MIRRORS => ['geändert']])->run(['update-reference' => true]);
self::assertSame(0, $code);
self::assertSame(
[
'library-submit-layout', 'preset-sync', 'tb-api-save', 'tb-load-setting-options', 'tb-store-template',
'vb-sync-flags',
],
array_keys(DiviReference::expected($this->expectedFile))
);
self::assertStringStartsWith(
'function et_pb_submit_layout(',
(string) DiviReference::readSource('library-submit-layout', $this->sourceDir)
);
}
/** Minimal stand-ins for the six mirrored Divi functions at their real paths. */
private function writeDivi(): void
{
$files = [
'includes/builder/shortcode-core.php' => "function et_pb_submit_layout( \$args ) {\n\treturn 1;\n}",
'includes/builder/frontend-builder/theme-builder/api.php' => "function et_theme_builder_api_save() {\n}",
'includes/builder/frontend-builder/theme-builder/theme-builder.php' =>
"function et_theme_builder_store_template( \$a, \$b, \$c ) {\n}\n"
. "function et_theme_builder_load_template_setting_options( \$ids ) {\n}",
'includes/builder-5/server/VisualBuilder/REST/SyncToServer/SyncToServerController.php' =>
"class SyncToServerController {\n\tpublic static function update( \$request ) {\n\t}\n}",
'includes/builder-5/server/Packages/GlobalData/GlobalPresetController.php' =>
"class GlobalPresetController {\n\tpublic static function sync( \$request ) {\n\t}\n}",
];
foreach ($files as $path => $code) {
$file = $this->themeDir . '/' . $path;
if (! is_dir(dirname($file))) {
mkdir(dirname($file), 0777, true);
}
file_put_contents($file, "<?php\n" . $code . "\n");
}
}
}