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:
co-authored by
Claude Opus 5.5
parent
c4efeb3ba4
commit
e387d8d29e
@@ -5,10 +5,13 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release;
|
||||
|
||||
use Brain\Monkey\Functions;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
|
||||
use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore;
|
||||
use LIT\MCP\Integrations\Divi\Library\LibraryItemSpec;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DiviPages;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DiviSaver;
|
||||
use LIT\MCP\Integrations\Divi\Pages\SaveScope;
|
||||
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;
|
||||
@@ -16,19 +19,26 @@ use LIT\MCP\Integrations\Divi\Release\ReleaseStatus;
|
||||
use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder;
|
||||
use LIT\MCP\Integrations\WordPress\Content\NewDraft;
|
||||
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||
use LIT\MCP\Tests\Support\ReleaseMatrices;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use WP_Post;
|
||||
|
||||
/** Spec §5.5: every writing adapter method refuses before its first write while Divi is not released. */
|
||||
/**
|
||||
* Spec §5.5, D5: every writing adapter method refuses before its first write exactly when a feature it needs is
|
||||
* locked – and passes the gate when only other features are locked. (DesignChange: DesignChangeTest.)
|
||||
*/
|
||||
final class ReleaseGuardsTest extends TestCase
|
||||
{
|
||||
private const WRITES = ['wp_update_post', 'wp_insert_post', 'update_post_meta', 'add_post_meta', 'delete_post_meta',
|
||||
'update_option', 'delete_option', 'wp_set_object_terms'];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(ReleaseState::Untested, '5.3.0'));
|
||||
// Nothing may be written: every write function WordPress offers fails the test.
|
||||
foreach (['wp_update_post', 'wp_insert_post', 'update_post_meta', 'add_post_meta', 'delete_post_meta',
|
||||
'update_option', 'delete_option', 'wp_set_object_terms'] as $write) {
|
||||
// Nothing may be written while locked: every write function WordPress offers fails the test.
|
||||
foreach (self::WRITES as $write) {
|
||||
Functions\when($write)->alias(static function () use ($write): never {
|
||||
self::fail($write . ' ran although Divi is locked');
|
||||
});
|
||||
@@ -36,41 +46,106 @@ final class ReleaseGuardsTest extends TestCase
|
||||
Functions\when('current_user_can')->justReturn(true);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{callable(): mixed}> */
|
||||
/** @return iterable<string, array{callable(): mixed, list<DiviFeature>}> */
|
||||
public static function writes(): iterable
|
||||
{
|
||||
$post = new WP_Post(['ID' => 7, 'post_content' => '', 'post_type' => 'page']);
|
||||
$spec = new LibraryItemSpec('Hero', 'section', false, 'regular', null, 'page');
|
||||
$page = [DiviFeature::PageWrite];
|
||||
$tb = [DiviFeature::ThemeBuilderWrite];
|
||||
|
||||
yield 'save' => [static fn () => (new DiviSaver())->save($post, '<!-- wp:divi/section /-->')];
|
||||
yield 'saveNew' => [static fn () => (new DiviSaver())->saveNew(7, '<!-- wp:divi/section /-->')];
|
||||
yield 'save' => [static fn () => (new DiviSaver())->save($post, '<!-- wp:divi/section /-->'), $page];
|
||||
yield 'save library item' => [
|
||||
static fn () => (new DiviSaver())->save($post, 'x', SaveScope::LibraryItem),
|
||||
[DiviFeature::PageWrite, DiviFeature::LibraryWrite],
|
||||
];
|
||||
yield 'save global item' => [
|
||||
static fn () => (new DiviSaver())->save($post, 'x', SaveScope::GlobalItem),
|
||||
[DiviFeature::PageWrite, DiviFeature::LibraryWrite],
|
||||
];
|
||||
yield 'save tb layout' => [
|
||||
static fn () => (new DiviSaver())->save($post, 'x', SaveScope::ThemeBuilderLayout),
|
||||
[DiviFeature::PageWrite, DiviFeature::ThemeBuilderWrite],
|
||||
];
|
||||
yield 'saveNew' => [static fn () => (new DiviSaver())->saveNew(7, '<!-- wp:divi/section /-->'), $page];
|
||||
yield 'saveNew library item' => [
|
||||
static fn () => (new DiviSaver())->saveNew(7, 'x', SaveScope::LibraryItem),
|
||||
[DiviFeature::PageWrite, DiviFeature::LibraryWrite],
|
||||
];
|
||||
yield 'pages setFlags' => [static fn () => DiviPages::setFlags(7), $page];
|
||||
yield 'library create' => [static fn () => (new DiviLibraryStore())->create(
|
||||
$spec,
|
||||
new NewDraft(null, FakeChangeLog::forbidden(), 'lit/library-save', 'et_pb_layout', 'Bibliothekseintrag')
|
||||
)];
|
||||
yield 'tb insertLayout' => [static fn () => (new DiviThemeBuilder())->insertLayout('header', 'Kopf')];
|
||||
), [DiviFeature::LibraryWrite]];
|
||||
yield 'tb liveId create' => [static fn () => (new DiviThemeBuilder())->liveId(true), $tb];
|
||||
yield 'tb insertLayout' => [static fn () => (new DiviThemeBuilder())->insertLayout('header', 'Kopf'), $tb];
|
||||
yield 'tb storeTemplate' => [static fn () => (new DiviThemeBuilder())->storeTemplate(3, [
|
||||
'title' => 'V', 'layouts' => [], 'use_on' => [], 'exclude_from' => [],
|
||||
])];
|
||||
yield 'tb attach' => [static fn () => (new DiviThemeBuilder())->attach(3, 9)];
|
||||
yield 'tb detach' => [static fn () => (new DiviThemeBuilder())->detach(3, 9)];
|
||||
yield 'tb setConditions' => [static fn () => (new DiviThemeBuilder())->setConditions(9, ['singular'], [])];
|
||||
yield 'tb setEnabled' => [static fn () => (new DiviThemeBuilder())->setEnabled(9, true)];
|
||||
yield 'tb unmarkUnused' => [static fn () => (new DiviThemeBuilder())->unmarkUnused(9)];
|
||||
yield 'pages setFlags' => [static fn () => DiviPages::setFlags(7)];
|
||||
yield 'tb liveId create' => [static fn () => (new DiviThemeBuilder())->liveId(true)];
|
||||
]), $tb];
|
||||
yield 'tb attach' => [static fn () => (new DiviThemeBuilder())->attach(3, 9), $tb];
|
||||
yield 'tb detach' => [static fn () => (new DiviThemeBuilder())->detach(3, 9), $tb];
|
||||
yield 'tb setConditions' => [static fn () => (new DiviThemeBuilder())->setConditions(9, ['singular'], []), $tb];
|
||||
yield 'tb setEnabled' => [static fn () => (new DiviThemeBuilder())->setEnabled(9, true), $tb];
|
||||
yield 'tb unmarkUnused' => [static fn () => (new DiviThemeBuilder())->unmarkUnused(9), $tb];
|
||||
yield 'design savePresets' => [
|
||||
static fn () => (new DesignSystemWriter())->savePresets(['module' => [], 'group' => []], false),
|
||||
[DiviFeature::PresetWrite],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider writes */
|
||||
public function testRefusesBeforeTheFirstWrite(callable $write): void
|
||||
/**
|
||||
* @dataProvider writes
|
||||
* @param list<DiviFeature> $needs
|
||||
*/
|
||||
public function testRefusesBeforeTheFirstWriteWhenAFeatureItNeedsIsLocked(callable $write, array $needs): void
|
||||
{
|
||||
$this->expectException(DiviLocked::class);
|
||||
foreach ([ReleaseState::Untested, ReleaseState::Incompatible] as $state) {
|
||||
foreach ($needs as $feature) {
|
||||
ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status([$feature], $state));
|
||||
|
||||
$write();
|
||||
try {
|
||||
$write();
|
||||
self::fail(sprintf('%s %s must refuse', $feature->value, $state->value));
|
||||
} catch (DiviLocked $locked) {
|
||||
self::assertSame([$feature], $locked->features);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider writes
|
||||
* @param list<DiviFeature> $needs
|
||||
*/
|
||||
public function testPassesTheGateWhenOnlyOtherFeaturesAreLocked(callable $write, array $needs): void
|
||||
{
|
||||
$others = array_values(array_filter(
|
||||
DiviFeature::cases(),
|
||||
static fn (DiviFeature $feature): bool => ! in_array($feature, $needs, true)
|
||||
));
|
||||
ReleaseGate::useResolver(
|
||||
static fn (): ReleaseStatus => ReleaseMatrices::status($others, ReleaseState::Incompatible)
|
||||
);
|
||||
// Past the gate the method may write or fail on the missing Divi – anything but the lock.
|
||||
foreach (self::WRITES as $name) {
|
||||
Functions\when($name)->alias(static function () use ($name): never {
|
||||
throw new RuntimeException($name . ' reached');
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
$write();
|
||||
} catch (DiviLocked $locked) {
|
||||
self::fail('refused although only other features are locked: ' . $locked->getMessage());
|
||||
} catch (Throwable) {
|
||||
// Reached the code behind the gate.
|
||||
}
|
||||
self::addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testTheCheckPageMayBeWrittenWhileProbing(): void
|
||||
{
|
||||
ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(ReleaseState::Untested, '5.3.0'));
|
||||
Functions\when('wp_update_post')->justReturn(41);
|
||||
Functions\when('update_post_meta')->justReturn(true);
|
||||
Functions\when('get_post_meta')->alias(
|
||||
|
||||
Reference in New Issue
Block a user