Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
4.3 KiB
PHP
133 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release;
|
|
|
|
use LIT\MCP\Abilities\WriteFailed;
|
|
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
|
use LIT\MCP\Integrations\Divi\Release\DiviLocked;
|
|
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
|
|
use LIT\MCP\Integrations\Divi\Release\ReleaseState;
|
|
use LIT\MCP\Integrations\Divi\Release\ReleaseStatus;
|
|
use LIT\MCP\Tests\Unit\TestCase;
|
|
use RuntimeException;
|
|
|
|
final class ReleaseGateTest extends TestCase
|
|
{
|
|
private function untested(): void
|
|
{
|
|
ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(ReleaseState::Untested, '5.3.0'));
|
|
}
|
|
|
|
public function testTheTestCaseDefaultIsReleased(): void
|
|
{
|
|
self::assertTrue(ReleaseGate::status()->writable());
|
|
ReleaseGate::assertWritable(7);
|
|
}
|
|
|
|
public function testAssertWritableThrowsTheLockWithTheStatus(): void
|
|
{
|
|
$this->untested();
|
|
|
|
try {
|
|
ReleaseGate::assertWritable(7);
|
|
self::fail('an untested Divi must lock writes');
|
|
} catch (DiviLocked $locked) {
|
|
self::assertSame('5.3.0', $locked->status->version);
|
|
self::assertToolError($locked->toError(), 'divi_untested');
|
|
}
|
|
}
|
|
|
|
public function testProbingAllowsOnlyThatPostAndOnlyMeanwhile(): void
|
|
{
|
|
$this->untested();
|
|
|
|
$result = ReleaseGate::whileProbing(41, static function (): string {
|
|
ReleaseGate::assertWritable(41);
|
|
|
|
return 'written';
|
|
});
|
|
self::assertSame('written', $result);
|
|
|
|
foreach ([static fn () => ReleaseGate::whileProbing(41, static fn () => ReleaseGate::assertWritable(42)),
|
|
static fn () => ReleaseGate::whileProbing(41, static fn () => ReleaseGate::assertWritable()),
|
|
static fn () => ReleaseGate::assertWritable(41)] as $attempt) {
|
|
try {
|
|
$attempt();
|
|
self::fail('only post 41, only while probing');
|
|
} catch (DiviLocked) {
|
|
self::addToAssertionCount(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function testProbingEndsEvenWhenTheWriteThrows(): void
|
|
{
|
|
$this->untested();
|
|
|
|
try {
|
|
ReleaseGate::whileProbing(41, static function (): never {
|
|
throw new RuntimeException('boom');
|
|
});
|
|
} catch (RuntimeException) {
|
|
}
|
|
|
|
$this->expectException(DiviLocked::class);
|
|
ReleaseGate::assertWritable(41);
|
|
}
|
|
|
|
public function testStatusIsComputedOncePerRequestUntilForgotten(): void
|
|
{
|
|
$calls = 0;
|
|
ReleaseGate::useResolver(static function () use (&$calls): ReleaseStatus {
|
|
++$calls;
|
|
|
|
return ReleaseStatus::released('5.2.1');
|
|
});
|
|
|
|
ReleaseGate::status();
|
|
ReleaseGate::status();
|
|
self::assertSame(1, $calls);
|
|
|
|
ReleaseGate::forget();
|
|
ReleaseGate::status();
|
|
self::assertSame(2, $calls);
|
|
}
|
|
|
|
public function testAFailingResolverLocksWrites(): void
|
|
{
|
|
ReleaseGate::useResolver(static function (): never {
|
|
throw new RuntimeException('theme API gone');
|
|
});
|
|
$log = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
|
$previous = ini_set('error_log', $log);
|
|
|
|
try {
|
|
$status = ReleaseGate::status();
|
|
} finally {
|
|
ini_set('error_log', (string) $previous);
|
|
}
|
|
|
|
self::assertSame(ReleaseState::Incompatible, $status->state);
|
|
self::assertCount(count(DiviFeature::cases()), $status->matrix->unusable(), 'every feature is locked');
|
|
self::assertSame(
|
|
['Der Freigabe-Zustand ließ sich nicht ermitteln (Details im PHP-Error-Log).'],
|
|
$status->matrix->of(DiviFeature::PageRead)->problems
|
|
);
|
|
self::assertStringContainsString('theme API gone', (string) file_get_contents($log));
|
|
unlink($log);
|
|
$this->expectException(DiviLocked::class);
|
|
ReleaseGate::assertWritable();
|
|
}
|
|
|
|
public function testFindsTheLockInsideAWrappedFailure(): void
|
|
{
|
|
$locked = new DiviLocked(new ReleaseStatus(ReleaseState::Untested, '5.3.0'));
|
|
$wrapped = WriteFailed::rolledBack('Nicht gespeichert.', new RuntimeException('draft', 0, $locked));
|
|
|
|
self::assertSame($locked, DiviLocked::in($wrapped));
|
|
self::assertNull(DiviLocked::in(new RuntimeException('other')));
|
|
}
|
|
}
|