*/ private array $deleted = []; protected function setUp(): void { parent::setUp(); // The probe must work while Divi is untested – that is when it runs. ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(ReleaseState::Untested, '5.3.0')); $this->deleted = []; Functions\when('wp_insert_post')->justReturn(41); Functions\when('get_post')->justReturn( new WP_Post(['ID' => 41, 'post_content' => '']) ); Functions\when('get_post_meta')->alias( static fn (int $id, string $key): string => $key === '_et_pb_show_page_creation' ? 'off' : 'on' ); Functions\when('wp_delete_post')->alias(function (int $id, bool $force): WP_Post { self::assertTrue($force, 'never to the trash'); $this->deleted[] = $id; return new WP_Post(['ID' => $id]); }); } private function composer(?callable $write = null): ContentComposer { $composer = Mockery::mock(ContentComposer::class); $default = static function (int $id, string $type, array $sections): array { // The composer writes through the saver, which asks the gate: the probe must have opened it for 41. ReleaseGate::assertWritable($id); return ['warnings' => [], 'outline' => '']; }; $composer->allows('write')->andReturnUsing($write ?? $default); return $composer; } private function renderer(string $html): PageRenderer { $renderer = Mockery::mock(PageRenderer::class); $renderer->allows('render')->andReturn($html); return $renderer; } public function testGreenWhenEveryModuleRendersAndThePageIsGone(): void { $probe = new WriteProbe( $this->composer(), $this->renderer(implode(' ', WriteProbe::TEXTS)), '/nonexistent-divi' ); self::assertSame([], $probe->run()); self::assertSame([41], $this->deleted); } public function testMissingTextsAndFlagsAreReported(): void { Functions\when('get_post_meta')->justReturn(''); $probe = new WriteProbe($this->composer(), $this->renderer('litcheck-heading'), '/nonexistent-divi'); $problems = $probe->run(); self::assertContains('Builder-Kennzeichen _et_pb_use_builder fehlt.', $problems); self::assertContains('„litcheck-button“ fehlt in der gerenderten Seite.', $problems); self::assertNotContains('„litcheck-heading“ fehlt in der gerenderten Seite.', $problems); self::assertSame([41], $this->deleted); } public function testDeletesThePageEvenWhenWritingThrows(): void { $composer = $this->composer(static function (): never { throw new RuntimeException('Composer kaputt'); }); $problems = (new WriteProbe($composer, $this->renderer(''), '/nonexistent-divi'))->run(); self::assertSame(['Schreibtest abgebrochen: Composer kaputt'], $problems); self::assertSame([41], $this->deleted); $this->expectException(\LIT\MCP\Integrations\Divi\Release\DiviLocked::class); ReleaseGate::assertWritable(41); } public function testReportsAPageThatCouldNotBeDeleted(): void { Functions\when('wp_delete_post')->justReturn(false); $probe = new WriteProbe( $this->composer(), $this->renderer(implode(' ', WriteProbe::TEXTS)), '/nonexistent-divi' ); $problems = $probe->run(); self::assertSame( ['Die Testseite 41 konnte nicht gelöscht werden; bitte unter Seiten endgültig löschen.'], $problems ); } public function testCreatesAMarkedDraftPage(): void { // wp_insert_post is already stubbed in setUp(); Functions\expect() cannot rewire the same // function again (Brain Monkey keeps the first redefinition), so the args are captured here. $captured = null; Functions\when('wp_insert_post')->alias(static function (array $post, bool $wpError) use (&$captured): int { $captured = $post; return 41; }); $probe = new WriteProbe( $this->composer(), $this->renderer(implode(' ', WriteProbe::TEXTS)), '/nonexistent-divi' ); $probe->run(); self::assertIsArray($captured); self::assertSame('page', $captured['post_type']); self::assertSame('draft', $captured['post_status']); self::assertTrue(str_starts_with($captured['post_title'], '[LIT MCP Prüfung] ')); self::assertSame(['_lit_mcp_check_page' => '1'], $captured['meta_input']); } public function testRemovesOrphanedCheckPages(): void { Functions\expect('get_posts')->once()->with(Mockery::on(static fn (array $query): bool => $query['meta_key'] === '_lit_mcp_check_page' && $query['post_status'] === 'any' && $query['fields'] === 'ids' && ($query['date_query'][0]['before'] ?? null) === '10 minutes ago'))->andReturn([51, 52]); self::assertSame([51, 52], WriteProbe::removeOrphans()); self::assertSame([51, 52], $this->deleted); } }