alias(static fn (string $path = ''): string => 'https://k.de/wp-admin/' . $path); Functions\when('add_query_arg')->alias( static fn (array $args, string $url): string => $url . '?' . http_build_query($args) ); Functions\when('get_current_user_id')->justReturn(1); } protected function tearDown(): void { unset($GLOBALS['wpdb']); parent::tearDown(); } /** Mock of the global $wpdb, its INSERT IGNORE returning $inserted rows affected. */ private static function wpdb(int $inserted): \wpdb { $db = Mockery::mock(\wpdb::class)->makePartial(); $db->expects('query')->once()->with(Mockery::on(static function (string $sql): bool { return str_starts_with( $sql, "INSERT IGNORE INTO wp_options (option_name, option_value, autoload) " . "VALUES ('lit_mcp_divi_check_lock', '" ) && str_ends_with($sql, "', 'no')"); }))->andReturn($inserted); $GLOBALS['wpdb'] = $db; return $db; } public function testRegistersAPostRouteForAdministratorsOnly(): void { Functions\expect('register_rest_route')->once()->with( 'lit-mcp/v1', '/divi-check', Mockery::on(static function (array $args): bool { Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'manage_options'); $allowed = ($args['permission_callback'])(); Functions\when('current_user_can')->justReturn(false); return $args['methods'] === 'POST' && $allowed === true && ($args['permission_callback'])() === false; }) ); (new DiviCheckRoute(static fn () => null))->registerRoute(); } public function testRunsTheCheckStoresTheReportAndRedirectsToTheTab(): void { self::wpdb(1); $report = new CheckReport('r1', 'd', 1, '5.3.0', '0.6.0', 'k', []); $check = Mockery::mock(CompatibilityCheck::class); $check->expects('run')->with(1)->andReturn($report); $reports = Mockery::mock(CheckReportStore::class); $reports->expects('save')->with($report); Functions\expect('delete_option')->once()->with(DiviCheckRoute::LOCK); $response = (new DiviCheckRoute(static fn () => $check, $reports))->handle(); self::assertSame(303, $response->get_status()); self::assertStringContainsString('tab=divi', $response->get_headers()['Location']); self::assertStringContainsString('lit_mcp_notice=divi_checked', $response->get_headers()['Location']); } public function testASecondCheckWaitsForTheFirst(): void { $db = self::wpdb(0); $db->expects('get_var')->once()->andReturn((string) time()); Functions\expect('delete_option')->never(); $reports = Mockery::mock(CheckReportStore::class); $reports->expects('save')->never(); $response = (new DiviCheckRoute(static fn () => self::fail('no second check'), $reports))->handle(); self::assertStringContainsString('lit_mcp_notice=divi_check_running', $response->get_headers()['Location']); } public function testAStaleLockIsTakenOver(): void { $db = self::wpdb(0); $db->expects('get_var')->once()->andReturn('1'); $db->expects('query')->once()->with(Mockery::on(static function (string $sql): bool { return str_starts_with($sql, "UPDATE wp_options SET option_value = '") && str_ends_with( $sql, "' WHERE option_name = 'lit_mcp_divi_check_lock' AND option_value = '1'" ); }))->andReturn(1); Functions\expect('delete_option')->once()->with(DiviCheckRoute::LOCK); $check = Mockery::mock(CompatibilityCheck::class); $check->expects('run')->andReturn(new CheckReport('r1', 'd', 1, '5.3.0', '0.6.0', 'k', [])); $reports = Mockery::mock(CheckReportStore::class); $reports->expects('save'); $response = (new DiviCheckRoute(static fn () => $check, $reports))->handle(); self::assertStringContainsString('lit_mcp_notice=divi_checked', $response->get_headers()['Location']); } public function testATakeoverLostToAnotherRequestWaitsForIt(): void { $db = self::wpdb(0); $db->expects('get_var')->once()->andReturn('1'); $db->expects('query')->once()->with(Mockery::on(static fn (string $sql): bool => str_starts_with( $sql, "UPDATE wp_options SET option_value = '" )))->andReturn(0); Functions\expect('delete_option')->never(); $reports = Mockery::mock(CheckReportStore::class); $reports->expects('save')->never(); $response = (new DiviCheckRoute(static fn () => self::fail('no second check'), $reports))->handle(); self::assertStringContainsString('lit_mcp_notice=divi_check_running', $response->get_headers()['Location']); } public function testTheLockIsReleasedAndTheCheckMarkedFailedWhenItThrows(): void { self::wpdb(1); Functions\expect('delete_option')->once()->with(DiviCheckRoute::LOCK); $check = Mockery::mock(CompatibilityCheck::class); $check->expects('run')->andThrow(new RuntimeException('divi kaputt')); $reports = Mockery::mock(CheckReportStore::class); $reports->expects('save')->never(); // error_log() is a PHP internal and cannot be stubbed; redirect it to a temp file instead. $log = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log'); $previous = ini_set('error_log', $log); try { $response = (new DiviCheckRoute(static fn () => $check, $reports))->handle(); } finally { ini_set('error_log', (string) $previous); } self::assertStringContainsString('lit_mcp_notice=divi_check_failed', $response->get_headers()['Location']); self::assertStringContainsString( 'divi check failed: RuntimeException: divi kaputt', (string) file_get_contents($log) ); unlink($log); } }