feat(compat): Profile, Gates und Pods über den Kompatibilitätskern
Neu: Tier, Profile, Compatibility, CapabilityLocked, CapabilityGate, CompatibilityComponent, ProvidesCompatibility, DiviProfile sowie PodsFeature, PodsCompatibility und PodsIntegration. Das CapabilityGate sperrt gesperrte und unbekannte Fähigkeits-IDs (übernimmt die Sperre unbekannter IDs vom DiviFeatureGate) und delegiert nur Divi ausdrücklich. Pods-Schema, -Lesen, -Schreiben und -Feldrechte laufen über usable(PodsFeature::…); Schreiben verlangt FieldRead und FieldWrite, damit ein Rollback keine Werte verliert. lit-site-status meldet Pods über die CompatibilityComponent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b511ef6348
commit
c85131603b
@@ -13,6 +13,7 @@ use LIT\MCP\Abilities\ToolAvailability;
|
||||
use LIT\MCP\Admin\AdminTab;
|
||||
use LIT\MCP\Integrations\Divi\Admin\DiviTab;
|
||||
use LIT\MCP\Admin\SettingsPage;
|
||||
use LIT\MCP\Compatibility\ProvidesCompatibility;
|
||||
use LIT\MCP\Integrations\Core\EnvironmentComponent;
|
||||
use LIT\MCP\Integrations\Divi\DiviBuilderDetector;
|
||||
use LIT\MCP\Integrations\Divi\DiviEnvironmentComponent;
|
||||
@@ -21,6 +22,7 @@ use LIT\MCP\Integrations\Divi\DiviLogoUsage;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviCheckRoute;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviProfile;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseResolver;
|
||||
use LIT\MCP\Integrations\Integration;
|
||||
use LIT\MCP\Integrations\IntegrationRegistry;
|
||||
@@ -121,6 +123,14 @@ final class DiviIntegrationTest extends TestCase
|
||||
self::assertFalse(DiviIntegration::builderEnabledFor('post'));
|
||||
}
|
||||
|
||||
public function testProvidesTheDiviCompatibilityProfile(): void
|
||||
{
|
||||
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
|
||||
|
||||
self::assertInstanceOf(ProvidesCompatibility::class, $integration);
|
||||
self::assertEquals([DiviProfile::profile()], $integration->compatibilities());
|
||||
}
|
||||
|
||||
public function testProvidesTheLibraryGroup(): void
|
||||
{
|
||||
Functions\when('__')->returnArg();
|
||||
|
||||
@@ -265,18 +265,18 @@ final class DiviFeatureGateTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testAnUnknownRequirementOfTheCoreLocksTheToolAndIsLogged(): void
|
||||
/** The lock of IDs that are no Divi capability is CapabilityGate's (unknown ones fail closed there). */
|
||||
public function testIdsThatAreNoDiviCapabilityAreIgnored(): void
|
||||
{
|
||||
$log = new FakeChangeLog();
|
||||
$tool = $this->contentCreate($this->composerNeeding('page-write', 'something-else'));
|
||||
DiviFeatureFixtures::useStates([
|
||||
'page-write' => ReleaseState::Incompatible,
|
||||
'catalog' => ReleaseState::Incompatible,
|
||||
]);
|
||||
$tool = $this->contentCreate($this->composerNeeding('pods-field-read', 'something-else'));
|
||||
|
||||
$error = $this->gate($log)->refusal($tool, ['post_type' => 'page', 'divi' => [[]]]);
|
||||
$payload = self::assertToolError($error, 'divi_incompatible');
|
||||
|
||||
self::assertSame(['something-else'], $payload['unknown']);
|
||||
self::assertSame(423, $error->get_error_data()['status'] ?? null);
|
||||
self::assertSame('lit/content-create', $log->records[0]['ability']);
|
||||
self::assertSame('refused', $log->records[0]['result']);
|
||||
self::assertNull(
|
||||
$this->gate(FakeChangeLog::forbidden())->refusal($tool, ['post_type' => 'page', 'divi' => [[]]])
|
||||
);
|
||||
}
|
||||
|
||||
public function testAnnotateNeverChangesAResult(): void
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release;
|
||||
|
||||
use Brain\Monkey\Functions;
|
||||
use LIT\MCP\Compatibility\ReleaseState;
|
||||
use LIT\MCP\Compatibility\Tier;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviAdapters;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviProfile;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
|
||||
use LIT\MCP\Tests\Support\ReleaseMatrices;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use Mockery;
|
||||
|
||||
final class DiviProfileTest extends TestCase
|
||||
{
|
||||
private function theme(string $template, string $version = '5.2.1'): void
|
||||
{
|
||||
$theme = Mockery::mock();
|
||||
$theme->allows('get_template')->andReturn($template);
|
||||
$theme->allows('parent')->andReturn(false);
|
||||
$theme->allows('get')->with('Version')->andReturn($version);
|
||||
Functions\when('wp_get_theme')->justReturn($theme);
|
||||
}
|
||||
|
||||
public function testDescribesDiviAsAReleasePerVersionPlugin(): void
|
||||
{
|
||||
$profile = DiviProfile::profile();
|
||||
|
||||
self::assertSame('divi', $profile->id);
|
||||
self::assertSame('Divi', $profile->label);
|
||||
self::assertSame(Tier::ReleasePerVersion, $profile->tier);
|
||||
self::assertSame(DiviFeature::cases(), $profile->capabilities);
|
||||
self::assertEquals(array_values(DiviAdapters::manifests()), $profile->manifests);
|
||||
}
|
||||
|
||||
public function testTheMatrixIsTheReleaseGatesMatrix(): void
|
||||
{
|
||||
$status = ReleaseMatrices::status([DiviFeature::PageWrite], ReleaseState::Incompatible);
|
||||
ReleaseGate::useResolver(static fn () => $status);
|
||||
|
||||
self::assertSame($status->matrix, (DiviProfile::profile()->matrix)());
|
||||
}
|
||||
|
||||
public function testTheVersionComesFromTheDiviTemplateAndThereIsNoActiveCheck(): void
|
||||
{
|
||||
$this->theme('Divi', '5.3.0');
|
||||
$profile = DiviProfile::profile();
|
||||
self::assertNull($profile->active, 'Divi has its own component; DiviIntegration decides availability');
|
||||
self::assertSame('5.3.0', ($profile->version)());
|
||||
|
||||
$this->theme('twentytwentyfive');
|
||||
self::assertNull(($profile->version)());
|
||||
}
|
||||
}
|
||||
@@ -119,20 +119,12 @@ final class FeatureSetsTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testUnknownIdsNamesTheIdsOfACoreToolThatAreNoDiviFeature(): void
|
||||
public function testDeclaredByIgnoresIdsThatAreNoDiviFeature(): void
|
||||
{
|
||||
$tool = Mockery::mock(Ability::class, DeclaresRequirements::class);
|
||||
$tool->allows('requirements')->with(['divi' => []])->andReturn(['page-write', 'other-integration', 'catalog']);
|
||||
$tool->allows('requirements')->andReturn(['pods-field-read', 'page-write']);
|
||||
|
||||
self::assertSame(['other-integration'], FeatureSets::unknownIds($tool, ['divi' => []]));
|
||||
}
|
||||
|
||||
public function testUnknownIdsIsEmptyForDiviToolsAndAnyOtherTool(): void
|
||||
{
|
||||
$divi = Mockery::mock(Ability::class, RequiresDiviFeatures::class);
|
||||
|
||||
self::assertSame([], FeatureSets::unknownIds($divi, []));
|
||||
self::assertSame([], FeatureSets::unknownIds(Mockery::mock(Ability::class), []));
|
||||
self::assertSame([DiviFeature::PageWrite], FeatureSets::declaredBy($tool, ['divi' => []]));
|
||||
}
|
||||
|
||||
public function testDeclaredByIsEmptyForAnyOtherTool(): void
|
||||
|
||||
Reference in New Issue
Block a user