*/ private array $options = []; protected function setUp(): void { parent::setUp(); $this->options = []; Functions\when('get_option')->alias( fn (string $name, mixed $default = false): mixed => $this->options[$name] ?? $default ); Functions\when('update_option')->alias(function (string $name, mixed $value, mixed $autoload = null): bool { $this->options[$name] = $value; self::assertFalse($autoload, 'the report is read in the admin only – no autoload'); return true; }); } /** @param list $steps */ private static function report(array $steps): CheckReport { return new CheckReport('r1', '2026-10-01T08:00:00+00:00', 1, '5.3.0', '0.6.0', 'key', $steps); } /** @return list */ private static function green(): array { return array_map( static fn (string $key): CheckStep => new CheckStep($key, []), [CheckStep::MANIFEST, CheckStep::MIRRORS, 'catalog'] ); } /** What the Divi report looked like before the generic version: no label per step, key divi_version. */ private static function oldDiviReport(): array { return [ 'id' => 'r1', 'created_at' => '2026-10-01T08:00:00+00:00', 'user_id' => 1, 'divi_version' => '5.3.0', 'plugin_version' => '0.6.0', 'state_key' => 'key', 'steps' => [ ['key' => 'manifest', 'problems' => [], 'skipped' => false], ['key' => 'mirrors', 'problems' => ['Theme Builder: Nachbau von x() – y'], 'skipped' => false], ['key' => 'write', 'problems' => [], 'skipped' => true], ], ]; } public function testGreenOnlyWithEveryStepGreenAndNoneSkipped(): void { self::assertTrue(self::report(self::green())->green()); self::assertFalse(self::report([])->green()); $red = self::green(); $red[2] = new CheckStep('catalog', ['divi/text: unbekanntes Modul']); self::assertFalse(self::report($red)->green()); $skipped = self::green(); $skipped[2] = new CheckStep('catalog', [], true); self::assertFalse(self::report($skipped)->green()); } public function testFreshOnlyForTheSameVersionsAndStateKey(): void { $report = self::report(self::green()); self::assertTrue($report->freshFor('5.3.0', '0.6.0', 'key')); self::assertFalse($report->freshFor('5.3.1', '0.6.0', 'key'), 'plugin under test updated'); self::assertFalse($report->freshFor('5.3.0', '0.7.0', 'key'), 'this plugin updated'); self::assertFalse($report->freshFor('5.3.0', '0.6.0', 'other'), 'a source file changed'); } public function testRoundTripsThroughAnArrayWithTheLabel(): void { $steps = self::green(); $steps[1] = new CheckStep(CheckStep::MIRRORS, ['x'], false, 'Fingerabdrücke'); $report = self::report($steps); self::assertEquals($report, CheckReport::fromArray($report->toArray())); self::assertSame('Fingerabdrücke', $report->step(CheckStep::MIRRORS)?->label()); self::assertSame('Fingerabdrücke', $report->toArray()['steps'][1]['label']); self::assertNull(CheckReport::fromArray('broken')); self::assertNull(CheckReport::fromArray(['id' => 'r1'])); } public function testAStepWithoutLabelShowsItsKeyAndIsWrittenWithoutOne(): void { $step = new CheckStep('catalog', []); self::assertSame('catalog', $step->label()); self::assertArrayNotHasKey('label', $step->toArray()); } public function testTheGenericStepsHaveOneLabelSourceWithTheProfileName(): void { self::assertSame('Pods-Funktionen, Klassen und Registrierungen', CheckStep::genericLabel(CheckStep::MANIFEST, 'Pods')); self::assertSame('Fingerabdrücke nachgebauter Pods-Funktionen', CheckStep::genericLabel(CheckStep::MIRRORS, 'Pods')); self::assertNull(CheckStep::genericLabel('catalog', 'Pods')); } public function testAReportWithoutStepsIsNotGreen(): void { self::assertFalse(self::report([])->green()); } public function testAnOldDiviReportIsReadAndWrittenUnchanged(): void { $old = self::oldDiviReport(); $report = CheckReport::fromArray($old, 'divi_version'); self::assertSame('5.3.0', $report?->version); self::assertSame('manifest', $report?->steps[0]->label(), 'no stored label: the raw key'); self::assertSame($old, $report?->toArray('divi_version')); self::assertNull(CheckReport::fromArray($old), 'the default key is "version"'); } public function testTheStoreKeepsTheDiviOptionAndFormat(): void { $this->options['lit_mcp_divi_check_report'] = self::oldDiviReport(); $store = new CheckReportStore('divi', 'divi_version'); $report = $store->latest(); self::assertSame('r1', $report?->id); $store->save($report); self::assertSame(self::oldDiviReport(), $this->options['lit_mcp_divi_check_report']); } public function testTheStoresOfAProfileUseItsIdAndVersionKey(): void { $profile = new Profile( id: 'divi', label: 'Divi', tier: Tier::ReportOnly, capabilities: [FakeCapability::Read], reportVersionKey: 'divi_version' ); $this->options['lit_mcp_divi_check_report'] = self::oldDiviReport(); $this->options['lit_mcp_divi_releases'] = [[ 'divi_version' => '5.2.2', 'plugin_version' => '0.6.0', 'user_id' => 3, 'released_at' => '2026-10-01T08:00:00+00:00', 'report_id' => 'r', ]]; self::assertSame('r1', CheckReportStore::forProfile($profile)->latest()?->id); self::assertSame('r', ReleaseStore::forProfile($profile)->find('5.2.2', '0.6.0')['report_id'] ?? null); } public function testStoredReportReadsTheDiviFormatForAProfileWithItsKey(): void { $profile = new Profile( id: 'divi', label: 'Divi', tier: Tier::ReportOnly, capabilities: [FakeCapability::Read], reportVersionKey: 'divi_version' ); $this->options['lit_mcp_divi_check_report'] = self::oldDiviReport(); self::assertSame('5.3.0', Compatibility::storedReport($profile)?->version); } public function testTheStoreUsesAnOptionAndVersionKeyPerProfile(): void { $store = new CheckReportStore('pods'); self::assertNull($store->latest()); $store->save(self::report(self::green())); self::assertSame(['lit_mcp_pods_check_report'], array_keys($this->options)); self::assertSame('5.3.0', $this->options['lit_mcp_pods_check_report']['version']); self::assertSame('5.3.0', $store->latest()?->version); self::assertNull((new CheckReportStore('divi', 'divi_version'))->latest()); } }