diff --git a/src/Compatibility/Call.php b/src/Compatibility/Call.php new file mode 100644 index 0000000..d44a045 --- /dev/null +++ b/src/Compatibility/Call.php @@ -0,0 +1,81 @@ + $features [] = the default of the manifest. + */ + public function __construct( + public readonly string $symbol, + public readonly ?int $args = null, + private readonly array $features = [] + ) { + } + + public function className(): ?string + { + return str_contains($this->symbol, '::') ? ltrim(explode('::', $this->symbol, 2)[0], '\\') : null; + } + + /** The key of this call's signature fingerprint in the reference: "sig:" and the symbol without a leading backslash. */ + public function signatureKey(): string + { + return self::SIGNATURE_PREFIX . ltrim($this->symbol, '\\'); + } + + public function member(): string + { + return str_contains($this->symbol, '::') ? explode('::', $this->symbol, 2)[1] : $this->symbol; + } + + /** The function or method exists and takes the number of arguments the adapter passes. */ + public function accepts(): bool + { + $class = $this->className(); + if ($class === null) { + if (! function_exists($this->member())) { + return false; + } + $reflection = new ReflectionFunction($this->member()); + } else { + if (! method_exists($class, $this->member())) { + return false; + } + $reflection = new ReflectionMethod($class, $this->member()); + } + + return $this->args === null + || ($reflection->getNumberOfRequiredParameters() <= $this->args + && ($reflection->isVariadic() || $reflection->getNumberOfParameters() >= $this->args)); + } + + public function features(): array + { + return $this->features; + } + + public function label(): string + { + $class = $this->className(); + + return ($class === null ? '' : SourceFingerprint::shortName($class) . '::') . $this->member() . '()'; + } +} diff --git a/src/Compatibility/CompatibilityMatrix.php b/src/Compatibility/CompatibilityMatrix.php new file mode 100644 index 0000000..d37c03a --- /dev/null +++ b/src/Compatibility/CompatibilityMatrix.php @@ -0,0 +1,230 @@ + keyed by capability ID, in the order of the capabilities */ + private array $features = []; + + /** + * @param list $capabilities + * @param array $statuses keyed by capability ID + */ + public function __construct(array $capabilities, array $statuses) + { + foreach ($capabilities as $capability) { + $status = $statuses[$capability->id()] ?? null; + $this->features[$capability->id()] = $status instanceof FeatureStatus && $status->feature === $capability + ? $status + : self::missing($capability); + } + } + + private static function missing(Capability $capability): FeatureStatus + { + return new FeatureStatus($capability, ReleaseState::Incompatible, [self::MISSING]); + } + + /** @param list $capabilities */ + public static function allReleased(array $capabilities, string $source = self::SOURCE_CODE): self + { + return self::uniform($capabilities, ReleaseState::Released, [], $source); + } + + /** @param list $capabilities */ + public static function allIncompatible(array $capabilities, string $problem): self + { + return self::uniform($capabilities, ReleaseState::Incompatible, [$problem]); + } + + /** + * Every capability in $state – the matrix of a status built without one (ReleaseStatus' constructor). + * + * @param list $capabilities + * @param list $problems + */ + public static function uniform( + array $capabilities, + ReleaseState $state, + array $problems = [], + ?string $source = null + ): self { + $released = $state === ReleaseState::Released; + $statuses = []; + foreach ($capabilities as $capability) { + $statuses[$capability->id()] = new FeatureStatus( + $capability, + $state, + $released ? [] : $problems, + $released ? ($source ?? self::SOURCE_CODE) : null + ); + } + + return new self($capabilities, $statuses); + } + + /** + * The matrix the problems of a check add up to (spec §5.2–§5.4): per capability a hard problem (call, mirror, + * constant) makes it incompatible, released version or not; a released version releases every other one; without + * release the automatic release decides (changed signature: incompatible, missing reference: untested, no + * problem: released from SOURCE_AUTO), else it is untested. + * + * @param list $capabilities + * @param list $problems + * @param string|null $source SOURCE_CODE or SOURCE_DATABASE for a released version, else null. + * @param bool $automatic The automatic release applies (no release, known version, filter on). + */ + public static function fromProblems(array $capabilities, array $problems, ?string $source, bool $automatic): self + { + $statuses = []; + foreach ($capabilities as $capability) { + $concerning = array_values(array_filter( + $problems, + static fn (Problem $problem): bool => $problem->concerns($capability) + )); + $statuses[$capability->id()] = self::statusOf($capability, $concerning, $source, $automatic); + } + + return new self($capabilities, $statuses); + } + + /** @param list $problems The problems concerning $capability. */ + private static function statusOf( + Capability $capability, + array $problems, + ?string $source, + bool $automatic + ): FeatureStatus { + $hard = array_filter($problems, static fn (Problem $problem): bool => $problem->isHard()); + if ($hard !== []) { + return new FeatureStatus($capability, ReleaseState::Incompatible, self::labels($hard)); + } + if ($source !== null) { + return new FeatureStatus($capability, ReleaseState::Released, [], $source); + } + if (! $automatic) { + return new FeatureStatus($capability, ReleaseState::Untested, self::labels($problems)); + } + + $changed = array_filter($problems, static fn (Problem $problem): bool => $problem->kind === Problem::SIGNATURE); + if ($changed !== []) { + return new FeatureStatus($capability, ReleaseState::Incompatible, self::labels($changed)); + } + + return $problems === [] + ? new FeatureStatus($capability, ReleaseState::Released, [], self::SOURCE_AUTO) + : new FeatureStatus($capability, ReleaseState::Untested, self::labels($problems)); + } + + /** + * @param array $problems + * @return list + */ + private static function labels(array $problems): array + { + return array_values(array_map(static fn (Problem $problem): string => $problem->label, $problems)); + } + + public function of(Capability $feature): FeatureStatus + { + return $this->features[$feature->id()] ?? self::missing($feature); + } + + /** @param list $features */ + public function usable(array $features): bool + { + foreach ($features as $feature) { + if (! $this->of($feature)->usable()) { + return false; + } + } + + return true; + } + + /** + * The statuses of those of $features that are not usable – each once, in the order of $features. The one answer + * to "what locks this call" for the gates, the adapters and the + * refusals. + * + * @param list $features + * @return list + */ + public function locked(array $features): array + { + $locked = []; + foreach ($features as $feature) { + $status = $this->of($feature); + if (! $status->usable()) { + $locked[$feature->id()] = $status; + } + } + + return array_values($locked); + } + + /** @return list */ + public function unusable(): array + { + return array_values(array_filter( + $this->features, + static fn (FeatureStatus $status): bool => ! $status->usable() + )); + } + + /** Incompatible before untested before released. */ + public function state(): ReleaseState + { + $states = array_map(static fn (FeatureStatus $status): ReleaseState => $status->state, $this->features); + + return match (true) { + in_array(ReleaseState::Incompatible, $states, true) => ReleaseState::Incompatible, + in_array(ReleaseState::Untested, $states, true) => ReleaseState::Untested, + default => ReleaseState::Released, + }; + } + + /** The common source of a fully released matrix; null when not all released or the sources differ. */ + public function source(): ?string + { + $sources = array_unique(array_map( + static fn (FeatureStatus $status): string => (string) $status->source, + $this->features + )); + + return $this->state() === ReleaseState::Released && count($sources) === 1 ? (string) reset($sources) : null; + } + + /** @return list Every problem label once, in feature order. */ + public function problems(): array + { + $problems = []; + foreach ($this->features as $status) { + $problems = array_merge($problems, $status->problems); + } + + return array_values(array_unique($problems)); + } + + /** @return list, source: ?string}> */ + public function toArray(): array + { + return array_values(array_map(static fn (FeatureStatus $status): array => $status->toArray(), $this->features)); + } +} diff --git a/src/Integrations/Divi/Release/ConstantRequirement.php b/src/Compatibility/ConstantRequirement.php similarity index 65% rename from src/Integrations/Divi/Release/ConstantRequirement.php rename to src/Compatibility/ConstantRequirement.php index 86a4c23..2001f37 100644 --- a/src/Integrations/Divi/Release/ConstantRequirement.php +++ b/src/Compatibility/ConstantRequirement.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; defined('ABSPATH') || exit; -/** A Divi constant an adapter reads (spec §4.1); it must be defined. */ +/** A constant of a third-party plugin or theme that an adapter reads (spec §4.1); it must be defined. */ final class ConstantRequirement implements Requirement { - /** @param list $features [] = the default of the manifest. */ + /** @param list $features [] = the default of the manifest. */ public function __construct(public readonly string $name, private readonly array $features = []) { } diff --git a/src/Integrations/Divi/Release/FeatureStatus.php b/src/Compatibility/FeatureStatus.php similarity index 77% rename from src/Integrations/Divi/Release/FeatureStatus.php rename to src/Compatibility/FeatureStatus.php index 29b3775..0bb4a1c 100644 --- a/src/Integrations/Divi/Release/FeatureStatus.php +++ b/src/Compatibility/FeatureStatus.php @@ -2,20 +2,20 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; defined('ABSPATH') || exit; -/** The release state of one Divi feature (E1): released (with its source), untested or incompatible. */ +/** The release state of one capability (E1): released (with its source), untested or incompatible. */ final class FeatureStatus { /** * @param list $problems Labels of the problems behind the state (Problem::$label). - * @param string|null $source For a released feature: ReleaseStatus::SOURCE_CODE, SOURCE_DATABASE or + * @param string|null $source For a released feature: CompatibilityMatrix::SOURCE_CODE, SOURCE_DATABASE or * SOURCE_AUTO; null otherwise. */ public function __construct( - public readonly DiviFeature $feature, + public readonly Capability $feature, public readonly ReleaseState $state, public readonly array $problems = [], public readonly ?string $source = null @@ -31,7 +31,7 @@ final class FeatureStatus public function summary(): array { return [ - 'id' => $this->feature->value, + 'id' => $this->feature->id(), 'label' => $this->feature->label(), 'state' => $this->state->value, 'problems' => $this->problems, diff --git a/src/Compatibility/GlobalRequirement.php b/src/Compatibility/GlobalRequirement.php new file mode 100644 index 0000000..64a3830 --- /dev/null +++ b/src/Compatibility/GlobalRequirement.php @@ -0,0 +1,32 @@ + $features [] = the default of the manifest. */ + public function __construct(public readonly string $name, private readonly array $features = []) + { + } + + public function label(): string + { + return 'Globale Variable ' . $this->name; + } + + public function features(): array + { + return $this->features; + } + + /** isset, not array_key_exists: a global that is null cannot be used by the adapter either. */ + public function isSet(): bool + { + return isset($GLOBALS[$this->name]); + } +} diff --git a/src/Integrations/Divi/Release/LineDiff.php b/src/Compatibility/LineDiff.php similarity index 98% rename from src/Integrations/Divi/Release/LineDiff.php rename to src/Compatibility/LineDiff.php index 7845af1..e8d1f90 100644 --- a/src/Integrations/Divi/Release/LineDiff.php +++ b/src/Compatibility/LineDiff.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; defined('ABSPATH') || exit; diff --git a/src/Integrations/Divi/Release/Manifest.php b/src/Compatibility/Manifest.php similarity index 51% rename from src/Integrations/Divi/Release/Manifest.php rename to src/Compatibility/Manifest.php index e84cd9c..4be00cd 100644 --- a/src/Integrations/Divi/Release/Manifest.php +++ b/src/Compatibility/Manifest.php @@ -2,40 +2,36 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; defined('ABSPATH') || exit; /** - * The Divi dependencies of one adapter (spec §4.1) – the single source for DiviCompatibility, the release gate, - * the admin check and DiviBoundaryTest. Pure data, no WordPress calls. + * The external dependencies of one adapter (spec §4.1) – the single source for the compatibility checks, the release + * gate, the admin check and DiviBoundaryTest. Pure data, no WordPress calls. Only area and calls are needed; the + * other fields are optional (the Divi integration uses all of them). */ final class Manifest { - public const PAGES = 'Seiten'; - public const LIBRARY = 'Bibliothek'; - public const THEME_BUILDER = 'Theme Builder'; - public const DESIGN = 'Designsystem'; - public const CATALOG = 'Katalog'; - /** @var list */ public readonly array $constants; /** - * @param string $area One of the constants above (the five areas of spec §3). - * @param list $calls Divi functions and static methods the adapter calls. - * @param list $constants Divi constants it reads; a plain name stands for a + * @param string $area Area label for reports (the Divi integration: DiviAreas). + * @param list $calls Functions and static methods the adapter calls. + * @param list $constants Constants it reads; a plain name stands for a * ConstantRequirement without features of its own. - * @param list $postTypes Divi post types it needs registered. - * @param list $taxonomies Divi taxonomies it needs registered. - * @param list $blockTypes Divi block types it needs (registered or in Divi's - * module metadata). - * @param list $keys Other Divi names it uses as strings: meta keys, options, + * @param list $postTypes Post types it needs registered. + * @param list $taxonomies Taxonomies it needs registered. + * @param list $blockTypes Block types it needs (registered or known + * to the ManifestCheck knowsBlock closure). + * @param list $keys Other names it uses as strings: meta keys, options, * internal post types that need no registration check, * query arguments. - * @param list $mirrors Divi functions whose storage logic it repeats. - * @param list $features The features of every call, mirror and constant that names + * @param list $mirrors Functions whose storage logic it repeats. + * @param list $features The features of every call, mirror and constant that names * none itself (featuresOf()). + * @param list $globals Global variables it reads (isset check). */ public function __construct( public readonly string $area, @@ -46,7 +42,8 @@ final class Manifest public readonly array $blockTypes = [], public readonly array $keys = [], public readonly array $mirrors = [], - public readonly array $features = [] + public readonly array $features = [], + public readonly array $globals = [] ) { $this->constants = array_map( static fn (string|ConstantRequirement $constant): ConstantRequirement => is_string($constant) @@ -56,13 +53,13 @@ final class Manifest ); } - /** @return list Calls, constants and mirrors – everything the release gate checks as code. */ + /** @return list Calls, constants, mirrors and globals – everything the release gate checks as code. */ public function requirements(): array { - return [...$this->calls, ...$this->constants, ...$this->mirrors]; + return [...$this->calls, ...$this->constants, ...$this->mirrors, ...$this->globals]; } - /** @return list The features $requirement names itself, else this manifest's default. */ + /** @return list The features $requirement names itself, else this manifest's default. */ public function featuresOf(Requirement $requirement): array { $own = $requirement->features(); @@ -71,8 +68,8 @@ final class Manifest } /** - * @return list Every Divi name the adapter's code may contain (DiviBoundaryTest): called functions and - * classes, constants, post types, taxonomies, keys and the names of mirrored functions. + * @return list Every external name the adapter's code may contain (DiviBoundaryTest): called functions and + * classes, constants, globals, post types, taxonomies, keys and the names of mirrored functions. */ public function names(): array { @@ -86,6 +83,7 @@ final class Manifest return array_values(array_unique(array_merge( $names, $constants, + array_map(static fn (GlobalRequirement $global): string => $global->name, $this->globals), $this->postTypes, $this->taxonomies, $this->keys, diff --git a/src/Integrations/Divi/Release/ManifestCheck.php b/src/Compatibility/ManifestCheck.php similarity index 70% rename from src/Integrations/Divi/Release/ManifestCheck.php rename to src/Compatibility/ManifestCheck.php index a91fe1f..0c268ad 100644 --- a/src/Integrations/Divi/Release/ManifestCheck.php +++ b/src/Compatibility/ManifestCheck.php @@ -2,18 +2,16 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; use Closure; -use LIT\MCP\Integrations\Divi\Schema\DiviModules; -use ReflectionFunction; -use ReflectionMethod; +use LogicException; use Throwable; defined('ABSPATH') || exit; /** - * The checks of all adapter manifests (spec §4.3), as label => check for reports, and as problems with their Divi + * The checks of all adapter manifests (spec §4.3), as label => check for reports, and as problems with their * features for the release matrix (problems(), E1). Code checks (calls with the argument count the adapter passes * and their signature, constants) and mirror checks decide the release gate; registration checks (post types, * taxonomies, block types) depend on the request context (stage3 finding 6) and only show in reports. @@ -22,32 +20,30 @@ defined('ABSPATH') || exit; */ class ManifestCheck { - /** @var array */ - private array $manifests; - - /** @var Closure(Mirror): ?string */ - private Closure $current; - - /** @var Closure(string): bool */ - private Closure $knowsBlock; - /** - * @param array|null $manifests null = DiviAdapters::manifests(). - * @param array|null $expected Mirror ID or "sig:" => sha256; - * null = DiviReference::expected(). + * @param array $manifests Area key => manifest. + * @param array|null $expected Mirror ID or "sig:" => sha256; null = none known. * @param (Closure(Mirror): ?string)|null $current Hash of the installed mirror, null = unreadable; - * null = SourceFingerprint in the Divi theme directory. - * @param (Closure(string): bool)|null $knowsBlock null = DiviModules::knowsBlock(). + * no closure = every mirror is unreadable (a problem). + * @param (Closure(string): bool)|null $knowsBlock Whether a block type is known; required when a manifest + * has block types, which would otherwise pass unchecked. + * @param bool $signatures false = no signature checks: neither reference-missing + * nor signature problems (calls are still checked for + * existence and argument count). + * @throws LogicException When a manifest has block types but $knowsBlock is null. */ public function __construct( - ?array $manifests = null, + private array $manifests, private ?array $expected = null, - ?Closure $current = null, - ?Closure $knowsBlock = null + private ?Closure $current = null, + private ?Closure $knowsBlock = null, + private bool $signatures = true ) { - $this->manifests = $manifests ?? DiviAdapters::manifests(); - $this->current = $current ?? self::installed(...); - $this->knowsBlock = $knowsBlock ?? DiviModules::knowsBlock(...); + foreach ($manifests as $manifest) { + if ($manifest->blockTypes !== [] && $knowsBlock === null) { + throw new LogicException('block types need a knowsBlock closure, else they pass unchecked'); + } + } } /** @return array */ @@ -62,6 +58,9 @@ class ManifestCheck $checks[$manifest->area . ': ' . $constant->label()] = static fn (): bool => defined($constant->name); } + foreach ($manifest->globals as $global) { + $checks[$manifest->area . ': ' . $global->label()] = static fn (): bool => $global->isSet(); + } } return $checks; @@ -92,7 +91,7 @@ class ManifestCheck $checks[$manifest->area . ': Taxonomie ' . $taxonomy] = static fn (): bool => taxonomy_exists($taxonomy); } - foreach ($manifest->blockTypes as $block) { + foreach ($this->knowsBlock === null ? [] : $manifest->blockTypes as $block) { $checks[$manifest->area . ': Block-Typ ' . $block] = fn (): bool => ($this->knowsBlock)($block); } } @@ -123,8 +122,8 @@ class ManifestCheck } /** - * Calls (missing or other argument count: call; no reference: reference-missing; other signature: signature) - * and constants. Reflection only – cheap enough for every request. + * Calls (missing or other argument count: call; no reference: reference-missing; other signature: signature; + * the last two only with signatures), constants and globals. Reflection only – cheap enough for every request. * * @return list */ @@ -147,6 +146,15 @@ class ManifestCheck ); } } + foreach ($manifest->globals as $global) { + if (! $global->isSet()) { + $problems[] = new Problem( + $manifest->area . ': ' . $global->label(), + $manifest->featuresOf($global), + Problem::GLOBAL + ); + } + } } return $problems; @@ -154,8 +162,8 @@ class ManifestCheck /** * Mirrors that differ from their reference, cannot be read or have no reference – kind mirror in every case: - * a mirror repeats how Divi writes, so it never runs unverified, whatever the release. Reads Divi files; the - * resolver caches the result. + * a mirror repeats how the third-party code writes, so it never runs unverified, whatever the release. Reads + * files; the resolver caches the result. * * @return list */ @@ -180,22 +188,7 @@ class ManifestCheck /** The function or method exists and takes the number of arguments the adapter passes. */ public static function accepts(Call $call): bool { - $class = $call->className(); - if ($class === null) { - if (! function_exists($call->member())) { - return false; - } - $reflection = new ReflectionFunction($call->member()); - } else { - if (! method_exists($class, $call->member())) { - return false; - } - $reflection = new ReflectionMethod($class, $call->member()); - } - - return $call->args === null - || ($reflection->getNumberOfRequiredParameters() <= $call->args - && ($reflection->isVariadic() || $reflection->getNumberOfParameters() >= $call->args)); + return $call->accepts(); } private function callProblem(Manifest $manifest, Call $call): ?Problem @@ -206,7 +199,11 @@ class ManifestCheck return new Problem($label, $features, Problem::CALL); } - $expected = $this->expected()[DiviReference::signatureKey($call)] ?? null; + if (! $this->signatures) { + return null; + } + + $expected = ($this->expected ?? [])[$call->signatureKey()] ?? null; if ($expected === null) { return new Problem($label . ' – Referenz der Signatur fehlt', $features, Problem::REFERENCE_MISSING); } @@ -219,15 +216,9 @@ class ManifestCheck /** The mirror's reference exists and the installed source has the same fingerprint. */ private function matches(Mirror $mirror): bool { - $expected = $this->expected()[$mirror->id] ?? null; + $expected = ($this->expected ?? [])[$mirror->id] ?? null; - return $expected !== null && ($this->current)($mirror) === $expected; - } - - /** @return array */ - private function expected(): array - { - return $this->expected ??= DiviReference::expected(); + return $expected !== null && $this->current !== null && ($this->current)($mirror) === $expected; } private static function mirrorLabel(Manifest $manifest, Mirror $mirror): string @@ -244,13 +235,4 @@ class ManifestCheck return false; } } - - private static function installed(Mirror $mirror): ?string - { - $dir = DiviEnvironment::themeDir(); - - return $dir === null - ? null - : SourceFingerprint::of($dir . '/' . $mirror->file, $mirror->function, $mirror->class)->hash; - } } diff --git a/src/Compatibility/Mirror.php b/src/Compatibility/Mirror.php new file mode 100644 index 0000000..84a31ff --- /dev/null +++ b/src/Compatibility/Mirror.php @@ -0,0 +1,48 @@ +.txt – an example, not a rule). + * @param string $file File of the third-party code, relative to its plugin or theme directory. + * @param string $function Function or method name. + * @param string|null $class Class of a method – the short name is enough (the file fixes where it + * lives, and a short name is no external name for DiviBoundaryTest); null for a + * function. + * @param string $what What the plugin repeats (German, for reports). + * @param list $features [] = the default of the manifest. + */ + public function __construct( + public readonly string $id, + public readonly string $file, + public readonly string $function, + public readonly ?string $class, + public readonly string $what, + private readonly array $features = [] + ) { + } + + public function features(): array + { + return $this->features; + } + + public function label(): string + { + $class = $this->class === null ? '' : SourceFingerprint::shortName($this->class) . '::'; + + return $class . $this->function . '()'; + } +} diff --git a/src/Integrations/Divi/Release/Problem.php b/src/Compatibility/Problem.php similarity index 55% rename from src/Integrations/Divi/Release/Problem.php rename to src/Compatibility/Problem.php index 438b831..896693f 100644 --- a/src/Integrations/Divi/Release/Problem.php +++ b/src/Compatibility/Problem.php @@ -2,15 +2,17 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; + +use Closure; defined('ABSPATH') || exit; /** - * One failed manifest check and the Divi features it concerns (Manifest::featuresOf()). The kind decides how the - * resolver weighs it (E1): call, mirror and constant lock their features for every Divi version, released or not; - * signature and reference-missing only matter for a version without release (automatic release). A problem without - * features concerns every feature (fail closed). + * One failed manifest check and the capabilities it concerns (Manifest::featuresOf()). The kind decides how the + * resolver weighs it (E1): call, mirror, constant and global lock their features for every version, released + * or not; signature and reference-missing only matter for a version without release (automatic release). A problem + * without features concerns every feature (fail closed). */ final class Problem { @@ -18,6 +20,7 @@ final class Problem public const SIGNATURE = 'signature'; public const MIRROR = 'mirror'; public const CONSTANT = 'constant'; + public const GLOBAL = 'global'; public const REFERENCE_MISSING = 'reference-missing'; public const RESOLVER = 'resolver'; @@ -26,15 +29,16 @@ final class Problem self::SIGNATURE, self::MIRROR, self::CONSTANT, + self::GLOBAL, self::REFERENCE_MISSING, self::RESOLVER, ]; - private const HARD = [self::CALL, self::MIRROR, self::CONSTANT, self::RESOLVER]; + private const HARD = [self::CALL, self::MIRROR, self::CONSTANT, self::GLOBAL, self::RESOLVER]; /** - * @param string $label German, e.g. "Seiten: et_x()" (the labels of ManifestCheck). - * @param list $features - * @param string $kind One of the constants above. + * @param string $label German, e.g. "Seiten: et_x()" (the labels of ManifestCheck). + * @param list $features + * @param string $kind One of the constants above. */ public function __construct( public readonly string $label, @@ -43,13 +47,13 @@ final class Problem ) { } - /** Locks its features whatever the release (the code no longer fits Divi or a mirror differs). */ + /** Locks its features whatever the release (the third-party code no longer fits or a mirror differs). */ public function isHard(): bool { return in_array($this->kind, self::HARD, true); } - public function concerns(DiviFeature $feature): bool + public function concerns(Capability $feature): bool { return $this->features === [] || in_array($feature, $this->features, true); } @@ -60,12 +64,16 @@ final class Problem return [ 'label' => $this->label, 'kind' => $this->kind, - 'features' => array_map(static fn (DiviFeature $feature): string => $feature->value, $this->features), + 'features' => array_map(static fn (Capability $feature): string => $feature->id(), $this->features), ]; } - /** The problem of toArray(), or null for anything else (an unknown kind or feature included). */ - public static function fromArray(mixed $row): ?self + /** + * The problem of toArray(), or null for anything else (an unknown kind or feature included). + * + * @param Closure(string): ?Capability $capability Finds a capability by its ID, null for an unknown one. + */ + public static function fromArray(mixed $row, Closure $capability): ?self { if ( ! is_array($row) @@ -78,7 +86,7 @@ final class Problem $features = []; foreach ($row['features'] as $value) { - $feature = is_string($value) ? DiviFeature::tryFrom($value) : null; + $feature = is_string($value) ? $capability($value) : null; if ($feature === null) { return null; } diff --git a/src/Integrations/Divi/Release/ReleaseState.php b/src/Compatibility/ReleaseState.php similarity index 82% rename from src/Integrations/Divi/Release/ReleaseState.php rename to src/Compatibility/ReleaseState.php index fb4ecea..5ac5e9b 100644 --- a/src/Integrations/Divi/Release/ReleaseState.php +++ b/src/Compatibility/ReleaseState.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; defined('ABSPATH') || exit; diff --git a/src/Integrations/Divi/Release/Requirement.php b/src/Compatibility/Requirement.php similarity index 66% rename from src/Integrations/Divi/Release/Requirement.php rename to src/Compatibility/Requirement.php index b119701..a2f6b5e 100644 --- a/src/Integrations/Divi/Release/Requirement.php +++ b/src/Compatibility/Requirement.php @@ -2,11 +2,14 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; defined('ABSPATH') || exit; -/** One thing an adapter needs from Divi (a call, a mirrored function or a constant) and the features it serves. */ +/** + * One thing an adapter needs from a third-party plugin or theme (a call, a mirrored function or a constant) and the + * features it serves. + */ interface Requirement { /** Name for reports, e.g. "GlobalPreset::get_data()" (without the manifest area). */ @@ -16,7 +19,7 @@ interface Requirement * The features this requirement serves itself; [] = the default of the manifest that lists it * (Manifest::featuresOf() resolves that). * - * @return list + * @return list */ public function features(): array; } diff --git a/src/Integrations/Divi/Release/SignatureFingerprint.php b/src/Compatibility/SignatureFingerprint.php similarity index 87% rename from src/Integrations/Divi/Release/SignatureFingerprint.php rename to src/Compatibility/SignatureFingerprint.php index 51accfa..28ad658 100644 --- a/src/Integrations/Divi/Release/SignatureFingerprint.php +++ b/src/Compatibility/SignatureFingerprint.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; use ReflectionException; use ReflectionFunction; @@ -14,14 +14,15 @@ use ReflectionType; defined('ABSPATH') || exit; /** - * Fingerprint of the signature of one Divi function or static method (spec §4.2, E1 of the plan "Divi-Fähigkeiten"), - * read with Reflection from the running PHP process – so it needs the symbol to be loaded (in WordPress Divi is) and - * never includes a file. The class of a method is autoloaded the way ManifestCheck::accepts() already does it. + * Fingerprint of the signature of one function or static method of a third-party plugin or theme (spec §4.2, E1 of + * the plan "Divi-Fähigkeiten"), read with Reflection from the running PHP process – so it needs the symbol to be + * loaded (in WordPress the plugin or theme is, once active) and never includes a file. The class of a method is + * autoloaded the way ManifestCheck::accepts() already does it. * * What the hash covers: function or method; for a method its visibility and whether it is static; per parameter the * type (nullable and union included; no type reads as "mixed"), by reference, variadic and whether it is optional * (has a default); the return type (none reads as "mixed"). What it leaves out: - * - Parameter names: the plugin passes every argument to Divi positionally (PHP 8 named arguments are never used), + * - Parameter names: the plugin passes every argument positionally (PHP 8 named arguments are never used), * so a rename cannot break a call, and a harmless rename must not lock a feature. * - Default values: they are expressions (constants, arrays) that Reflection may be unable to evaluate in some * requests and that change spelling without changing meaning; only "has a default" matters for the number of diff --git a/src/Integrations/Divi/Release/SourceFingerprint.php b/src/Compatibility/SourceFingerprint.php similarity index 91% rename from src/Integrations/Divi/Release/SourceFingerprint.php rename to src/Compatibility/SourceFingerprint.php index 75556de..abe8dc1 100644 --- a/src/Integrations/Divi/Release/SourceFingerprint.php +++ b/src/Compatibility/SourceFingerprint.php @@ -2,18 +2,18 @@ declare(strict_types=1); -namespace LIT\MCP\Integrations\Divi\Release; +namespace LIT\MCP\Compatibility; use RuntimeException; defined('ABSPATH') || exit; /** - * Fingerprint of one Divi function or method (spec §4.2), read with token_get_all(): the file is never included, - * so functions Divi loads only in some requests (theme-builder/api.php, admin-ajax only) are readable in every - * request. The hash covers the tokens from "function" to the closing brace without comments and whitespace – - * new comments or reformatting keep it, any code change changes it. $source is the verbatim text of those - * tokens for the human diff (wp lit-mcp divi-check --diff). + * Fingerprint of one function or method of a third-party plugin or theme (spec §4.2), read with token_get_all(): the + * file is never included, so functions loaded only in some requests (e.g. Divi's theme-builder/api.php, admin-ajax + * only) are readable in every request. The hash covers the tokens from "function" to the closing brace without + * comments and whitespace – new comments or reformatting keep it, any code change changes it. $source is the verbatim + * text of those tokens for the human diff (wp lit-mcp divi-check --diff). */ final class SourceFingerprint { diff --git a/src/Integrations/Divi/Admin/DiviTab.php b/src/Integrations/Divi/Admin/DiviTab.php index 260ff13..75ca45e 100644 --- a/src/Integrations/Divi/Admin/DiviTab.php +++ b/src/Integrations/Divi/Admin/DiviTab.php @@ -9,15 +9,15 @@ use LIT\MCP\Admin\AdminPostAction; use LIT\MCP\Admin\AdminTab; use LIT\MCP\Admin\SettingsPage; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\CheckReport; use LIT\MCP\Integrations\Divi\Release\CheckReportStore; use LIT\MCP\Integrations\Divi\Release\DiviCheckRoute; use LIT\MCP\Integrations\Divi\Release\DiviEnvironment; use LIT\MCP\Integrations\Divi\Release\FeatureOverview; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; +use LIT\MCP\Compatibility\FeatureStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use LIT\MCP\Integrations\Divi\Release\ReleaseResolver; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseStore; use LIT\MCP\Integrations\Divi\Release\WriteProbe; diff --git a/src/Integrations/Divi/Cli/DiviCheckCommand.php b/src/Integrations/Divi/Cli/DiviCheckCommand.php index 405e296..6a6beda 100644 --- a/src/Integrations/Divi/Cli/DiviCheckCommand.php +++ b/src/Integrations/Divi/Cli/DiviCheckCommand.php @@ -5,17 +5,17 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Cli; use Closure; +use LIT\MCP\Compatibility\LineDiff; +use LIT\MCP\Compatibility\SourceFingerprint; 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\CompatibilityMatrix; +use LIT\MCP\Compatibility\CompatibilityMatrix; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; use LIT\MCP\Integrations\Divi\Release\DiviEnvironment; use LIT\MCP\Integrations\Divi\Release\DiviReference; -use LIT\MCP\Integrations\Divi\Release\LineDiff; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; -use LIT\MCP\Integrations\Divi\Release\SourceFingerprint; use RuntimeException; defined('ABSPATH') || exit; diff --git a/src/Integrations/Divi/Design/DesignSystemReader.php b/src/Integrations/Divi/Design/DesignSystemReader.php index e722131..323698b 100644 --- a/src/Integrations/Divi/Design/DesignSystemReader.php +++ b/src/Integrations/Divi/Design/DesignSystemReader.php @@ -6,10 +6,11 @@ namespace LIT\MCP\Integrations\Divi\Design; use ET\Builder\Packages\GlobalData\GlobalData; use ET\Builder\Packages\GlobalData\GlobalPreset; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; defined('ABSPATH') || exit; @@ -29,7 +30,7 @@ class DesignSystemReader implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ new Call(GlobalData::class . '::get_global_colors', 0), new Call(GlobalData::class . '::get_global_variables', 0), diff --git a/src/Integrations/Divi/Design/DesignSystemWriter.php b/src/Integrations/Divi/Design/DesignSystemWriter.php index 925a8e7..8824a3e 100644 --- a/src/Integrations/Divi/Design/DesignSystemWriter.php +++ b/src/Integrations/Divi/Design/DesignSystemWriter.php @@ -8,12 +8,13 @@ use ET\Builder\Framework\UserRole\UserRole; use ET\Builder\Packages\Conversion\Conversion; use ET\Builder\Packages\GlobalData\GlobalPreset; use ET\Builder\Packages\ModuleLibrary\ModuleRegistration; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; use WP_Error; @@ -43,7 +44,7 @@ class DesignSystemWriter implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ // Read by the preset tools as well as by the design system tool. new Call(GlobalPreset::class . '::get_data', 0, [DiviFeature::DesignRead, DiviFeature::PresetWrite]), diff --git a/src/Integrations/Divi/Design/FontCatalog.php b/src/Integrations/Divi/Design/FontCatalog.php index e51154f..0501d6b 100644 --- a/src/Integrations/Divi/Design/FontCatalog.php +++ b/src/Integrations/Divi/Design/FontCatalog.php @@ -4,10 +4,11 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Design; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; defined('ABSPATH') || exit; @@ -26,7 +27,7 @@ class FontCatalog implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ new Call('et_builder_get_fonts', 0), new Call('et_builder_get_custom_fonts', 0), diff --git a/src/Integrations/Divi/Design/FontInstaller.php b/src/Integrations/Divi/Design/FontInstaller.php index da75ea5..6807e69 100644 --- a/src/Integrations/Divi/Design/FontInstaller.php +++ b/src/Integrations/Divi/Design/FontInstaller.php @@ -6,12 +6,13 @@ namespace LIT\MCP\Integrations\Divi\Design; use ET\Builder\VisualBuilder\Fonts\FontsUtility; use ET\Builder\VisualBuilder\Hooks\HooksRegistration; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; @@ -35,7 +36,7 @@ class FontInstaller implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ new Call(FontsUtility::class . '::mime_types_font', 0), new Call(HooksRegistration::class . '::check_filetype_and_ext_font', 3), diff --git a/src/Integrations/Divi/Design/GlobalColorsAdapter.php b/src/Integrations/Divi/Design/GlobalColorsAdapter.php index 9912c6a..ff166dc 100644 --- a/src/Integrations/Divi/Design/GlobalColorsAdapter.php +++ b/src/Integrations/Divi/Design/GlobalColorsAdapter.php @@ -5,11 +5,12 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Design; use ET\Builder\Packages\GlobalData\GlobalData; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; @@ -39,7 +40,7 @@ class GlobalColorsAdapter implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ new Call(GlobalData::class . '::get_global_colors', 0), new Call(GlobalData::class . '::sanitize_global_colors_data', 1), diff --git a/src/Integrations/Divi/Design/GlobalVariablesAdapter.php b/src/Integrations/Divi/Design/GlobalVariablesAdapter.php index df71d19..caf67f5 100644 --- a/src/Integrations/Divi/Design/GlobalVariablesAdapter.php +++ b/src/Integrations/Divi/Design/GlobalVariablesAdapter.php @@ -5,11 +5,12 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Design; use ET\Builder\Packages\GlobalData\GlobalData; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; use WP_Error; @@ -28,7 +29,7 @@ class GlobalVariablesAdapter implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ new Call(GlobalData::class . '::get_global_variables', 0), new Call(GlobalData::class . '::set_global_variables', 2), diff --git a/src/Integrations/Divi/Design/ThemeOptionsAdapter.php b/src/Integrations/Divi/Design/ThemeOptionsAdapter.php index 2c8d43c..c8eeb6a 100644 --- a/src/Integrations/Divi/Design/ThemeOptionsAdapter.php +++ b/src/Integrations/Divi/Design/ThemeOptionsAdapter.php @@ -5,12 +5,13 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Design; use InvalidArgumentException; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; @@ -61,7 +62,7 @@ class ThemeOptionsAdapter implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::DESIGN, + DiviAreas::DESIGN, calls: [ new Call('et_sanitize_font_choices', 1), new Call('et_sanitize_global_font_weight', 1), diff --git a/src/Integrations/Divi/DiviCompatibility.php b/src/Integrations/Divi/DiviCompatibility.php index 4ac6411..10cb205 100644 --- a/src/Integrations/Divi/DiviCompatibility.php +++ b/src/Integrations/Divi/DiviCompatibility.php @@ -4,7 +4,8 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi; -use LIT\MCP\Integrations\Divi\Release\ManifestCheck; +use LIT\MCP\Compatibility\ManifestCheck; +use LIT\MCP\Integrations\Divi\Release\DiviManifestCheck; defined('ABSPATH') || exit; @@ -29,7 +30,7 @@ final class DiviCompatibility /** @return array */ public static function defaults(?ManifestCheck $check = null): array { - $check ??= new ManifestCheck(); + $check ??= DiviManifestCheck::create(); return array_merge($check->codeChecks(), $check->registrationChecks(), $check->mirrorChecks()); } diff --git a/src/Integrations/Divi/Library/DiviLibraryStore.php b/src/Integrations/Divi/Library/DiviLibraryStore.php index 714864a..05bc4d6 100644 --- a/src/Integrations/Divi/Library/DiviLibraryStore.php +++ b/src/Integrations/Divi/Library/DiviLibraryStore.php @@ -5,12 +5,13 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Library; use LIT\MCP\Abilities\WriteFailed; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use LIT\MCP\Integrations\WordPress\Content\NewDraft; use RuntimeException; @@ -55,7 +56,7 @@ class DiviLibraryStore implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::LIBRARY, + DiviAreas::LIBRARY, calls: [ // isGlobal() serves listing and describing as well as the global edits. new Call( diff --git a/src/Integrations/Divi/Library/DiviRoles.php b/src/Integrations/Divi/Library/DiviRoles.php index e2c3e22..d77dfa8 100644 --- a/src/Integrations/Divi/Library/DiviRoles.php +++ b/src/Integrations/Divi/Library/DiviRoles.php @@ -6,10 +6,11 @@ namespace LIT\MCP\Integrations\Divi\Library; use Closure; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; use WP_Error; defined('ABSPATH') || exit; @@ -35,7 +36,7 @@ class DiviRoles implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::LIBRARY, + DiviAreas::LIBRARY, calls: [new Call('et_pb_is_allowed', 1)], features: [DiviFeature::Roles] ); diff --git a/src/Integrations/Divi/Pages/DiviPages.php b/src/Integrations/Divi/Pages/DiviPages.php index c6f69d8..29f8208 100644 --- a/src/Integrations/Divi/Pages/DiviPages.php +++ b/src/Integrations/Divi/Pages/DiviPages.php @@ -4,12 +4,13 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Pages; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; use WP_Post; @@ -35,7 +36,7 @@ final class DiviPages implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::PAGES, + DiviAreas::PAGES, calls: [ // Also called by DiviThemeBuilder::clearSiteCaches() – a break locks both features. new Call( diff --git a/src/Integrations/Divi/Pages/DynamicContentMap.php b/src/Integrations/Divi/Pages/DynamicContentMap.php index 5eeffac..8a3f720 100644 --- a/src/Integrations/Divi/Pages/DynamicContentMap.php +++ b/src/Integrations/Divi/Pages/DynamicContentMap.php @@ -8,10 +8,11 @@ use Closure; use LIT\MCP\Integrations\Divi\Compose\Placeholders; use LIT\MCP\Integrations\Divi\Compose\DynamicContentRules; use LIT\MCP\Integrations\Divi\Compose\LoopScope; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; use LIT\MCP\Log; use Throwable; @@ -56,7 +57,7 @@ class DynamicContentMap implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::CATALOG, + DiviAreas::CATALOG, calls: [new Call(self::REGISTRY . '::get_options', 2)], features: [DiviFeature::DynamicSources] ); diff --git a/src/Integrations/Divi/Release/Call.php b/src/Integrations/Divi/Release/Call.php deleted file mode 100644 index 2b714ac..0000000 --- a/src/Integrations/Divi/Release/Call.php +++ /dev/null @@ -1,47 +0,0 @@ - $features [] = the default of the manifest. - */ - public function __construct( - public readonly string $symbol, - public readonly ?int $args = null, - private readonly array $features = [] - ) { - } - - public function className(): ?string - { - return str_contains($this->symbol, '::') ? ltrim(explode('::', $this->symbol, 2)[0], '\\') : null; - } - - public function member(): string - { - return str_contains($this->symbol, '::') ? explode('::', $this->symbol, 2)[1] : $this->symbol; - } - - public function features(): array - { - return $this->features; - } - - public function label(): string - { - $class = $this->className(); - - return ($class === null ? '' : SourceFingerprint::shortName($class) . '::') . $this->member() . '()'; - } -} diff --git a/src/Integrations/Divi/Release/CompatibilityCheck.php b/src/Integrations/Divi/Release/CompatibilityCheck.php index 3ed50c4..2fb631a 100644 --- a/src/Integrations/Divi/Release/CompatibilityCheck.php +++ b/src/Integrations/Divi/Release/CompatibilityCheck.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; use Closure; +use LIT\MCP\Compatibility\ManifestCheck; use LIT\MCP\Integrations\Divi\Schema\DiviModules; use LIT\MCP\Integrations\Divi\Schema\ShortformCheck; use LIT\MCP\Integrations\WordPress\Content\ContentComposer; @@ -40,7 +41,7 @@ class CompatibilityCheck public static function create(ContentComposer $composer): self { return new self( - new ManifestCheck(), + DiviManifestCheck::create(), static fn (): array => DiviModules::isAvailable() ? ShortformCheck::problems((new DiviModules())->catalog()) : ['Divi 5 ist in dieser Anfrage nicht geladen.'], diff --git a/src/Integrations/Divi/Release/CompatibilityMatrix.php b/src/Integrations/Divi/Release/CompatibilityMatrix.php deleted file mode 100644 index 4641243..0000000 --- a/src/Integrations/Divi/Release/CompatibilityMatrix.php +++ /dev/null @@ -1,148 +0,0 @@ - keyed by DiviFeature value, in DiviFeature::cases() order */ - private array $features = []; - - /** @param array $features keyed by DiviFeature value */ - public function __construct(array $features) - { - foreach (DiviFeature::cases() as $feature) { - $status = $features[$feature->value] ?? null; - $this->features[$feature->value] = $status instanceof FeatureStatus && $status->feature === $feature - ? $status - : new FeatureStatus($feature, ReleaseState::Incompatible, [self::MISSING]); - } - } - - public static function allReleased(string $source = ReleaseStatus::SOURCE_CODE): self - { - return self::uniform(ReleaseState::Released, [], $source); - } - - public static function allIncompatible(string $problem): self - { - return self::uniform(ReleaseState::Incompatible, [$problem]); - } - - /** - * Every feature in $state – the matrix of a status built without one (ReleaseStatus' constructor). - * - * @param list $problems - */ - public static function uniform(ReleaseState $state, array $problems = [], ?string $source = null): self - { - $released = $state === ReleaseState::Released; - $features = []; - foreach (DiviFeature::cases() as $feature) { - $features[$feature->value] = new FeatureStatus( - $feature, - $state, - $released ? [] : $problems, - $released ? ($source ?? ReleaseStatus::SOURCE_CODE) : null - ); - } - - return new self($features); - } - - public function of(DiviFeature $feature): FeatureStatus - { - return $this->features[$feature->value]; - } - - /** @param list $features */ - public function usable(array $features): bool - { - foreach ($features as $feature) { - if (! $this->of($feature)->usable()) { - return false; - } - } - - return true; - } - - /** - * The statuses of those of $features that are not usable – each once, in the order of $features. The one answer - * to "what locks this call" for the gate (DiviFeatureGate), the adapters (ReleaseGate::assertFeatures()) and the - * refusal (ReleaseStatus::refusalFor()). - * - * @param list $features - * @return list - */ - public function locked(array $features): array - { - $locked = []; - foreach ($features as $feature) { - $status = $this->of($feature); - if (! $status->usable()) { - $locked[$feature->value] = $status; - } - } - - return array_values($locked); - } - - /** @return list */ - public function unusable(): array - { - return array_values(array_filter( - $this->features, - static fn (FeatureStatus $status): bool => ! $status->usable() - )); - } - - /** Incompatible before untested before released. */ - public function state(): ReleaseState - { - $states = array_map(static fn (FeatureStatus $status): ReleaseState => $status->state, $this->features); - - return match (true) { - in_array(ReleaseState::Incompatible, $states, true) => ReleaseState::Incompatible, - in_array(ReleaseState::Untested, $states, true) => ReleaseState::Untested, - default => ReleaseState::Released, - }; - } - - /** The common source of a fully released matrix; null when not all released or the sources differ. */ - public function source(): ?string - { - $sources = array_unique(array_map( - static fn (FeatureStatus $status): string => (string) $status->source, - $this->features - )); - - return $this->state() === ReleaseState::Released && count($sources) === 1 ? (string) reset($sources) : null; - } - - /** @return list Every problem label once, in feature order. */ - public function problems(): array - { - $problems = []; - foreach ($this->features as $status) { - $problems = array_merge($problems, $status->problems); - } - - return array_values(array_unique($problems)); - } - - /** @return list, source: ?string}> */ - public function toArray(): array - { - return array_values(array_map(static fn (FeatureStatus $status): array => $status->toArray(), $this->features)); - } -} diff --git a/src/Integrations/Divi/Release/DiviAdapter.php b/src/Integrations/Divi/Release/DiviAdapter.php index 9f0387b..5a70acd 100644 --- a/src/Integrations/Divi/Release/DiviAdapter.php +++ b/src/Integrations/Divi/Release/DiviAdapter.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; +use LIT\MCP\Compatibility\Manifest; + defined('ABSPATH') || exit; /** A class that may touch Divi (spec §3); it lists every Divi dependency in its manifest. */ diff --git a/src/Integrations/Divi/Release/DiviAdapters.php b/src/Integrations/Divi/Release/DiviAdapters.php index d827d51..a6fe87c 100644 --- a/src/Integrations/Divi/Release/DiviAdapters.php +++ b/src/Integrations/Divi/Release/DiviAdapters.php @@ -4,6 +4,9 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Design\DesignSystemReader; use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter; use LIT\MCP\Integrations\Divi\Design\FontCatalog; diff --git a/src/Integrations/Divi/Release/DiviAreas.php b/src/Integrations/Divi/Release/DiviAreas.php new file mode 100644 index 0000000..191561a --- /dev/null +++ b/src/Integrations/Divi/Release/DiviAreas.php @@ -0,0 +1,17 @@ +file, $mirror->function, $mirror->class)->hash; + } +} diff --git a/src/Integrations/Divi/Release/DiviReference.php b/src/Integrations/Divi/Release/DiviReference.php index 27eb3f4..0764390 100644 --- a/src/Integrations/Divi/Release/DiviReference.php +++ b/src/Integrations/Divi/Release/DiviReference.php @@ -4,6 +4,9 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\SignatureFingerprint; +use LIT\MCP\Compatibility\SourceFingerprint; use RuntimeException; defined('ABSPATH') || exit; @@ -18,8 +21,7 @@ final class DiviReference { public const FILE = __DIR__ . '/fingerprints.json'; - /** Key prefix of the signature fingerprints; no mirror ID starts with it. */ - public const SIGNATURE_PREFIX = 'sig:'; + public const SIGNATURE_PREFIX = Call::SIGNATURE_PREFIX; /** * @return array Mirror ID or "sig:" => sha256, sorted by key; [] when the file is @@ -61,12 +63,6 @@ final class DiviReference } } - /** The key of $call's signature fingerprint: "sig:" and the symbol without a leading backslash. */ - public static function signatureKey(Call $call): string - { - return self::SIGNATURE_PREFIX . ltrim($call->symbol, '\\'); - } - /** * Signature fingerprints of the calls in the running PHP process (Divi loaded). * @@ -82,7 +78,7 @@ final class DiviReference if ($hash === null) { throw new RuntimeException(sprintf('signature of %s: symbol is not loaded', $call->label())); } - $signatures[self::signatureKey($call)] = $hash; + $signatures[$call->signatureKey()] = $hash; } ksort($signatures); diff --git a/src/Integrations/Divi/Release/FeatureOverview.php b/src/Integrations/Divi/Release/FeatureOverview.php index 385a1e2..2166c22 100644 --- a/src/Integrations/Divi/Release/FeatureOverview.php +++ b/src/Integrations/Divi/Release/FeatureOverview.php @@ -6,6 +6,7 @@ namespace LIT\MCP\Integrations\Divi\Release; use Closure; use LIT\MCP\Abilities\Ability; +use LIT\MCP\Compatibility\CompatibilityMatrix; defined('ABSPATH') || exit; diff --git a/src/Integrations/Divi/Release/Mirror.php b/src/Integrations/Divi/Release/Mirror.php deleted file mode 100644 index e6f42bb..0000000 --- a/src/Integrations/Divi/Release/Mirror.php +++ /dev/null @@ -1,46 +0,0 @@ -.txt. - * @param string $file Divi file, relative to the Divi theme directory. - * @param string $function Function or method name. - * @param string|null $class Class of a method – the short name is enough (the file fixes where it - * lives, and a short name is no Divi name for DiviBoundaryTest); null for a - * function. - * @param string $what What the plugin repeats (German, for reports). - * @param list $features [] = the default of the manifest. - */ - public function __construct( - public readonly string $id, - public readonly string $file, - public readonly string $function, - public readonly ?string $class, - public readonly string $what, - private readonly array $features = [] - ) { - } - - public function features(): array - { - return $this->features; - } - - public function label(): string - { - $class = $this->class === null ? '' : SourceFingerprint::shortName($this->class) . '::'; - - return $class . $this->function . '()'; - } -} diff --git a/src/Integrations/Divi/Release/ReleaseGate.php b/src/Integrations/Divi/Release/ReleaseGate.php index 14a276b..af3e95b 100644 --- a/src/Integrations/Divi/Release/ReleaseGate.php +++ b/src/Integrations/Divi/Release/ReleaseGate.php @@ -5,6 +5,9 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; use Closure; +use LIT\MCP\Compatibility\Capability; +use LIT\MCP\Compatibility\CompatibilityMatrix; +use LIT\MCP\Compatibility\FeatureStatus; use LIT\MCP\Log; use Throwable; @@ -37,7 +40,10 @@ final class ReleaseGate self::$status = (self::$resolver ?? static fn (): ReleaseStatus => (new ReleaseResolver())->resolve())(); } catch (Throwable $e) { Log::failure('divi release status', $e); - self::$status = ReleaseStatus::of('', CompatibilityMatrix::allIncompatible(self::UNRESOLVED)); + self::$status = ReleaseStatus::of( + '', + CompatibilityMatrix::allIncompatible(DiviFeature::cases(), self::UNRESOLVED) + ); } return self::$status; @@ -107,11 +113,17 @@ final class ReleaseGate { $status = self::status(); $locked = $status->matrix->locked($features); + if ($locked === []) { + return null; + } - return $locked === [] ? null : new DiviLocked( - $status, - array_map(static fn (FeatureStatus $feature): DiviFeature => $feature->feature, $locked) - ); + $named = array_values(array_filter( + array_map(static fn (FeatureStatus $feature): Capability => $feature->feature, $locked), + static fn (Capability $capability): bool => $capability instanceof DiviFeature + )); + + // Fail closed: the lock stays; without a DiviFeature among its capabilities it names the features asked for. + return new DiviLocked($status, $named === [] ? $features : $named); } public static function whileProbing(int $postId, callable $write): mixed diff --git a/src/Integrations/Divi/Release/ReleaseResolver.php b/src/Integrations/Divi/Release/ReleaseResolver.php index 30eff1d..32bb65e 100644 --- a/src/Integrations/Divi/Release/ReleaseResolver.php +++ b/src/Integrations/Divi/Release/ReleaseResolver.php @@ -5,6 +5,9 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; use Closure; +use LIT\MCP\Compatibility\CompatibilityMatrix; +use LIT\MCP\Compatibility\ManifestCheck; +use LIT\MCP\Compatibility\Problem; use LIT\MCP\Plugin; defined('ABSPATH') || exit; @@ -44,15 +47,19 @@ final class ReleaseResolver public function resolve(): ReleaseStatus { $version = ($this->version ?? DiviEnvironment::version(...))() ?? ''; - $check = $this->check ?? new ManifestCheck(); + $check = $this->check ?? DiviManifestCheck::create(); $problems = array_merge($check->codeProblems(), $this->mirrorProblems($check, $version)); if (in_array($version, DiviReleases::TESTED, true)) { - return ReleaseStatus::of($version, self::matrix($problems, ReleaseStatus::SOURCE_CODE, false)); + return ReleaseStatus::of($version, self::matrix($problems, CompatibilityMatrix::SOURCE_CODE, false)); } $entry = $version === '' ? null : ($this->store ?? new ReleaseStore())->find($version, Plugin::VERSION); if ($entry !== null) { - return ReleaseStatus::of($version, self::matrix($problems, ReleaseStatus::SOURCE_DATABASE, false), $entry); + return ReleaseStatus::of( + $version, + self::matrix($problems, CompatibilityMatrix::SOURCE_DATABASE, false), + $entry + ); } return ReleaseStatus::of($version, self::matrix($problems, null, $version !== '' && self::automatic())); @@ -81,60 +88,10 @@ final class ReleaseResolver return apply_filters(self::AUTO_RELEASE_FILTER, true) === true; } - /** - * @param list $problems - * @param string|null $source SOURCE_CODE or SOURCE_DATABASE for a released version, else null. - * @param bool $automatic The automatic release applies (no release, known version, filter on). - */ + /** @param list $problems */ private static function matrix(array $problems, ?string $source, bool $automatic): CompatibilityMatrix { - $features = []; - foreach (DiviFeature::cases() as $feature) { - $concerning = array_values(array_filter( - $problems, - static fn (Problem $problem): bool => $problem->concerns($feature) - )); - $features[$feature->value] = self::feature($feature, $concerning, $source, $automatic); - } - - return new CompatibilityMatrix($features); - } - - /** @param list $problems The problems concerning $feature. */ - private static function feature( - DiviFeature $feature, - array $problems, - ?string $source, - bool $automatic - ): FeatureStatus { - $hard = array_filter($problems, static fn (Problem $problem): bool => $problem->isHard()); - if ($hard !== []) { - return new FeatureStatus($feature, ReleaseState::Incompatible, self::labels($hard)); - } - if ($source !== null) { - return new FeatureStatus($feature, ReleaseState::Released, [], $source); - } - if (! $automatic) { - return new FeatureStatus($feature, ReleaseState::Untested, self::labels($problems)); - } - - $changed = array_filter($problems, static fn (Problem $problem): bool => $problem->kind === Problem::SIGNATURE); - if ($changed !== []) { - return new FeatureStatus($feature, ReleaseState::Incompatible, self::labels($changed)); - } - - return $problems === [] - ? new FeatureStatus($feature, ReleaseState::Released, [], ReleaseStatus::SOURCE_AUTO) - : new FeatureStatus($feature, ReleaseState::Untested, self::labels($problems)); - } - - /** - * @param array $problems - * @return list - */ - private static function labels(array $problems): array - { - return array_values(array_map(static fn (Problem $problem): string => $problem->label, $problems)); + return CompatibilityMatrix::fromProblems(DiviFeature::cases(), $problems, $source, $automatic); } /** @return list */ @@ -174,7 +131,7 @@ final class ReleaseResolver $problems = []; foreach ($entry['problems'] as $row) { - $problem = Problem::fromArray($row); + $problem = Problem::fromArray($row, DiviFeature::tryFrom(...)); if ($problem === null) { return null; } diff --git a/src/Integrations/Divi/Release/ReleaseStatus.php b/src/Integrations/Divi/Release/ReleaseStatus.php index 86f0ebe..28931e6 100644 --- a/src/Integrations/Divi/Release/ReleaseStatus.php +++ b/src/Integrations/Divi/Release/ReleaseStatus.php @@ -5,6 +5,9 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; use LIT\MCP\Abilities\Errors; +use LIT\MCP\Compatibility\CompatibilityMatrix; +use LIT\MCP\Compatibility\FeatureStatus; +use LIT\MCP\Compatibility\ReleaseState; use LogicException; use WP_Error; @@ -16,9 +19,9 @@ defined('ABSPATH') || exit; */ final class ReleaseStatus { - public const SOURCE_CODE = 'code'; - public const SOURCE_DATABASE = 'database'; - public const SOURCE_AUTO = 'auto'; + public const SOURCE_CODE = CompatibilityMatrix::SOURCE_CODE; + public const SOURCE_DATABASE = CompatibilityMatrix::SOURCE_DATABASE; + public const SOURCE_AUTO = CompatibilityMatrix::SOURCE_AUTO; private const FEATURE_UNTESTED = 'Achtung: %1$s ist auf dieser Website für %2$s noch nicht für LIT MCP ' . 'freigegeben. Das ist gesperrt, bis ein Administrator unter Einstellungen → LIT MCP → Divi die Prüfung ' @@ -52,7 +55,7 @@ final class ReleaseStatus if ($matrix !== null && $matrix->state() !== $state) { throw new LogicException('the matrix is ' . $matrix->state()->value . ', not ' . $state->value); } - $this->matrix = $matrix ?? CompatibilityMatrix::uniform($state, $problems, $source); + $this->matrix = $matrix ?? CompatibilityMatrix::uniform(DiviFeature::cases(), $state, $problems, $source); } /** @param array|null $release The database entry of a SOURCE_DATABASE release. */ diff --git a/src/Integrations/Divi/Schema/DiviModules.php b/src/Integrations/Divi/Schema/DiviModules.php index feaeeed..3813a8f 100644 --- a/src/Integrations/Divi/Schema/DiviModules.php +++ b/src/Integrations/Divi/Schema/DiviModules.php @@ -7,10 +7,11 @@ namespace LIT\MCP\Integrations\Divi\Schema; use ET\Builder\Packages\ModuleLibrary\ModuleRegistration; use LIT\MCP\Integrations\Divi\BlockTree\GlobalInstance; use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Compatibility\Manifest; use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder; defined('ABSPATH') || exit; @@ -39,7 +40,7 @@ class DiviModules implements DiviAdapter public static function manifest(): Manifest { return new Manifest( - Manifest::CATALOG, + DiviAreas::CATALOG, calls: [ new Call(ModuleRegistration::class . '::get_all_core_modules_metadata', 0), new Call(ModuleRegistration::class . '::get_default_attrs', 2), diff --git a/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php b/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php index c7b45e6..09001d6 100644 --- a/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php +++ b/src/Integrations/Divi/ThemeBuilder/DiviThemeBuilder.php @@ -4,12 +4,13 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\ThemeBuilder; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use RuntimeException; use Throwable; @@ -83,7 +84,7 @@ class DiviThemeBuilder implements DiviAdapter $dir = 'includes/builder/frontend-builder/theme-builder/'; return new Manifest( - Manifest::THEME_BUILDER, + DiviAreas::THEME_BUILDER, calls: [ // liveId(): reads the live post, or creates it like Divi's first save. new Call( diff --git a/tests/Support/DiviFeatureFixtures.php b/tests/Support/DiviFeatureFixtures.php index ef0869d..2bcdf1b 100644 --- a/tests/Support/DiviFeatureFixtures.php +++ b/tests/Support/DiviFeatureFixtures.php @@ -4,11 +4,11 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Support; -use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix; +use LIT\MCP\Compatibility\ReleaseState; +use LIT\MCP\Compatibility\CompatibilityMatrix; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; +use LIT\MCP\Compatibility\FeatureStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; /** A release status in which single Divi capabilities are locked – for guard and tool tests. */ @@ -37,7 +37,7 @@ final class DiviFeatureFixtures } } - $status = new ReleaseStatus($overall, $version, null, [], null, matrix: new CompatibilityMatrix($features)); + $status = new ReleaseStatus($overall, $version, null, [], null, matrix: new CompatibilityMatrix(DiviFeature::cases(), $features)); ReleaseGate::useResolver(static fn (): ReleaseStatus => $status); } diff --git a/tests/Support/MatrixFixtures.php b/tests/Support/MatrixFixtures.php index e46d524..5e12b17 100644 --- a/tests/Support/MatrixFixtures.php +++ b/tests/Support/MatrixFixtures.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Support; -use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix; +use LIT\MCP\Compatibility\ReleaseState; +use LIT\MCP\Compatibility\CompatibilityMatrix; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; +use LIT\MCP\Compatibility\FeatureStatus; /** Compatibility matrices for tests: every feature released, with single features replaced. */ final class MatrixFixtures @@ -22,10 +22,10 @@ final class MatrixFixtures $features[$feature->value] = new FeatureStatus($feature, ReleaseState::Released, [], $source); } foreach ($replace as $status) { - $features[$status->feature->value] = $status; + $features[$status->feature->id()] = $status; } - return new CompatibilityMatrix($features); + return new CompatibilityMatrix(DiviFeature::cases(), $features); } /** @param string ...$problems */ diff --git a/tests/Support/ReleaseMatrices.php b/tests/Support/ReleaseMatrices.php index 98b0cbc..fe464f4 100644 --- a/tests/Support/ReleaseMatrices.php +++ b/tests/Support/ReleaseMatrices.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Support; -use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix; +use LIT\MCP\Compatibility\ReleaseState; +use LIT\MCP\Compatibility\CompatibilityMatrix; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; +use LIT\MCP\Compatibility\FeatureStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; /** @@ -30,7 +30,7 @@ final class ReleaseMatrices : new FeatureStatus($feature, ReleaseState::Released, [], ReleaseStatus::SOURCE_CODE); } - return new CompatibilityMatrix($features); + return new CompatibilityMatrix(DiviFeature::cases(), $features); } /** @param list $locked */ @@ -52,7 +52,7 @@ final class ReleaseMatrices string $source = ReleaseStatus::SOURCE_CODE, ?array $release = null ): ReleaseStatus { - return ReleaseStatus::of($version, CompatibilityMatrix::allReleased($source), $release); + return ReleaseStatus::of($version, CompatibilityMatrix::allReleased(DiviFeature::cases(), $source), $release); } /** @@ -62,6 +62,6 @@ final class ReleaseMatrices */ public static function uniform(ReleaseState $state, string $version = '5.3.0', array $problems = []): ReleaseStatus { - return ReleaseStatus::of($version, CompatibilityMatrix::uniform($state, $problems)); + return ReleaseStatus::of($version, CompatibilityMatrix::uniform(DiviFeature::cases(), $state, $problems)); } } diff --git a/tests/Unit/Abilities/AbilityRegistrarTest.php b/tests/Unit/Abilities/AbilityRegistrarTest.php index e819c44..ef6f31a 100644 --- a/tests/Unit/Abilities/AbilityRegistrarTest.php +++ b/tests/Unit/Abilities/AbilityRegistrarTest.php @@ -12,11 +12,11 @@ use LIT\MCP\Abilities\Errors; use LIT\MCP\Abilities\ExposurePolicy; use LIT\MCP\Abilities\ToolRefusal; use LIT\MCP\Abilities\WriteFailed; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviFeatureGate; 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\RequiresDiviFeatures; use LIT\MCP\Integrations\Integration; use LIT\MCP\Integrations\IntegrationRegistry; diff --git a/tests/Unit/Architecture/CoreIndependenceTest.php b/tests/Unit/Architecture/CoreIndependenceTest.php index ff1d906..27ef7cb 100644 --- a/tests/Unit/Architecture/CoreIndependenceTest.php +++ b/tests/Unit/Architecture/CoreIndependenceTest.php @@ -24,6 +24,7 @@ final class CoreIndependenceTest extends TestCase 'Admin', 'ChangeLog', 'Cli', + 'Compatibility', 'Guides', 'Integrations/Core', 'Integrations/Media', diff --git a/tests/Unit/Compatibility/CallTest.php b/tests/Unit/Compatibility/CallTest.php new file mode 100644 index 0000000..a28bb68 --- /dev/null +++ b/tests/Unit/Compatibility/CallTest.php @@ -0,0 +1,52 @@ +accepts()); + self::assertTrue((new Call('sprintf', 5))->accepts(), 'variadic'); + } + + public function testRejectsAnArgumentCountThePlatformFunctionDoesNotTake(): void + { + self::assertFalse((new Call('strlen', 2))->accepts(), 'strlen takes one argument'); + self::assertFalse((new Call('str_replace', 2))->accepts(), 'str_replace needs three'); + } + + public function testNullArgumentCountOnlyChecksExistence(): void + { + self::assertTrue((new Call('str_replace'))->accepts()); + } + + public function testMissingFunctionOrMethodIsRejected(): void + { + self::assertFalse((new Call('lit_mcp_missing_function'))->accepts()); + self::assertFalse((new Call(self::class . '::missing'))->accepts()); + self::assertFalse((new Call('Lit\Missing\Klass::method'))->accepts()); + } + + public function testChecksTheArgumentCountOfAMethod(): void + { + self::assertTrue((new Call(self::method(), 1))->accepts()); + self::assertTrue((new Call(self::method(), 2))->accepts()); + self::assertFalse((new Call(self::method(), 0))->accepts(), 'one required'); + self::assertFalse((new Call(self::method(), 3))->accepts(), 'at most two'); + } +} diff --git a/tests/Unit/Integrations/Divi/Release/CompatibilityMatrixTest.php b/tests/Unit/Compatibility/CompatibilityMatrixTest.php similarity index 54% rename from tests/Unit/Integrations/Divi/Release/CompatibilityMatrixTest.php rename to tests/Unit/Compatibility/CompatibilityMatrixTest.php index a622402..4e1eb06 100644 --- a/tests/Unit/Integrations/Divi/Release/CompatibilityMatrixTest.php +++ b/tests/Unit/Compatibility/CompatibilityMatrixTest.php @@ -2,12 +2,14 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; -use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix; +use LIT\MCP\Compatibility\Capability; +use LIT\MCP\Compatibility\CompatibilityMatrix; +use LIT\MCP\Compatibility\FeatureStatus; +use LIT\MCP\Compatibility\Problem; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Tests\Support\ReleaseMatrices; use PHPUnit\Framework\TestCase; @@ -15,7 +17,7 @@ final class CompatibilityMatrixTest extends TestCase { public function testAFeatureWithoutResultIsIncompatible(): void { - $matrix = new CompatibilityMatrix([ + $matrix = new CompatibilityMatrix(DiviFeature::cases(), [ 'page-read' => new FeatureStatus(DiviFeature::PageRead, ReleaseState::Released, [], 'code'), // Under the wrong key: does not count for page-write. 'page-write' => new FeatureStatus(DiviFeature::PageRead, ReleaseState::Released, [], 'code'), @@ -32,8 +34,8 @@ final class CompatibilityMatrixTest extends TestCase public function testAllReleasedAndAllIncompatible(): void { - $released = CompatibilityMatrix::allReleased('database'); - $broken = CompatibilityMatrix::allIncompatible('kaputt'); + $released = CompatibilityMatrix::allReleased(DiviFeature::cases(), 'database'); + $broken = CompatibilityMatrix::allIncompatible(DiviFeature::cases(), 'kaputt'); foreach (DiviFeature::cases() as $feature) { self::assertTrue($released->of($feature)->usable()); @@ -43,7 +45,7 @@ final class CompatibilityMatrixTest extends TestCase self::assertNull($broken->of($feature)->source); } self::assertSame([], $released->unusable()); - self::assertSame('code', CompatibilityMatrix::allReleased()->source()); + self::assertSame('code', CompatibilityMatrix::allReleased(DiviFeature::cases())->source()); } public function testTheStateIsTheWorstFeatureAndEveryProblemIsListedOnce(): void @@ -64,7 +66,7 @@ final class CompatibilityMatrixTest extends TestCase { self::assertSame('auto', self::matrix([])->source()); - $mixed = new CompatibilityMatrix(array_merge( + $mixed = new CompatibilityMatrix(DiviFeature::cases(), array_merge( self::features([]), ['roles' => new FeatureStatus(DiviFeature::Roles, ReleaseState::Released, [], 'code')] )); @@ -146,6 +148,95 @@ final class CompatibilityMatrixTest extends TestCase /** @param array}> $locked */ private static function matrix(array $locked): CompatibilityMatrix { - return new CompatibilityMatrix(self::features($locked)); + return new CompatibilityMatrix(DiviFeature::cases(), self::features($locked)); + } + + private static function fakeCapability(string $id, bool $write = false): Capability + { + return new class ($id, $write) implements Capability { + public function __construct(private readonly string $id, private readonly bool $write) + { + } + + public function id(): string + { + return $this->id; + } + + public function label(): string + { + return 'Fake ' . $this->id; + } + + public function isWrite(): bool + { + return $this->write; + } + }; + } + + public function testWorksForCapabilitiesOfAnotherIntegration(): void + { + $read = self::fakeCapability('pods-x'); + $write = self::fakeCapability('pods-y', true); + $stranger = self::fakeCapability('pods-z'); + + $matrix = CompatibilityMatrix::uniform([$read, $write], ReleaseState::Untested, ['offen']); + + self::assertSame(ReleaseState::Untested, $matrix->of($read)->state); + self::assertSame(['pods-x', 'pods-y'], array_column($matrix->toArray(), 'id')); + self::assertFalse($matrix->usable([$read, $write])); + self::assertSame( + [CompatibilityMatrix::MISSING], + $matrix->of($stranger)->problems, + 'a capability the matrix does not know is incompatible (fail closed)' + ); + self::assertSame(ReleaseState::Released, CompatibilityMatrix::allReleased([$read])->state()); + self::assertSame( + ReleaseState::Incompatible, + CompatibilityMatrix::allIncompatible([$read], 'kaputt')->of($read)->state + ); + } + + public function testTheSourceConstantsLiveOnTheMatrix(): void + { + self::assertSame('code', CompatibilityMatrix::SOURCE_CODE); + self::assertSame('database', CompatibilityMatrix::SOURCE_DATABASE); + self::assertSame('auto', CompatibilityMatrix::SOURCE_AUTO); + } + + public function testFromProblemsWeighsHardAndSoftProblemsPerCapability(): void + { + $a = self::fakeCapability('pods-a'); + $b = self::fakeCapability('pods-b'); + $c = self::fakeCapability('pods-c'); + $problems = [ + new Problem('hart', [$a], Problem::CALL), + new Problem('Signatur', [$b], Problem::SIGNATURE), + new Problem('Referenz', [$c], Problem::REFERENCE_MISSING), + ]; + + $released = CompatibilityMatrix::fromProblems([$a, $b, $c], $problems, CompatibilityMatrix::SOURCE_CODE, false); + self::assertSame(ReleaseState::Incompatible, $released->of($a)->state, 'hard locks even a released version'); + self::assertSame(['hart'], $released->of($a)->problems); + self::assertSame(CompatibilityMatrix::SOURCE_CODE, $released->of($b)->source); + + $manual = CompatibilityMatrix::fromProblems([$a, $b, $c], $problems, null, false); + self::assertSame(ReleaseState::Untested, $manual->of($b)->state); + self::assertSame(['Signatur'], $manual->of($b)->problems); + + $auto = CompatibilityMatrix::fromProblems([$a, $b, $c], $problems, null, true); + self::assertSame(ReleaseState::Incompatible, $auto->of($b)->state, 'changed signature'); + self::assertSame(ReleaseState::Untested, $auto->of($c)->state, 'reference missing'); + + $clean = CompatibilityMatrix::fromProblems([$a], [new Problem('b', [$b], Problem::CALL)], null, true); + self::assertSame(ReleaseState::Released, $clean->of($a)->state); + self::assertSame(CompatibilityMatrix::SOURCE_AUTO, $clean->of($a)->source); + + $everyone = CompatibilityMatrix::fromProblems([$a, $b], [new Problem('alle', [], Problem::CALL)], null, true); + self::assertSame([ReleaseState::Incompatible, ReleaseState::Incompatible], [ + $everyone->of($a)->state, + $everyone->of($b)->state, + ], 'a problem without capabilities concerns every one'); } } diff --git a/tests/Unit/Integrations/Divi/Release/FeatureStatusTest.php b/tests/Unit/Compatibility/FeatureStatusTest.php similarity index 87% rename from tests/Unit/Integrations/Divi/Release/FeatureStatusTest.php rename to tests/Unit/Compatibility/FeatureStatusTest.php index a2a356b..c9aef84 100644 --- a/tests/Unit/Integrations/Divi/Release/FeatureStatusTest.php +++ b/tests/Unit/Compatibility/FeatureStatusTest.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; +use LIT\MCP\Compatibility\FeatureStatus; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use PHPUnit\Framework\TestCase; final class FeatureStatusTest extends TestCase diff --git a/tests/Unit/Integrations/Divi/Release/LineDiffTest.php b/tests/Unit/Compatibility/LineDiffTest.php similarity index 90% rename from tests/Unit/Integrations/Divi/Release/LineDiffTest.php rename to tests/Unit/Compatibility/LineDiffTest.php index 6223726..ff9de74 100644 --- a/tests/Unit/Integrations/Divi/Release/LineDiffTest.php +++ b/tests/Unit/Compatibility/LineDiffTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; -use LIT\MCP\Integrations\Divi\Release\LineDiff; +use LIT\MCP\Compatibility\LineDiff; use PHPUnit\Framework\TestCase; final class LineDiffTest extends TestCase diff --git a/tests/Unit/Integrations/Divi/Release/ManifestCheckTest.php b/tests/Unit/Compatibility/ManifestCheckTest.php similarity index 71% rename from tests/Unit/Integrations/Divi/Release/ManifestCheckTest.php rename to tests/Unit/Compatibility/ManifestCheckTest.php index 046aa85..d13f83e 100644 --- a/tests/Unit/Integrations/Divi/Release/ManifestCheckTest.php +++ b/tests/Unit/Compatibility/ManifestCheckTest.php @@ -2,19 +2,22 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; use ArrayObject; use Brain\Monkey\Functions; -use LIT\MCP\Integrations\Divi\Release\Call; -use LIT\MCP\Integrations\Divi\Release\ConstantRequirement; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\ConstantRequirement; +use LIT\MCP\Compatibility\GlobalRequirement; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\ManifestCheck; +use LIT\MCP\Compatibility\Mirror; +use LIT\MCP\Compatibility\Problem; +use LIT\MCP\Compatibility\SignatureFingerprint; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\ManifestCheck; -use LIT\MCP\Integrations\Divi\Release\Mirror; -use LIT\MCP\Integrations\Divi\Release\Problem; -use LIT\MCP\Integrations\Divi\Release\SignatureFingerprint; use LIT\MCP\Tests\Unit\TestCase; +use LogicException; use RuntimeException; final class ManifestCheckTest extends TestCase @@ -23,7 +26,7 @@ final class ManifestCheckTest extends TestCase { $manifests = [ 'A' => new Manifest( - Manifest::PAGES, + DiviAreas::PAGES, calls: [ new Call('strlen', 1), new Call('lit_mcp_missing_function', 0), @@ -140,7 +143,7 @@ final class ManifestCheckTest extends TestCase private function featureCheck(?\Closure $current = null): ManifestCheck { $manifest = new Manifest( - Manifest::PAGES, + DiviAreas::PAGES, calls: [ new Call('strlen', 1), new Call('str_replace', 3, [DiviFeature::PageWrite]), @@ -232,4 +235,88 @@ final class ManifestCheckTest extends TestCase array_column(self::rows($check->problems()), 'label') ); } + + public function testWithoutSignaturesACallWithoutReferenceIsNoProblem(): void + { + $check = new ManifestCheck( + ['A' => new Manifest(DiviAreas::PAGES, calls: [new Call('strlen', 1), new Call('trim', 1)])], + [], + null, + null, + false + ); + + self::assertSame([], $check->codeProblems()); + } + + public function testWithoutSignaturesAMissingCallStillFails(): void + { + $check = new ManifestCheck( + ['A' => new Manifest(DiviAreas::PAGES, calls: [new Call('strlen', 2)])], + signatures: false + ); + + self::assertSame(['call'], array_column(self::rows($check->codeProblems()), 'kind')); + } + + public function testAMissingGlobalIsAHardProblemWithItsFeatures(): void + { + $GLOBALS['lit_mcp_present_global'] = 1; + $GLOBALS['lit_mcp_null_global'] = null; + $check = new ManifestCheck(['A' => new Manifest( + DiviAreas::PAGES, + globals: [ + new GlobalRequirement('lit_mcp_present_global'), + new GlobalRequirement('lit_mcp_null_global'), + new GlobalRequirement('lit_mcp_missing_global', [DiviFeature::Catalog]), + ], + features: [DiviFeature::PageRead] + )], signatures: false); + + try { + $problems = $check->problems(); + $failed = ManifestCheck::failed($check->codeChecks()); + } finally { + unset($GLOBALS['lit_mcp_present_global'], $GLOBALS['lit_mcp_null_global']); + } + + self::assertSame( + [ + ['label' => 'Seiten: Globale Variable lit_mcp_null_global', 'kind' => 'global', + 'features' => ['page-read']], + ['label' => 'Seiten: Globale Variable lit_mcp_missing_global', 'kind' => 'global', + 'features' => ['catalog']], + ], + self::rows($problems) + ); + self::assertTrue($problems[0]->isHard()); + self::assertSame( + ['Seiten: Globale Variable lit_mcp_null_global', 'Seiten: Globale Variable lit_mcp_missing_global'], + $failed + ); + } + + public function testWithoutCurrentClosureEveryMirrorIsUnreadable(): void + { + $check = new ManifestCheck(['A' => new Manifest( + DiviAreas::PAGES, + mirrors: [new Mirror('same', 'a.php', 'a', null, 'gleich')] + )], ['same' => 'h1']); + + self::assertSame(['mirror'], array_column(self::rows($check->mirrorProblems()), 'kind')); + } + + public function testBlockTypesWithoutKnowsBlockClosureRefuseToBuildTheCheck(): void + { + $this->expectException(LogicException::class); + + new ManifestCheck(['A' => new Manifest(DiviAreas::PAGES, blockTypes: ['divi/x'])]); + } + + public function testWithoutBlockTypesNoKnowsBlockClosureIsNeeded(): void + { + $check = new ManifestCheck(['A' => new Manifest(DiviAreas::PAGES, calls: [new Call('strlen', 1)])]); + + self::assertSame([], $check->registrationChecks()); + } } diff --git a/tests/Unit/Integrations/Divi/Release/ManifestTest.php b/tests/Unit/Compatibility/ManifestTest.php similarity index 82% rename from tests/Unit/Integrations/Divi/Release/ManifestTest.php rename to tests/Unit/Compatibility/ManifestTest.php index 8742180..2636254 100644 --- a/tests/Unit/Integrations/Divi/Release/ManifestTest.php +++ b/tests/Unit/Compatibility/ManifestTest.php @@ -2,13 +2,15 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; -use LIT\MCP\Integrations\Divi\Release\Call; -use LIT\MCP\Integrations\Divi\Release\ConstantRequirement; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\ConstantRequirement; +use LIT\MCP\Compatibility\GlobalRequirement; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\Mirror; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\Mirror; use PHPUnit\Framework\TestCase; final class ManifestTest extends TestCase @@ -39,7 +41,7 @@ final class ManifestTest extends TestCase public function testNamesListEveryDeclaredDiviName(): void { $manifest = new Manifest( - Manifest::LIBRARY, + DiviAreas::LIBRARY, calls: [new Call('et_pb_is_allowed', 1), new Call('ET\Builder\GlobalLayout::is_global_layout_template', 1)], constants: ['ET_BUILDER_PRODUCT_VERSION'], postTypes: ['et_pb_layout'], @@ -59,7 +61,7 @@ final class ManifestTest extends TestCase public function testPlainConstantNamesBecomeRequirementsWithALabel(): void { $manifest = new Manifest( - Manifest::CATALOG, + DiviAreas::CATALOG, constants: ['ET_BUILDER_PRODUCT_VERSION', new ConstantRequirement('ET_OTHER', [DiviFeature::Roles])] ); @@ -77,7 +79,7 @@ final class ManifestTest extends TestCase { $call = new Call('et_pb_is_allowed', 1); $mirror = new Mirror('m', 'a.php', 'a', null, 'Terme'); - $manifest = new Manifest(Manifest::LIBRARY, calls: [$call], constants: ['ET_X'], mirrors: [$mirror]); + $manifest = new Manifest(DiviAreas::LIBRARY, calls: [$call], constants: ['ET_X'], mirrors: [$mirror]); $requirements = $manifest->requirements(); @@ -93,7 +95,7 @@ final class ManifestTest extends TestCase $inherited = new Call('et_b'); $mirror = new Mirror('m', 'a.php', 'a', null, 'x', [DiviFeature::LibraryWrite]); $manifest = new Manifest( - Manifest::PAGES, + DiviAreas::PAGES, calls: [$own, $inherited], constants: ['ET_X'], mirrors: [$mirror], @@ -110,6 +112,14 @@ final class ManifestTest extends TestCase { $call = new Call('et_a'); - self::assertSame([], (new Manifest(Manifest::PAGES, calls: [$call]))->featuresOf($call)); + self::assertSame([], (new Manifest(DiviAreas::PAGES, calls: [$call]))->featuresOf($call)); + } + + public function testGlobalsAreRequirementsAndNames(): void + { + $manifest = new Manifest(DiviAreas::PAGES, globals: [new GlobalRequirement('wp_cache_clear_cache')]); + + self::assertSame('wp_cache_clear_cache', $manifest->names()[0]); + self::assertSame('Globale Variable wp_cache_clear_cache', $manifest->requirements()[0]->label()); } } diff --git a/tests/Unit/Integrations/Divi/Release/ProblemTest.php b/tests/Unit/Compatibility/ProblemTest.php similarity index 63% rename from tests/Unit/Integrations/Divi/Release/ProblemTest.php rename to tests/Unit/Compatibility/ProblemTest.php index 78020ce..3dfa799 100644 --- a/tests/Unit/Integrations/Divi/Release/ProblemTest.php +++ b/tests/Unit/Compatibility/ProblemTest.php @@ -2,10 +2,11 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; +use LIT\MCP\Compatibility\Capability; +use LIT\MCP\Compatibility\Problem; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Problem; use PHPUnit\Framework\TestCase; final class ProblemTest extends TestCase @@ -44,7 +45,7 @@ final class ProblemTest extends TestCase ['label' => 'Seiten: Nachbau von a() – x', 'kind' => 'mirror', 'features' => ['page-write']], $problem->toArray() ); - self::assertEquals($problem, Problem::fromArray($problem->toArray())); + self::assertEquals($problem, Problem::fromArray($problem->toArray(), DiviFeature::tryFrom(...))); } public function testFromArrayRefusesAnythingElse(): void @@ -56,7 +57,38 @@ final class ProblemTest extends TestCase 'unknown feature' => ['label' => 'x', 'kind' => 'call', 'features' => ['page-delete']], 'features no list' => ['label' => 'x', 'kind' => 'call', 'features' => 'page-write'], ] as $case => $row) { - self::assertNull(Problem::fromArray($row), $case); + self::assertNull(Problem::fromArray($row, DiviFeature::tryFrom(...)), $case); } } + + public function testFromArrayResolvesFeaturesThroughTheLookupClosure(): void + { + $pods = new class implements Capability { + public function id(): string + { + return 'pods-x'; + } + + public function label(): string + { + return 'Pods X'; + } + + public function isWrite(): bool + { + return false; + } + }; + $lookup = static fn (string $id): ?Capability => $id === 'pods-x' ? $pods : null; + + $problem = Problem::fromArray(['label' => 'x', 'kind' => 'call', 'features' => ['pods-x']], $lookup); + self::assertNotNull($problem); + self::assertSame([$pods], $problem->features); + self::assertTrue($problem->concerns($pods)); + + self::assertNull( + Problem::fromArray(['label' => 'x', 'kind' => 'call', 'features' => ['pods-x', 'page-read']], $lookup), + 'one unknown ID discards the problem' + ); + } } diff --git a/tests/Unit/Integrations/Divi/Release/SignatureFingerprintTest.php b/tests/Unit/Compatibility/SignatureFingerprintTest.php similarity index 96% rename from tests/Unit/Integrations/Divi/Release/SignatureFingerprintTest.php rename to tests/Unit/Compatibility/SignatureFingerprintTest.php index 17336d4..7d3096e 100644 --- a/tests/Unit/Integrations/Divi/Release/SignatureFingerprintTest.php +++ b/tests/Unit/Compatibility/SignatureFingerprintTest.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; use ArrayObject; -use LIT\MCP\Integrations\Divi\Release\Call; -use LIT\MCP\Integrations\Divi\Release\SignatureFingerprint; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\SignatureFingerprint; use PHPUnit\Framework\TestCase; /** Stand-in for a Divi class: one baseline method and one variant per property the fingerprint covers. */ @@ -191,7 +191,7 @@ final class SignatureFingerprintTest extends TestCase public function testTheClassNeverIncludesAFile(): void { $source = (string) file_get_contents( - dirname(__DIR__, 5) . '/src/Integrations/Divi/Release/SignatureFingerprint.php' + dirname(__DIR__, 3) . '/src/Compatibility/SignatureFingerprint.php' ); $forbidden = [T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, T_EVAL]; diff --git a/tests/Unit/Integrations/Divi/Release/SourceFingerprintTest.php b/tests/Unit/Compatibility/SourceFingerprintTest.php similarity index 97% rename from tests/Unit/Integrations/Divi/Release/SourceFingerprintTest.php rename to tests/Unit/Compatibility/SourceFingerprintTest.php index 51d5b08..08e026d 100644 --- a/tests/Unit/Integrations/Divi/Release/SourceFingerprintTest.php +++ b/tests/Unit/Compatibility/SourceFingerprintTest.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +namespace LIT\MCP\Tests\Unit\Compatibility; -use LIT\MCP\Integrations\Divi\Release\SourceFingerprint; +use LIT\MCP\Compatibility\SourceFingerprint; use PHPUnit\Framework\TestCase; use RuntimeException; diff --git a/tests/Unit/Integrations/Divi/Admin/DiviTabTest.php b/tests/Unit/Integrations/Divi/Admin/DiviTabTest.php index 6f14226..398daef 100644 --- a/tests/Unit/Integrations/Divi/Admin/DiviTabTest.php +++ b/tests/Unit/Integrations/Divi/Admin/DiviTabTest.php @@ -7,15 +7,15 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Admin; use Brain\Monkey\Actions; use Brain\Monkey\Functions; use LIT\MCP\Admin\AdminPostAction; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Admin\DiviTab; 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\DiviFeature; use LIT\MCP\Integrations\Divi\Release\FeatureOverview; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; +use LIT\MCP\Compatibility\FeatureStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseStore; use LIT\MCP\Integrations\Divi\Release\WriteProbe; diff --git a/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php b/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php index ba18abc..d0b12c9 100644 --- a/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php +++ b/tests/Unit/Integrations/Divi/Cli/DiviCheckCommandTest.php @@ -5,17 +5,17 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Cli; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\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\CompatibilityMatrix; +use LIT\MCP\Compatibility\CompatibilityMatrix; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviReference; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use LIT\MCP\Integrations\Divi\Release\ReleaseResolver; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Tests\Support\MatrixFixtures; use LIT\MCP\Tests\Support\ReleaseMatrices; diff --git a/tests/Unit/Integrations/Divi/Design/DesignChangeTest.php b/tests/Unit/Integrations/Divi/Design/DesignChangeTest.php index abc0a73..46307a5 100644 --- a/tests/Unit/Integrations/Divi/Design/DesignChangeTest.php +++ b/tests/Unit/Integrations/Divi/Design/DesignChangeTest.php @@ -14,7 +14,7 @@ use LIT\MCP\Integrations\Divi\Library\UsageReport; 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\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Abilities\WriteFailed; use LIT\MCP\Tests\Support\FakeChangeLog; diff --git a/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php b/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php index 68680d4..2f14e79 100644 --- a/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php +++ b/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php @@ -6,9 +6,9 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; use LIT\MCP\Integrations\Divi\Design\DesignSystemReader; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Tests\Unit\TestCase; final class DesignSystemReaderTest extends TestCase @@ -18,7 +18,7 @@ final class DesignSystemReaderTest extends TestCase $manifest = DesignSystemReader::manifest(); self::assertContains(DesignSystemReader::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( ['GlobalData::get_global_colors()', 'GlobalData::get_global_variables()', 'GlobalPreset::get_data()'], diff --git a/tests/Unit/Integrations/Divi/Design/DesignSystemWriterTest.php b/tests/Unit/Integrations/Divi/Design/DesignSystemWriterTest.php index 259c88d..80cc1b4 100644 --- a/tests/Unit/Integrations/Divi/Design/DesignSystemWriterTest.php +++ b/tests/Unit/Integrations/Divi/Design/DesignSystemWriterTest.php @@ -4,13 +4,13 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; 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; @@ -22,7 +22,7 @@ final class DesignSystemWriterTest extends TestCase $manifest = DesignSystemWriter::manifest(); self::assertContains(DesignSystemWriter::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( [ 'GlobalPreset::get_data()', 'GlobalPreset::prepare_data()', 'GlobalPreset::validate_preset_count()', diff --git a/tests/Unit/Integrations/Divi/Design/DiviDesignDetachAbilityTest.php b/tests/Unit/Integrations/Divi/Design/DiviDesignDetachAbilityTest.php index f162759..c52ba37 100644 --- a/tests/Unit/Integrations/Divi/Design/DiviDesignDetachAbilityTest.php +++ b/tests/Unit/Integrations/Divi/Design/DiviDesignDetachAbilityTest.php @@ -8,6 +8,7 @@ use Brain\Monkey\Functions; use Closure; use LIT\MCP\Abilities\Errors; use LIT\MCP\Abilities\WriteFailed; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\BlockTree\Block; use LIT\MCP\Integrations\Divi\Design\DesignChange; use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter; @@ -24,7 +25,6 @@ use LIT\MCP\Integrations\Divi\Library\GlobalUsages; use LIT\MCP\Integrations\Divi\Library\UsageReport; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Tests\Support\FakeChangeLog; use LIT\MCP\Tests\Support\LockTable; use LIT\MCP\Tests\Support\ReleaseMatrices; diff --git a/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php b/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php index 3fcc610..cd19d83 100644 --- a/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php +++ b/tests/Unit/Integrations/Divi/Design/DiviFontsUploadAbilityTest.php @@ -7,12 +7,12 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; use LIT\MCP\Abilities\Errors; use LIT\MCP\Abilities\WriteFailed; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\DiviFontsUploadAbility; use LIT\MCP\Integrations\Divi\Design\FontCatalog; use LIT\MCP\Integrations\Divi\Design\FontInstaller; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Media\RemoteFileFetcher; use LIT\MCP\Limits; use LIT\MCP\Tests\Support\FakeChangeLog; diff --git a/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php b/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php index f35dcf6..6d355d7 100644 --- a/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php +++ b/tests/Unit/Integrations/Divi/Design/FontCatalogTest.php @@ -6,10 +6,10 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; use LIT\MCP\Integrations\Divi\Design\FontCatalog; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\Manifest; use LIT\MCP\Tests\Unit\TestCase; /** @@ -24,7 +24,7 @@ final class FontCatalogTest extends TestCase $manifest = FontCatalog::manifest(); self::assertContains(FontCatalog::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( [ 'et_builder_get_fonts()', 'et_builder_get_custom_fonts()', 'et_builder_get_google_fonts()', diff --git a/tests/Unit/Integrations/Divi/Design/FontInstallerTest.php b/tests/Unit/Integrations/Divi/Design/FontInstallerTest.php index e59b616..3a535de 100644 --- a/tests/Unit/Integrations/Divi/Design/FontInstallerTest.php +++ b/tests/Unit/Integrations/Divi/Design/FontInstallerTest.php @@ -5,15 +5,15 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\FontInstaller; -use LIT\MCP\Integrations\Divi\Release\Call; -use LIT\MCP\Integrations\Divi\Release\ConstantRequirement; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\ConstantRequirement; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; 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; @@ -65,7 +65,7 @@ final class FontInstallerTest extends TestCase $manifest = FontInstaller::manifest(); self::assertContains(FontInstaller::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( ['FontsUtility::mime_types_font()', 'HooksRegistration::check_filetype_and_ext_font()', 'FontsUtility::font_add()', 'FontsUtility::font_remove()'], diff --git a/tests/Unit/Integrations/Divi/Design/GlobalColorsAdapterTest.php b/tests/Unit/Integrations/Divi/Design/GlobalColorsAdapterTest.php index 0396f3e..09e40d1 100644 --- a/tests/Unit/Integrations/Divi/Design/GlobalColorsAdapterTest.php +++ b/tests/Unit/Integrations/Divi/Design/GlobalColorsAdapterTest.php @@ -5,14 +5,14 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\GlobalColorsAdapter; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; 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; @@ -32,7 +32,7 @@ final class GlobalColorsAdapterTest extends TestCase $manifest = GlobalColorsAdapter::manifest(); self::assertContains(GlobalColorsAdapter::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( ['GlobalData::get_global_colors()', 'GlobalData::sanitize_global_colors_data()', 'GlobalData::set_global_colors()'], diff --git a/tests/Unit/Integrations/Divi/Design/GlobalVariablesAdapterTest.php b/tests/Unit/Integrations/Divi/Design/GlobalVariablesAdapterTest.php index ef5d23a..5301f09 100644 --- a/tests/Unit/Integrations/Divi/Design/GlobalVariablesAdapterTest.php +++ b/tests/Unit/Integrations/Divi/Design/GlobalVariablesAdapterTest.php @@ -5,14 +5,14 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\GlobalVariablesAdapter; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; 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; @@ -33,7 +33,7 @@ final class GlobalVariablesAdapterTest extends TestCase $manifest = GlobalVariablesAdapter::manifest(); self::assertContains(GlobalVariablesAdapter::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( ['GlobalData::get_global_variables()', 'GlobalData::set_global_variables()', 'et_pb_is_allowed()'], array_map(static fn (Call $call): string => $call->label(), $manifest->calls) diff --git a/tests/Unit/Integrations/Divi/Design/PageRewriterTest.php b/tests/Unit/Integrations/Divi/Design/PageRewriterTest.php index 1075b97..f12f660 100644 --- a/tests/Unit/Integrations/Divi/Design/PageRewriterTest.php +++ b/tests/Unit/Integrations/Divi/Design/PageRewriterTest.php @@ -21,7 +21,7 @@ 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\ReleaseState; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Tests\Support\FakeChangeLog; use LIT\MCP\Tests\Support\LibraryFixtures; use LIT\MCP\Tests\Support\ReleaseMatrices; diff --git a/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php b/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php index 16fa270..5016db5 100644 --- a/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php +++ b/tests/Unit/Integrations/Divi/Design/ThemeOptionsAdapterTest.php @@ -6,14 +6,14 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design; use Brain\Monkey\Functions; use InvalidArgumentException; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\ThemeOptionsAdapter; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\DiviLocked; -use LIT\MCP\Integrations\Divi\Release\Manifest; 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; @@ -40,7 +40,7 @@ final class ThemeOptionsAdapterTest extends TestCase $manifest = ThemeOptionsAdapter::manifest(); self::assertContains(ThemeOptionsAdapter::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::DESIGN, $manifest->area); + self::assertSame(DiviAreas::DESIGN, $manifest->area); self::assertSame( [ 'et_sanitize_font_choices()', 'et_sanitize_global_font_weight()', 'et_sanitize_float_number()', diff --git a/tests/Unit/Integrations/Divi/DiviCompatibilityTest.php b/tests/Unit/Integrations/Divi/DiviCompatibilityTest.php index b3ea59f..729f5c3 100644 --- a/tests/Unit/Integrations/Divi/DiviCompatibilityTest.php +++ b/tests/Unit/Integrations/Divi/DiviCompatibilityTest.php @@ -5,10 +5,11 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi; use LIT\MCP\Integrations\Divi\DiviCompatibility; -use LIT\MCP\Integrations\Divi\Release\Call; -use LIT\MCP\Integrations\Divi\Release\Manifest; -use LIT\MCP\Integrations\Divi\Release\ManifestCheck; -use LIT\MCP\Integrations\Divi\Release\Mirror; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; +use LIT\MCP\Compatibility\Manifest; +use LIT\MCP\Compatibility\ManifestCheck; +use LIT\MCP\Compatibility\Mirror; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -31,7 +32,7 @@ final class DiviCompatibilityTest extends TestCase { $check = new ManifestCheck( ['X' => new Manifest( - Manifest::LIBRARY, + DiviAreas::LIBRARY, calls: [new Call('strlen', 1)], postTypes: ['et_pb_layout'], mirrors: [new Mirror('m', 'a.php', 'a', null, 'Terme')] @@ -51,7 +52,7 @@ final class DiviCompatibilityTest extends TestCase { $labels = implode("\n", array_keys(DiviCompatibility::defaults())); - $areas = [Manifest::PAGES, Manifest::LIBRARY, Manifest::THEME_BUILDER, Manifest::DESIGN, Manifest::CATALOG]; + $areas = [DiviAreas::PAGES, DiviAreas::LIBRARY, DiviAreas::THEME_BUILDER, DiviAreas::DESIGN, DiviAreas::CATALOG]; foreach ($areas as $area) { self::assertStringContainsString($area . ': ', $labels); } diff --git a/tests/Unit/Integrations/Divi/DiviEnvironmentComponentTest.php b/tests/Unit/Integrations/Divi/DiviEnvironmentComponentTest.php index 623c1b2..f6f0dae 100644 --- a/tests/Unit/Integrations/Divi/DiviEnvironmentComponentTest.php +++ b/tests/Unit/Integrations/Divi/DiviEnvironmentComponentTest.php @@ -5,13 +5,13 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Core\EnvironmentComponent; use LIT\MCP\Integrations\Divi\DiviCompatibility; use LIT\MCP\Integrations\Divi\DiviEnvironmentComponent; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\FeatureOverview; 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\FeatureFakeTool; use LIT\MCP\Tests\Support\MatrixFixtures; diff --git a/tests/Unit/Integrations/Divi/Library/DiviLibraryStoreTest.php b/tests/Unit/Integrations/Divi/Library/DiviLibraryStoreTest.php index 8490011..ac71785 100644 --- a/tests/Unit/Integrations/Divi/Library/DiviLibraryStoreTest.php +++ b/tests/Unit/Integrations/Divi/Library/DiviLibraryStoreTest.php @@ -9,7 +9,7 @@ use LIT\MCP\Abilities\WriteFailed; use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore; use LIT\MCP\Integrations\Divi\Library\LibraryItemSpec; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\WordPress\Content\NewDraft; use LIT\MCP\Tests\Support\FakeChangeLog; use LIT\MCP\Tests\Unit\TestCase; @@ -121,7 +121,7 @@ final class DiviLibraryStoreTest extends TestCase $manifest = DiviLibraryStore::manifest(); self::assertContains(DiviLibraryStore::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::LIBRARY, $manifest->area); + self::assertSame(DiviAreas::LIBRARY, $manifest->area); self::assertSame(['et_pb_layout'], $manifest->postTypes); self::assertSame( ['scope', 'layout_type', 'module_width', 'layout_category', 'layout_tag'], diff --git a/tests/Unit/Integrations/Divi/Pages/DiviContentComposerTest.php b/tests/Unit/Integrations/Divi/Pages/DiviContentComposerTest.php index 09848a7..6821661 100644 --- a/tests/Unit/Integrations/Divi/Pages/DiviContentComposerTest.php +++ b/tests/Unit/Integrations/Divi/Pages/DiviContentComposerTest.php @@ -4,13 +4,13 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\BlockTree\Parser; use LIT\MCP\Integrations\Divi\Compose\VariableRef; use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer; use LIT\MCP\Integrations\Divi\Pages\DiviSaver; use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Tests\Support\DiviFeatureFixtures; use LIT\MCP\Tests\Support\DiviFixtures; use LIT\MCP\Tests\Support\DiviWriterFixture; diff --git a/tests/Unit/Integrations/Divi/Pages/DiviPagesTest.php b/tests/Unit/Integrations/Divi/Pages/DiviPagesTest.php index 33da355..7e8f454 100644 --- a/tests/Unit/Integrations/Divi/Pages/DiviPagesTest.php +++ b/tests/Unit/Integrations/Divi/Pages/DiviPagesTest.php @@ -7,7 +7,7 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages; use Brain\Monkey\Functions; use LIT\MCP\Integrations\Divi\Pages\DiviPages; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Tests\Unit\TestCase; use RuntimeException; use WP_Post; @@ -63,7 +63,7 @@ final class DiviPagesTest extends TestCase $manifest = DiviPages::manifest(); self::assertContains(DiviPages::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::PAGES, $manifest->area); + self::assertSame(DiviAreas::PAGES, $manifest->area); self::assertSame(['vb-sync-flags'], array_map(static fn ($mirror): string => $mirror->id, $manifest->mirrors)); self::assertSame('SyncToServerController::update()', $manifest->mirrors[0]->label()); } diff --git a/tests/Unit/Integrations/Divi/Release/CompatibilityCheckTest.php b/tests/Unit/Integrations/Divi/Release/CompatibilityCheckTest.php index 37d4671..12bf27a 100644 --- a/tests/Unit/Integrations/Divi/Release/CompatibilityCheckTest.php +++ b/tests/Unit/Integrations/Divi/Release/CompatibilityCheckTest.php @@ -8,7 +8,7 @@ use Brain\Monkey\Functions; use LIT\MCP\Integrations\Divi\Release\CheckStep; use LIT\MCP\Integrations\Divi\Release\CompatibilityCheck; use LIT\MCP\Integrations\Divi\Release\FormatCheck; -use LIT\MCP\Integrations\Divi\Release\ManifestCheck; +use LIT\MCP\Compatibility\ManifestCheck; use LIT\MCP\Integrations\Divi\Release\WriteProbe; use LIT\MCP\Plugin; use LIT\MCP\Tests\Unit\TestCase; diff --git a/tests/Unit/Integrations/Divi/Release/DiviAreasTest.php b/tests/Unit/Integrations/Divi/Release/DiviAreasTest.php new file mode 100644 index 0000000..74e540b --- /dev/null +++ b/tests/Unit/Integrations/Divi/Release/DiviAreasTest.php @@ -0,0 +1,20 @@ +calls, ...$manifest->constants] as $requirement) { + $code[] = $manifest->area . ': ' . $requirement->label(); + } + $mirrors = [...$mirrors, ...array_map(static fn ($mirror): string => $mirror->id, $manifest->mirrors)]; + } + $check = DiviManifestCheck::create(); + + self::assertNotSame([], $code); + self::assertSame(array_values(array_unique($code)), array_keys($check->codeChecks())); + self::assertCount(count(array_unique($mirrors)), $check->mirrorChecks()); + self::assertNotSame([], $check->registrationChecks(), 'DiviModules::knowsBlock is wired for block types'); + } + + public function testCreateLoadsTheSignatureReferences(): void + { + $kinds = array_map(static fn ($problem): string => $problem->kind, DiviManifestCheck::create()->codeProblems()); + + self::assertNotContains('reference-missing', $kinds); + } +} diff --git a/tests/Unit/Integrations/Divi/Release/DiviReferenceTest.php b/tests/Unit/Integrations/Divi/Release/DiviReferenceTest.php index f7af2af..32b4d77 100644 --- a/tests/Unit/Integrations/Divi/Release/DiviReferenceTest.php +++ b/tests/Unit/Integrations/Divi/Release/DiviReferenceTest.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\SignatureFingerprint; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; use LIT\MCP\Integrations\Divi\Release\DiviReference; -use LIT\MCP\Integrations\Divi\Release\SignatureFingerprint; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -19,7 +19,7 @@ final class DiviReferenceTest extends TestCase $signatureKeys = []; foreach (DiviAdapters::manifests() as $manifest) { foreach ($manifest->calls as $call) { - $signatureKeys[] = 'sig:' . ltrim($call->symbol, '\\'); + $signatureKeys[] = $call->signatureKey(); } } $keys = array_values(array_unique(array_merge($mirrorIds, $signatureKeys))); @@ -40,10 +40,10 @@ final class DiviReferenceTest extends TestCase foreach (DiviAdapters::mirrors() as $mirror) { self::assertStringStartsNotWith(DiviReference::SIGNATURE_PREFIX, $mirror->id); } - self::assertSame('sig:et_pb_is_allowed', DiviReference::signatureKey(new Call('et_pb_is_allowed', 1))); + self::assertSame('sig:et_pb_is_allowed', (new Call('et_pb_is_allowed', 1))->signatureKey()); self::assertSame( 'sig:ET_Core_PageResource::remove_static_resources', - DiviReference::signatureKey(new Call('\\ET_Core_PageResource::remove_static_resources', 3)) + (new Call('\\ET_Core_PageResource::remove_static_resources', 3))->signatureKey() ); } diff --git a/tests/Unit/Integrations/Divi/Release/FeatureOverviewTest.php b/tests/Unit/Integrations/Divi/Release/FeatureOverviewTest.php index 33ae2b3..ebb823e 100644 --- a/tests/Unit/Integrations/Divi/Release/FeatureOverviewTest.php +++ b/tests/Unit/Integrations/Divi/Release/FeatureOverviewTest.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\FeatureOverview; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Tests\Support\FeatureFakeTool; use LIT\MCP\Tests\Support\MatrixFixtures; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Integrations/Divi/Release/ReleaseGateTest.php b/tests/Unit/Integrations/Divi/Release/ReleaseGateTest.php index fe5e79e..c0a99b3 100644 --- a/tests/Unit/Integrations/Divi/Release/ReleaseGateTest.php +++ b/tests/Unit/Integrations/Divi/Release/ReleaseGateTest.php @@ -6,11 +6,13 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; use Brain\Monkey\Functions; use LIT\MCP\Abilities\WriteFailed; +use LIT\MCP\Compatibility\Capability; +use LIT\MCP\Compatibility\CompatibilityMatrix; +use LIT\MCP\Compatibility\ReleaseState; 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\ReleaseResolver; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Tests\Support\DiviFeatureFixtures; use LIT\MCP\Tests\Support\ReleaseMatrices; @@ -43,6 +45,37 @@ final class ReleaseGateTest extends TestCase self::assertSame('5.3.0', $lock->status->version); } + public function testALockWhoseLockedCapabilityIsNoDiviFeatureStillRefusesTheFeatureAsked(): void + { + $foreign = new class () implements Capability { + public function id(): string + { + return DiviFeature::PageWrite->id(); + } + + public function label(): string + { + return 'fremde Fähigkeit'; + } + + public function isWrite(): bool + { + return true; + } + }; + ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseStatus::of( + '5.3.0', + CompatibilityMatrix::uniform([$foreign], ReleaseState::Incompatible, ['fremde Fähigkeit']) + )); + + try { + ReleaseGate::assertFeature(DiviFeature::PageWrite); + self::fail('a locked feature must refuse'); + } catch (DiviLocked $locked) { + self::assertSame([DiviFeature::PageWrite], $locked->features); + } + } + public function testAssertFeatureRefusesOnlyALockedFeature(): void { ReleaseGate::useResolver(static fn (): ReleaseStatus => ReleaseMatrices::status( diff --git a/tests/Unit/Integrations/Divi/Release/ReleaseGuardsTest.php b/tests/Unit/Integrations/Divi/Release/ReleaseGuardsTest.php index 9a3203f..3cec9bc 100644 --- a/tests/Unit/Integrations/Divi/Release/ReleaseGuardsTest.php +++ b/tests/Unit/Integrations/Divi/Release/ReleaseGuardsTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter; use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore; use LIT\MCP\Integrations\Divi\Library\LibraryItemSpec; @@ -14,7 +15,6 @@ 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; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder; use LIT\MCP\Integrations\WordPress\Content\NewDraft; diff --git a/tests/Unit/Integrations/Divi/Release/ReleaseResolverTest.php b/tests/Unit/Integrations/Divi/Release/ReleaseResolverTest.php index 77bd61d..5ebbc4c 100644 --- a/tests/Unit/Integrations/Divi/Release/ReleaseResolverTest.php +++ b/tests/Unit/Integrations/Divi/Release/ReleaseResolverTest.php @@ -6,11 +6,11 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; use Brain\Monkey\Filters; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\ManifestCheck; -use LIT\MCP\Integrations\Divi\Release\Problem; +use LIT\MCP\Compatibility\ManifestCheck; +use LIT\MCP\Compatibility\Problem; use LIT\MCP\Integrations\Divi\Release\ReleaseResolver; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseStore; use LIT\MCP\Plugin; diff --git a/tests/Unit/Integrations/Divi/Release/ReleaseStatusTest.php b/tests/Unit/Integrations/Divi/Release/ReleaseStatusTest.php index cb04320..202631e 100644 --- a/tests/Unit/Integrations/Divi/Release/ReleaseStatusTest.php +++ b/tests/Unit/Integrations/Divi/Release/ReleaseStatusTest.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; -use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix; +use LIT\MCP\Compatibility\ReleaseState; +use LIT\MCP\Compatibility\CompatibilityMatrix; use LIT\MCP\Integrations\Divi\Release\DiviFeature; -use LIT\MCP\Integrations\Divi\Release\FeatureStatus; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; +use LIT\MCP\Compatibility\FeatureStatus; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Tests\Support\ReleaseMatrices; use LIT\MCP\Tests\Unit\TestCase; @@ -57,14 +57,14 @@ final class ReleaseStatusTest extends TestCase self::assertNull($status->source); self::assertSame(['Seiten: et_x() – Signatur geändert'], $status->problems); self::assertSame(['user_id' => 1], $status->release); - self::assertSame('auto', ReleaseStatus::of('5.99.0', CompatibilityMatrix::allReleased('auto'))->source); + self::assertSame('auto', ReleaseStatus::of('5.99.0', CompatibilityMatrix::allReleased(DiviFeature::cases(), 'auto'))->source); } public function testAMatrixInAnotherStateThanTheStatusIsRefused(): void { $this->expectException(LogicException::class); - new ReleaseStatus(ReleaseState::Released, '5.2.1', 'code', [], null, CompatibilityMatrix::allIncompatible('x')); + new ReleaseStatus(ReleaseState::Released, '5.2.1', 'code', [], null, CompatibilityMatrix::allIncompatible(DiviFeature::cases(), 'x')); } public function testRefusalForAnUntestedFeatureNamesItAndKeepsTheOthersWorking(): void @@ -101,7 +101,7 @@ final class ReleaseStatusTest extends TestCase $features[$feature->value] = $matrix->of($feature); } $features['library-write'] = new FeatureStatus(DiviFeature::LibraryWrite, ReleaseState::Untested); - $status = ReleaseStatus::of('5.99.0', new CompatibilityMatrix($features)); + $status = ReleaseStatus::of('5.99.0', new CompatibilityMatrix(DiviFeature::cases(), $features)); $payload = self::assertToolError( $status->refusalFor([DiviFeature::PageWrite, DiviFeature::LibraryWrite, DiviFeature::ThemeBuilderWrite]), diff --git a/tests/Unit/Integrations/Divi/Release/WriteProbeTest.php b/tests/Unit/Integrations/Divi/Release/WriteProbeTest.php index e364b6a..8e23214 100644 --- a/tests/Unit/Integrations/Divi/Release/WriteProbeTest.php +++ b/tests/Unit/Integrations/Divi/Release/WriteProbeTest.php @@ -5,9 +5,9 @@ declare(strict_types=1); namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release; use Brain\Monkey\Functions; +use LIT\MCP\Compatibility\ReleaseState; use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; -use LIT\MCP\Integrations\Divi\Release\ReleaseState; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Integrations\Divi\Release\WriteProbe; use LIT\MCP\Integrations\WordPress\Content\ContentComposer; diff --git a/tests/Unit/Integrations/Divi/Schema/DiviModulesTest.php b/tests/Unit/Integrations/Divi/Schema/DiviModulesTest.php index a0501a7..ccbb269 100644 --- a/tests/Unit/Integrations/Divi/Schema/DiviModulesTest.php +++ b/tests/Unit/Integrations/Divi/Schema/DiviModulesTest.php @@ -6,10 +6,10 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Schema; use Brain\Monkey\Functions; use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap; -use LIT\MCP\Integrations\Divi\Release\Call; -use LIT\MCP\Integrations\Divi\Release\ConstantRequirement; +use LIT\MCP\Compatibility\Call; +use LIT\MCP\Compatibility\ConstantRequirement; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\Schema\DiviModules; use LIT\MCP\Tests\Unit\TestCase; @@ -19,8 +19,8 @@ final class DiviModulesTest extends TestCase { self::assertContains(DiviModules::class, DiviAdapters::CLASSES); self::assertContains(DynamicContentMap::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::CATALOG, DiviModules::manifest()->area); - self::assertSame(Manifest::CATALOG, DynamicContentMap::manifest()->area); + self::assertSame(DiviAreas::CATALOG, DiviModules::manifest()->area); + self::assertSame(DiviAreas::CATALOG, DynamicContentMap::manifest()->area); } public function testManifestListsModuleRegistrationAndTheBuilderSwitch(): void diff --git a/tests/Unit/Integrations/Divi/ThemeBuilder/DiviThemeBuilderTest.php b/tests/Unit/Integrations/Divi/ThemeBuilder/DiviThemeBuilderTest.php index 38a4270..6fb7224 100644 --- a/tests/Unit/Integrations/Divi/ThemeBuilder/DiviThemeBuilderTest.php +++ b/tests/Unit/Integrations/Divi/ThemeBuilder/DiviThemeBuilderTest.php @@ -7,9 +7,9 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\ThemeBuilder; use Brain\Monkey\Actions; use Brain\Monkey\Filters; use Brain\Monkey\Functions; -use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Compatibility\Call; use LIT\MCP\Integrations\Divi\Release\DiviAdapters; -use LIT\MCP\Integrations\Divi\Release\Manifest; +use LIT\MCP\Integrations\Divi\Release\DiviAreas; use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder; use LIT\MCP\Integrations\Divi\ThemeBuilder\InsertedBeforeFailure; use LIT\MCP\Tests\Unit\TestCase; @@ -25,7 +25,7 @@ final class DiviThemeBuilderTest extends TestCase $manifest = DiviThemeBuilder::manifest(); self::assertContains(DiviThemeBuilder::class, DiviAdapters::CLASSES); - self::assertSame(Manifest::THEME_BUILDER, $manifest->area); + self::assertSame(DiviAreas::THEME_BUILDER, $manifest->area); self::assertSame(DiviThemeBuilder::postTypes(), $manifest->postTypes); self::assertSame( ['tb-api-save', 'tb-store-template', 'tb-load-setting-options'], diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index cfb8267..f6279ae 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -6,7 +6,8 @@ namespace LIT\MCP\Tests\Unit; use Brain\Monkey; use Brain\Monkey\Functions; -use LIT\MCP\Integrations\Divi\Release\CompatibilityMatrix; +use LIT\MCP\Compatibility\CompatibilityMatrix; +use LIT\MCP\Integrations\Divi\Release\DiviFeature; use LIT\MCP\Integrations\Divi\Release\ReleaseGate; use LIT\MCP\Integrations\Divi\Release\ReleaseStatus; use LIT\MCP\Tests\Support\AssertsToolErrors; @@ -41,7 +42,7 @@ abstract class TestCase extends BaseTestCase // Every Divi feature is released unless a test says otherwise (the gate would ask WordPress and Divi). ReleaseGate::useResolver( - static fn (): ReleaseStatus => ReleaseStatus::of('5.2.1', CompatibilityMatrix::allReleased()) + static fn (): ReleaseStatus => ReleaseStatus::of('5.2.1', CompatibilityMatrix::allReleased(DiviFeature::cases())) ); }