feat: Divi-Adapter sperren je Fähigkeit – ReleaseGate::feature/assertFeature(s), DiviLocked und Ablehnung nennen die betroffenen Fähigkeiten (D6), DiviSaver prüft Seiten schreiben und die Fähigkeit des Speicherbereichs (D5), DesignArea::feature; Test-Standard alle Fähigkeiten freigegeben

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-18 22:10:41 +02:00
co-authored by Claude Opus 5.5
parent c4efeb3ba4
commit e387d8d29e
20 changed files with 525 additions and 65 deletions
@@ -5,11 +5,13 @@ declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release;
use LIT\MCP\Abilities\WriteFailed;
use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix;
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\Support\ReleaseMatrices;
use LIT\MCP\Tests\Unit\TestCase;
use RuntimeException;
@@ -20,10 +22,78 @@ final class ReleaseGateTest extends TestCase
ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(ReleaseState::Untested, '5.3.0'));
}
public function testTheTestCaseDefaultIsReleased(): void
public function testTheTestCaseDefaultReleasesEveryFeature(): void
{
self::assertTrue(ReleaseGate::status()->writable());
self::assertSame([], ReleaseGate::status()->matrix->unusable());
ReleaseGate::assertWritable(7);
ReleaseGate::assertFeatures(DiviFeature::cases());
}
public function testFeatureReadsTheMatrixOfThisRequest(): void
{
ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status([DiviFeature::Catalog]));
self::assertSame(ReleaseState::Untested, ReleaseGate::feature(DiviFeature::Catalog)->state);
self::assertTrue(ReleaseGate::feature(DiviFeature::PageRead)->usable());
}
public function testAssertFeatureRefusesOnlyALockedFeature(): void
{
ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status(
[DiviFeature::PresetWrite],
ReleaseState::Incompatible
));
ReleaseGate::assertFeature(DiviFeature::PageWrite);
try {
ReleaseGate::assertFeature(DiviFeature::PresetWrite, 7);
self::fail('a locked feature must refuse');
} catch (DiviLocked $locked) {
self::assertSame([DiviFeature::PresetWrite], $locked->features);
self::assertToolError($locked->toError(), 'divi_incompatible');
}
}
public function testAssertFeaturesNamesEveryLockedFeatureOnce(): void
{
ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status(
[DiviFeature::LibraryWrite, DiviFeature::ThemeBuilderWrite]
));
try {
ReleaseGate::assertFeatures([
DiviFeature::PageWrite,
DiviFeature::ThemeBuilderWrite,
DiviFeature::LibraryWrite,
DiviFeature::ThemeBuilderWrite,
]);
self::fail('locked features must refuse');
} catch (DiviLocked $locked) {
self::assertSame([DiviFeature::ThemeBuilderWrite, DiviFeature::LibraryWrite], $locked->features);
}
}
public function testAssertWritableNeedsEveryWritingFeature(): void
{
ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status([DiviFeature::Catalog]));
ReleaseGate::assertWritable();
ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status([DiviFeature::LibraryWrite]));
try {
ReleaseGate::assertWritable();
self::fail('a locked writing feature must refuse');
} catch (DiviLocked $locked) {
self::assertSame([DiviFeature::LibraryWrite], $locked->features);
}
ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(ReleaseState::Untested, '5.3.0'));
try {
ReleaseGate::assertWritable();
self::fail('an untested Divi must lock writes');
} catch (DiviLocked $locked) {
self::assertSame(CompatibilityMatrix::writeFeatures(), $locked->features);
}
}
public function testAssertWritableThrowsTheLockWithTheStatus(): void
@@ -45,6 +115,7 @@ final class ReleaseGateTest extends TestCase
$result = ReleaseGate::whileProbing(41, static function (): string {
ReleaseGate::assertWritable(41);
ReleaseGate::assertFeatures(DiviFeature::cases(), 41);
return 'written';
});
@@ -52,6 +123,10 @@ final class ReleaseGateTest extends TestCase
foreach ([static fn () => ReleaseGate::whileProbing(41, static fn () => ReleaseGate::assertWritable(42)),
static fn () => ReleaseGate::whileProbing(41, static fn () => ReleaseGate::assertWritable()),
static fn () => ReleaseGate::whileProbing(41, static fn () => ReleaseGate::assertFeature(
DiviFeature::PageWrite,
42
)),
static fn () => ReleaseGate::assertWritable(41)] as $attempt) {
try {
$attempt();
@@ -118,7 +193,7 @@ final class ReleaseGateTest extends TestCase
self::assertStringContainsString('theme API gone', (string) file_get_contents($log));
unlink($log);
$this->expectException(DiviLocked::class);
ReleaseGate::assertWritable();
ReleaseGate::assertFeature(DiviFeature::PageRead);
}
public function testFindsTheLockInsideAWrappedFailure(): void