mp = new OnecomMarketplace(); $_SERVER['ONECOM_DOMAIN_NAME'] = 'example.com'; // Clean up any existing transients $this->cleanup_all_transients(); } public function tearDown(): void { parent::tearDown(); $this->cleanup_all_transients(); if (isset($_SERVER['ONECOM_DOMAIN_NAME'])) { unset($_SERVER['ONECOM_DOMAIN_NAME']); } $_POST = []; } /** * Helper to clean up all marketplace-related transients */ private function cleanup_all_transients(): void { $plugins = ['wp-rocket', 'rank-math']; foreach ($plugins as $plugin) { delete_site_transient("{$plugin}_activation_start_at"); delete_site_transient("{$plugin}_activation_button_clicked_at"); delete_site_transient("{$plugin}-pp-activation-start-at"); delete_site_transient("{$plugin}_purchase_button_start_at"); delete_site_transient("{$plugin}_select_button_clicked_at"); delete_site_transient("{$plugin}_onecom_addon_info"); } } /** * Custom AJAX die handler that prevents die() from exiting * Note: wp_send_json() already echoes the JSON before calling wp_die(), * so we capture it via output buffering and throw an exception to stop execution */ public function wp_ajax_print_handler_filter() { return function( $message ) { throw new WPAjaxDieContinueException(); }; } /** * Mock HTTP failure response */ public function mock_http_failure( $preempt, $args, $url ) { return new WP_Error( 'http_request_failed', 'A valid URL was not provided.' ); } /** * Mock HTTP success response for addon info */ public function mock_http_addon_purchased( $preempt, $args, $url ) { return [ 'body' => json_encode([ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET' ] ]) ]; } /** * Mock HTTP success response for addon not purchased */ public function mock_http_addon_not_purchased( $preempt, $args, $url ) { return [ 'body' => json_encode([ 'success' => true, 'data' => [ 'source' => 'NOT_PURCHASED', 'product' => 'WP_ROCKET' ] ]) ]; } // ============================================================================ // CONSTRUCTOR AND INITIALIZATION TESTS // ============================================================================ public function test_constructor_creates_instance(): void { $mp = new OnecomMarketplace(); $this->assertInstanceOf(OnecomMarketplace::class, $mp); } public function test_init_registers_hooks(): void { $this->mp->init(); $this->assertTrue(has_action('admin_enqueue_scripts', [$this->mp, 'enqueueScripts']) !== false); $this->assertTrue(has_action('wp_ajax_marketplace_plugin_activate', [$this->mp, 'onclickPluginActivate']) !== false); $this->assertTrue(has_action('wp_ajax_marketplace_addon_purchase_check', [$this->mp, 'addonStatusCheck']) !== false); $this->assertTrue(has_action('wp_ajax_marketplace_addon_purchase_check_onload', [$this->mp, 'addonStatusCheckOnLoad']) !== false); $this->assertTrue(has_action('wp_ajax_marketplace_check_activate_banner', [$this->mp, 'checkActivateBannerOnReload']) !== false); $this->assertTrue(has_action('wp_ajax_get_addon_purchase_status', [$this->mp, 'isAddonPurchased']) !== false); $this->assertTrue(has_action('deactivated_plugin', [$this->mp, 'pluginDeactivated']) !== false); } public function test_init_registers_plugin_activate_reload_hook(): void { $this->mp->init(); $this->assertTrue(has_action('wp_ajax_marketplace_plugin_activate_reload', [$this->mp, 'onReloadPluginActivateCheck']) !== false); } // ============================================================================ // ENQUEUE SCRIPTS AND STYLES TESTS // ============================================================================ public function test_enqueueScripts_non_marketplace_page(): void { $result = $this->mp->enqueueScripts('other_page'); $this->assertNull($result); } public function test_enqueueScripts_marketplace_page(): void { $result = $this->mp->enqueueScripts('one-com_page_onecom-marketplace'); $this->assertNull($result); } public function test_enqueueScripts_marketplace_products_page(): void { $result = $this->mp->enqueueScripts('one-com_page_onecom-marketplace-products'); $this->assertNull($result); } // ============================================================================ // PLUGIN URL AND NOTICE TESTS // ============================================================================ public function test_getNoticePluginUrl_wp_rocket(): void { $url = $this->mp->getNoticePluginUrl('wp-rocket'); $this->assertStringContainsString('options-general.php?page=wprocket', $url); } public function test_getNoticePluginUrl_rank_math(): void { $url = $this->mp->getNoticePluginUrl('rank-math'); $this->assertStringContainsString('admin.php?page=rank-math&view=modules', $url); } public function test_getNoticePluginUrl_empty_string(): void { $url = $this->mp->getNoticePluginUrl(''); $this->assertEmpty($url); } public function test_getNoticePluginUrl_unknown_plugin(): void { $url = $this->mp->getNoticePluginUrl('unknown-plugin'); $this->assertEmpty($url); } public function test_getActivatedNotice_wp_rocket(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-success', $html); $this->assertStringContainsString('WP Rocket', $html); $this->assertStringContainsString('gv-notice-close', $html); $this->assertStringContainsString('success.svg', $html); $this->assertStringContainsString('close.svg', $html); $this->assertStringContainsString('options-general.php?page=wprocket', $html); } public function test_getActivatedNotice_rank_math(): void { $html = $this->mp->getActivatedNotice('rank-math'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-success', $html); $this->assertStringContainsString('Rank Math Pro', $html); $this->assertStringContainsString('admin.php?page=rank-math&view=modules', $html); } public function test_getActivatedNotice_contains_manage_button(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertStringContainsString('gv-button', $html); $this->assertStringContainsString('target="_blank"', $html); } public function test_getActivatedNotice_marketplace_products_page_wp_rocket(): void { $_POST['page'] = 'onecom-marketplace-products'; $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-success', $html); $this->assertStringContainsString('WP Rocket', $html); $this->assertStringContainsString('is active', $html); $this->assertStringContainsString('successfully activated on this installation', $html); $this->assertStringContainsString('Go to', $html); $this->assertStringContainsString('gv-hidden', $html); $this->assertStringContainsString('ocwp_ocmp_go_to_wp_rocket_clicked_event', $html); $this->assertStringContainsString('options-general.php?page=wprocket', $html); } public function test_getActivatedNotice_marketplace_products_page_rank_math(): void { $_POST['page'] = 'onecom-marketplace-products'; $html = $this->mp->getActivatedNotice('rank-math'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-success', $html); $this->assertStringContainsString('Rank Math Pro', $html); $this->assertStringContainsString('is active', $html); $this->assertStringContainsString('successfully activated on this installation', $html); $this->assertStringContainsString('Go to', $html); $this->assertStringContainsString('gv-hidden', $html); $this->assertStringContainsString('ocwp_ocmp_go_to_rank_math_clicked_event', $html); $this->assertStringContainsString('admin.php?page=rank-math&view=modules', $html); } public function test_getActivatedNotice_marketplace_products_hidden_class_present(): void { $_POST['page'] = 'onecom-marketplace-products'; $html = $this->mp->getActivatedNotice('wp-rocket'); // Verify the gv-hidden class is set for products page $this->assertStringContainsString('gv-hidden', $html); // The arrow icon should have the hidden class attribute $this->assertStringContainsString('class="gv-hidden"', $html); } public function test_getActivatedNotice_default_page_no_hidden_class(): void { // Don't set $_POST['page'], so it defaults to empty string $html = $this->mp->getActivatedNotice('wp-rocket'); // When page is not 'onecom-marketplace-products', hiddenClass should be empty // and the button should show "Get started" instead of "Go to" $this->assertStringContainsString('Get started', $html); $this->assertStringContainsString('ocwp_ocmp_get_started_wp_rocket_clicked_event', $html); } public function test_getActivatedNotice_slug_dash_to_underscore_conversion_wp_rocket(): void { $_POST['page'] = 'onecom-marketplace-products'; $html = $this->mp->getActivatedNotice('wp-rocket'); // Verify dashes are converted to underscores in event name $this->assertStringContainsString('ocwp_ocmp_go_to_wp_rocket_clicked_event', $html); $this->assertStringNotContainsString('ocwp_ocmp_go_to_wp-rocket_clicked_event', $html); } public function test_getActivatedNotice_slug_dash_to_underscore_conversion_rank_math(): void { $_POST['page'] = 'onecom-marketplace-products'; $html = $this->mp->getActivatedNotice('rank-math'); // Verify dashes are converted to underscores in event name $this->assertStringContainsString('ocwp_ocmp_go_to_rank_math_clicked_event', $html); $this->assertStringNotContainsString('ocwp_ocmp_go_to_rank-math_clicked_event', $html); } public function test_getActivatedNotice_returns_string(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertIsString($html); $this->assertNotEmpty($html); } public function test_getActivatedNotice_contains_close_button(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertStringContainsString('gv-notice-close', $html); $this->assertStringContainsString('close.svg', $html); } public function test_getActivatedNotice_contains_success_icon(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertStringContainsString('success.svg', $html); $this->assertStringContainsString('gv-notice-icon', $html); } public function test_getActivatedNotice_link_opens_in_new_tab(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); // Verify target="_blank" is present for opening link in new tab $this->assertStringContainsString('target="_blank"', $html); } public function test_getActivatedNotice_html_structure(): void { $html = $this->mp->getActivatedNotice('wp-rocket'); $this->assertStringContainsString('
assertStringContainsString('Go to', $html); $this->assertStringNotContainsString('Get started', $html); } // ============================================================================ // PLUGIN INSTALLATION TESTS // ============================================================================ public function test_isPluginInstalled_invalid_slug(): void { $result = $this->mp->isPluginInstalled('invalid-plugin'); $this->assertFalse($result); } public function test_isPluginInstalled_wp_rocket(): void { $result = $this->mp->isPluginInstalled('wp-rocket'); $this->assertIsBool($result); } public function test_isPluginInstalled_rank_math(): void { $result = $this->mp->isPluginInstalled('rank-math'); $this->assertIsBool($result); } // ============================================================================ // ADDON PURCHASE VALIDATION TESTS // ============================================================================ public function test_validateAddonPurchase_valid(): void { $valid = [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET' ] ]; $this->assertTrue($this->mp->validateAddonPurchase($valid, 'WP_ROCKET')); } public function test_validateAddonPurchase_invalid_success_false(): void { $invalid = [ 'success' => false, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET' ] ]; $this->assertFalse($this->mp->validateAddonPurchase($invalid, 'WP_ROCKET')); } public function test_validateAddonPurchase_not_purchased_source(): void { $not_purchased = [ 'success' => true, 'data' => [ 'source' => 'NOT_PURCHASED', 'product' => 'WP_ROCKET' ] ]; $this->assertFalse($this->mp->validateAddonPurchase($not_purchased, 'WP_ROCKET')); } public function test_validateAddonPurchase_wrong_product(): void { $wrong_product = [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'RANK_MATH' ] ]; $this->assertFalse($this->mp->validateAddonPurchase($wrong_product, 'WP_ROCKET')); } public function test_validateAddonPurchase_missing_data_key(): void { $missing_data = ['success' => true]; $this->assertFalse($this->mp->validateAddonPurchase($missing_data, 'WP_ROCKET')); } public function test_validateAddonPurchase_missing_source_key(): void { $missing_source = [ 'success' => true, 'data' => ['product' => 'WP_ROCKET'] ]; $this->assertFalse($this->mp->validateAddonPurchase($missing_source, 'WP_ROCKET')); } public function test_validateAddonPurchase_missing_product_key(): void { $missing_product = [ 'success' => true, 'data' => ['source' => 'PURCHASED'] ]; $this->assertFalse($this->mp->validateAddonPurchase($missing_product, 'WP_ROCKET')); } public function test_validateAddonPurchase_null_input(): void { $this->assertFalse($this->mp->validateAddonPurchase(null, 'WP_ROCKET')); } public function test_validateAddonPurchase_string_input(): void { $this->assertFalse($this->mp->validateAddonPurchase('invalid', 'WP_ROCKET')); } public function test_validateAddonPurchase_empty_array(): void { $this->assertFalse($this->mp->validateAddonPurchase([], 'WP_ROCKET')); } // ============================================================================ // ADDON INFO AND TRANSIENT TESTS // ============================================================================ public function test_getAddonInfo_returns_cached_transient(): void { $cached_data = ['success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'WP_ROCKET']]; set_site_transient('wp-rocket_onecom_addon_info', $cached_data, 60); $result = $this->mp->getAddonInfo('wp-rocket', false); $this->assertEquals($cached_data, $result); delete_site_transient('wp-rocket_onecom_addon_info'); } public function test_getAddonInfo_empty_domain_error(): void { // Temporarily unset the domain to test empty domain error $original_domain = $_SERVER['ONECOM_DOMAIN_NAME'] ?? null; unset($_SERVER['ONECOM_DOMAIN_NAME']); $result = $this->mp->getAddonInfo('wp-rocket', false, ''); // Restore the original domain if ($original_domain !== null) { $_SERVER['ONECOM_DOMAIN_NAME'] = $original_domain; } $this->assertIsArray($result); $this->assertFalse($result['success']); $this->assertEquals('Empty domain', $result['error']); } public function test_getAddonInfo_with_custom_domain(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['callWPApiForAddon']) ->getMock(); $expected_data = ['success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'WP_ROCKET']]; $this->mp->method('callWPApiForAddon')->willReturn($expected_data); $result = $this->mp->getAddonInfo('wp-rocket', true, 'custom-domain.com'); $this->assertEquals($expected_data, $result); } public function test_getAddonInfo_force_refresh_uses_api(): void { $cached_data = ['success' => true, 'data' => ['source' => 'NOT_PURCHASED']]; set_site_transient('wp-rocket_onecom_addon_info', $cached_data, 60); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['callWPApiForAddon']) ->getMock(); $new_data = ['success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'WP_ROCKET']]; $this->mp->method('callWPApiForAddon')->willReturn($new_data); $result = $this->mp->getAddonInfo('wp-rocket', true); $this->assertEquals($new_data, $result); delete_site_transient('wp-rocket_onecom_addon_info'); } public function test_getAddonInfo_for_rank_math(): void { $cached_data = ['success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'RANK_MATH']]; set_site_transient('rank-math_onecom_addon_info', $cached_data, 60); $result = $this->mp->getAddonInfo('rank-math', false); $this->assertEquals($cached_data, $result); delete_site_transient('rank-math_onecom_addon_info'); } // ============================================================================ // TRANSIENT MANAGEMENT TESTS // ============================================================================ public function test_setTransientForAddonActivation_wp_rocket(): void { $slug = 'wp-rocket'; $this->mp->setTransientForAddonActivation($slug); $this->assertNotFalse(get_site_transient('wp-rocket_activation_start_at')); $this->assertNotFalse(get_site_transient('wp-rocket_activation_button_clicked_at')); $this->assertNotFalse(get_site_transient('wp-rocket-pp-activation-start-at')); } public function test_setTransientForAddonActivation_rank_math(): void { $slug = 'rank-math'; $this->mp->setTransientForAddonActivation($slug); $this->assertNotFalse(get_site_transient('rank-math_activation_start_at')); $this->assertNotFalse(get_site_transient('rank-math_activation_button_clicked_at')); $this->assertNotFalse(get_site_transient('rank-math-pp-activation-start-at')); } public function test_clearActivationQueue_wp_rocket(): void { $slug = 'wp-rocket'; set_site_transient('wp-rocket_activation_start_at', 1, 60); set_site_transient('wp-rocket_activation_button_clicked_at', 1, 60); set_site_transient('wp-rocket-pp-activation-start-at', 1, 60); $this->mp->clearActivationQueue($slug); $this->assertFalse(get_site_transient('wp-rocket_activation_start_at')); $this->assertFalse(get_site_transient('wp-rocket_activation_button_clicked_at')); $this->assertFalse(get_site_transient('wp-rocket-pp-activation-start-at')); } public function test_clearActivationQueue_rank_math(): void { $slug = 'rank-math'; set_site_transient('rank-math_activation_start_at', 1, 60); set_site_transient('rank-math_activation_button_clicked_at', 1, 60); set_site_transient('rank-math-pp-activation-start-at', 1, 60); $this->mp->clearActivationQueue($slug); $this->assertFalse(get_site_transient('rank-math_activation_start_at')); $this->assertFalse(get_site_transient('rank-math_activation_button_clicked_at')); $this->assertFalse(get_site_transient('rank-math-pp-activation-start-at')); } public function test_clearAddonStatusQueue_wp_rocket(): void { $slug = 'wp-rocket'; set_site_transient('wp-rocket_purchase_button_start_at', 1, 60); set_site_transient('wp-rocket_select_button_clicked_at', 1, 60); $this->mp->clearAddonStatusQueue($slug); $this->assertFalse(get_site_transient('wp-rocket_purchase_button_start_at')); $this->assertFalse(get_site_transient('wp-rocket_select_button_clicked_at')); } public function test_clearAddonStatusQueue_rank_math(): void { $slug = 'rank-math'; set_site_transient('rank-math_purchase_button_start_at', 1, 60); set_site_transient('rank-math_select_button_clicked_at', 1, 60); $this->mp->clearAddonStatusQueue($slug); $this->assertFalse(get_site_transient('rank-math_purchase_button_start_at')); $this->assertFalse(get_site_transient('rank-math_select_button_clicked_at')); } // ============================================================================ // AJAX HANDLER TESTS - Using mock wp_send_json/wp_die pattern // ============================================================================ public function test_onclickPluginActivate_sets_transient(): void { $_POST['plugin_slug'] = 'wp-rocket'; delete_site_transient('wp-rocket_activation_button_clicked_at'); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->onclickPluginActivate(); } catch (WPAjaxDieContinueException $e) { // Expected - wp_send_json calls wp_die } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $this->assertNotFalse(get_site_transient('wp-rocket_activation_button_clicked_at')); } public function test_onclickPluginActivate_override_rank_math_slug(): void { $_POST['plugin_slug'] = 'seo-by-rank-math-pro'; delete_site_transient('rank-math_activation_button_clicked_at'); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->onclickPluginActivate(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $this->assertNotFalse(get_site_transient('rank-math_activation_button_clicked_at')); } public function test_addonStatusCheck_sets_transient(): void { $_POST['plugin_slug'] = 'wp-rocket'; delete_site_transient('wp-rocket_select_button_clicked_at'); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->addonStatusCheck(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $this->assertNotFalse(get_site_transient('wp-rocket_select_button_clicked_at')); } // ============================================================================ // CHECK ADDON PURCHASE STATUS - ADDITIONAL COVERAGE TESTS (Lines 726-738, 750-787) // ============================================================================ public function test_checkAddonPurchaseStatus_button_not_clicked(): void { $_POST['plugin_slug'] = 'wp-rocket'; delete_site_transient('wp-rocket_select_button_clicked_at'); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('normal_reload', $response['status']); } public function test_checkAddonPurchaseStatus_addon_purchased_success(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the select button clicked transient set_site_transient('wp-rocket_select_button_clicked_at', time(), 300); // Set a recent purchase start time to simulate a retry (not first attempt) set_site_transient('wp-rocket_purchase_button_start_at', current_time('timestamp'), 3600); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['getAddonInfo', 'validateAddonPurchase', 'clearAddonStatusQueue', 'setTransientForAddonActivation']) ->getMock(); // Mock addon as purchased $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'WP_ROCKET'] ]); $this->mp->method('validateAddonPurchase')->willReturn(true); // No willReturn() for void methods add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('addon_purchased', $response['status']); // Cleanup delete_site_transient('wp-rocket_select_button_clicked_at'); delete_site_transient('wp-rocket_purchase_button_start_at'); } public function test_checkAddonPurchaseStatus_plugin_already_active(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the select button clicked transient set_site_transient('wp-rocket_select_button_clicked_at', time(), 300); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['clearAddonStatusQueue', 'setTransientForAddonActivation']) ->getMock(); // No willReturn() for void methods add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as active $mock_active_plugins = function() { return ['wp-rocket/wp-rocket.php']; }; add_filter('pre_option_active_plugins', $mock_active_plugins); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_active_plugins); $this->assertJson($output); $response = json_decode($output, true); // When plugin is active, send_success is called $this->assertEquals('addon_purchased', $response['status']); // Cleanup delete_site_transient('wp-rocket_select_button_clicked_at'); } public function test_checkAddonPurchaseStatus_first_queue_attempt(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the select button clicked transient set_site_transient('wp-rocket_select_button_clicked_at', time(), 300); // No purchase start time set yet delete_site_transient('wp-rocket_purchase_button_start_at'); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('added_in_queue', $response['status']); // Verify transient was set $this->assertNotFalse(get_site_transient('wp-rocket_purchase_button_start_at')); // Cleanup delete_site_transient('wp-rocket_select_button_clicked_at'); delete_site_transient('wp-rocket_purchase_button_start_at'); } public function test_checkAddonPurchaseStatus_timeout_expired(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the select button clicked transient set_site_transient('wp-rocket_select_button_clicked_at', time(), 300); // Set an old purchase start time to trigger timeout $old_time = current_time('timestamp') - (10 * MINUTE_IN_SECONDS); set_site_transient('wp-rocket_purchase_button_start_at', $old_time, 3600); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['getAddonInfo', 'validateAddonPurchase', 'clearAddonStatusQueue']) ->getMock(); // Mock addon as not purchased $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'NOT_PURCHASED', 'product' => 'WP_ROCKET'] ]); $this->mp->method('validateAddonPurchase')->willReturn(false); // No willReturn() for void methods add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('expired_queue', $response['status']); // Cleanup delete_site_transient('wp-rocket_select_button_clicked_at'); delete_site_transient('wp-rocket_purchase_button_start_at'); } public function test_checkAddonPurchaseStatus_time_left_less_than_30_seconds(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the select button clicked transient set_site_transient('wp-rocket_select_button_clicked_at', time(), 300); // Set purchase start time with less than 30 seconds remaining (4 minutes 45 seconds elapsed of 5 minutes) $recent_time = current_time('timestamp') - (4 * MINUTE_IN_SECONDS + 45); set_site_transient('wp-rocket_purchase_button_start_at', $recent_time, 3600); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['getAddonInfo', 'validateAddonPurchase', 'clearAddonStatusQueue']) ->getMock(); // Mock addon as not purchased $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'NOT_PURCHASED', 'product' => 'WP_ROCKET'] ]); $this->mp->method('validateAddonPurchase')->willReturn(false); // No willReturn() for void methods add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('expired_queue', $response['status']); // Cleanup delete_site_transient('wp-rocket_select_button_clicked_at'); delete_site_transient('wp-rocket_purchase_button_start_at'); } public function test_checkAddonPurchaseStatus_still_in_progress(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the select button clicked transient set_site_transient('wp-rocket_select_button_clicked_at', time(), 300); // Set a recent purchase start time (still in progress) set_site_transient('wp-rocket_purchase_button_start_at', current_time('timestamp'), 3600); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['getAddonInfo', 'validateAddonPurchase']) ->getMock(); // Mock addon as not purchased $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'NOT_PURCHASED', 'product' => 'WP_ROCKET'] ]); $this->mp->method('validateAddonPurchase')->willReturn(false); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->checkAddonPurchaseStatus('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('already_in_queue', $response['status']); // Cleanup delete_site_transient('wp-rocket_select_button_clicked_at'); delete_site_transient('wp-rocket_purchase_button_start_at'); } public function test_addonStatusCheckOnLoad_executes(): void { $_POST['plugin_slug'] = 'wp-rocket'; set_site_transient('wp-rocket_select_button_clicked_at', time(), 60); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->addonStatusCheckOnLoad(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); } public function test_onReloadPluginActivateCheck_executes(): void { $_POST['plugin_slug'] = 'wp-rocket'; set_site_transient('wp-rocket_activation_button_clicked_at', time(), 60); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->onReloadPluginActivateCheck(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); } public function test_onReloadPluginActivateCheck_rank_math_override(): void { $_POST['plugin_slug'] = 'seo-by-rank-math-pro'; set_site_transient('rank-math_activation_button_clicked_at', time(), 60); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->onReloadPluginActivateCheck(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); } // ============================================================================ // ADDON PURCHASE AND PROVISIONER TESTS // ============================================================================ public function test_isAddonPurchased_wp_rocket(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['validateAddonPurchase', 'getAddonInfo']) ->getMock(); $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'WP_ROCKET'] ]); $this->mp->method('validateAddonPurchase')->willReturn(true); $result = $this->mp->isAddonPurchased('wp-rocket'); $this->assertTrue($result); } public function test_isAddonPurchased_rank_math(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['validateAddonPurchase', 'getAddonInfo']) ->getMock(); $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'RANK_MATH'] ]); $this->mp->method('validateAddonPurchase')->willReturn(true); $result = $this->mp->isAddonPurchased('rank-math'); $this->assertTrue($result); } public function test_isAddonPurchased_not_purchased(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['validateAddonPurchase', 'getAddonInfo']) ->getMock(); $this->mp->method('getAddonInfo')->willReturn([ 'success' => true, 'data' => ['source' => 'NOT_PURCHASED', 'product' => 'WP_ROCKET'] ]); $this->mp->method('validateAddonPurchase')->willReturn(false); $result = $this->mp->isAddonPurchased('wp-rocket'); $this->assertFalse($result); } public function test_isAddonPurchased_with_addon_info(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['validateAddonPurchase', 'getAddonInfo']) ->getMock(); $addon_data = [ 'success' => true, 'data' => ['source' => 'PURCHASED', 'product' => 'WP_ROCKET', 'createdAt' => '2024-01-01'] ]; $this->mp->method('getAddonInfo')->willReturn($addon_data); $this->mp->method('validateAddonPurchase')->willReturn(true); $result = $this->mp->isAddonPurchased('wp-rocket', true); $this->assertIsArray($result); $this->assertTrue($result['is_active']); $this->assertEquals($addon_data, $result['addon_info']); } public function test_callWpApiProvisioner_addon_not_subscribed(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(false); $result = $this->mp->callWpApiProvisioner('wp-rocket'); $this->assertEquals('addon_not_subscribed', $result); } public function test_callWpApiProvisioner_already_in_queue(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); set_site_transient('wp-rocket-pp-activation-start-at', time(), 60); $result = $this->mp->callWpApiProvisioner('wp-rocket'); $this->assertEquals('already_in_queue', $result); } // ============================================================================ // CALL WP API PROVISIONER - ADDITIONAL COVERAGE TESTS (Lines 407-435) // ============================================================================ public function test_callWpApiProvisioner_http_request_made(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); // Track HTTP request $http_called = false; $captured_url = null; $captured_args = null; $mock_http = function($preempt, $args, $url) use (&$http_called, &$captured_url, &$captured_args) { $http_called = true; $captured_url = $url; $captured_args = $args; return [ 'response' => ['code' => 200], 'body' => json_encode(['success' => true]) ]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('wp-rocket'); remove_filter('pre_http_request', $mock_http, 10); $this->assertTrue($http_called); $this->assertNotNull($captured_url); $this->assertStringContainsString('/plugin-provisioner', $captured_url); $this->assertEquals('added_to_queue', $result); // Cleanup delete_site_transient('wp-rocket-pp-activation-start-at'); } public function test_callWpApiProvisioner_http_body_contains_required_fields(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); $captured_args = null; $mock_http = function($preempt, $args, $url) use (&$captured_args) { $captured_args = $args; return [ 'response' => ['code' => 200], 'body' => json_encode(['success' => true]) ]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('wp-rocket'); remove_filter('pre_http_request', $mock_http, 10); // Verify the HTTP request body contains required fields $this->assertNotNull($captured_args); $this->assertArrayHasKey('body', $captured_args); $body = json_decode($captured_args['body'], true); $this->assertIsArray($body); $this->assertArrayHasKey('subdomain', $body); $this->assertArrayHasKey('domain', $body); $this->assertArrayHasKey('addon_slug', $body); $this->assertEquals('wp-rocket', $body['addon_slug']); // Cleanup delete_site_transient('wp-rocket-pp-activation-start-at'); } public function test_callWpApiProvisioner_sets_transient_with_correct_key(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); $mock_http = function($preempt, $args, $url) { return ['response' => ['code' => 200], 'body' => json_encode(['success' => true])]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('wp-rocket'); remove_filter('pre_http_request', $mock_http, 10); // Verify transient is set with correct key $transient_value = get_site_transient('wp-rocket-pp-activation-start-at'); $this->assertNotFalse($transient_value); $this->assertTrue(is_numeric($transient_value)); // Cleanup delete_site_transient('wp-rocket-pp-activation-start-at'); } public function test_callWpApiProvisioner_rank_math_transient_key(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); $mock_http = function($preempt, $args, $url) { return ['response' => ['code' => 200], 'body' => json_encode(['success' => true])]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('rank-math'); remove_filter('pre_http_request', $mock_http, 10); // Verify correct transient key is used for rank-math $transient_value = get_site_transient('rank-math-pp-activation-start-at'); $this->assertNotFalse($transient_value); // Verify wp-rocket transient is not set $this->assertFalse(get_site_transient('wp-rocket-pp-activation-start-at')); // Cleanup delete_site_transient('rank-math-pp-activation-start-at'); } public function test_callWpApiProvisioner_returns_added_to_queue_string(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); $mock_http = function($preempt, $args, $url) { return ['response' => ['code' => 200], 'body' => json_encode(['success' => true])]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('wp-rocket'); remove_filter('pre_http_request', $mock_http, 10); // Verify the return value is exactly 'added_to_queue' $this->assertIsString($result); $this->assertEquals('added_to_queue', $result); // Cleanup delete_site_transient('wp-rocket-pp-activation-start-at'); } public function test_callWpApiProvisioner_http_filter_added_and_removed(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); $mock_http = function($preempt, $args, $url) { return ['response' => ['code' => 200], 'body' => json_encode(['success' => true])]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('wp-rocket'); remove_filter('pre_http_request', $mock_http, 10); // After execution, verify the filter was properly managed $this->assertEquals('added_to_queue', $result); // Verify transient was set (indicating the complete flow executed) $this->assertNotFalse(get_site_transient('wp-rocket-pp-activation-start-at')); // Cleanup delete_site_transient('wp-rocket-pp-activation-start-at'); } public function test_callWpApiProvisioner_url_construction_with_middleware_constant(): void { $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn(true); $captured_url = null; $mock_http = function($preempt, $args, $url) use (&$captured_url) { $captured_url = $url; return ['response' => ['code' => 200], 'body' => json_encode(['success' => true])]; }; add_filter('pre_http_request', $mock_http, 10, 3); $result = $this->mp->callWpApiProvisioner('wp-rocket'); remove_filter('pre_http_request', $mock_http, 10); // Verify URL uses MIDDLEWARE_URL constant and plugin-provisioner endpoint $this->assertNotNull($captured_url); $this->assertStringContainsString(MIDDLEWARE_URL, $captured_url); $this->assertStringContainsString('plugin-provisioner', $captured_url); // Cleanup delete_site_transient('wp-rocket-pp-activation-start-at'); } // ============================================================================ // PLUGIN ACTIVATION AND DEACTIVATION TESTS // ============================================================================ public function test_activateWpPlugin_button_not_clicked(): void { $_POST['plugin_slug'] = 'wp-rocket'; delete_site_transient('wp-rocket_activation_button_clicked_at'); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $this->assertStringContainsString('normal_reload', $output); } // ============================================================================ // ACTIVATE WP PLUGIN - ADDITIONAL COVERAGE TESTS (Lines 625-639, 664-680, 693-705) // ============================================================================ public function test_activateWpPlugin_success_activation(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the activation button clicked transient set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as installed and active $mock_active_plugins = function() { return ['wp-rocket/wp-rocket.php']; }; add_filter('pre_option_active_plugins', $mock_active_plugins); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_active_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('activated', $response['status']); $this->assertArrayHasKey('notice_html', $response); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); } public function test_activateWpPlugin_activation_failed(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the activation button clicked transient set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as installed but activation will fail $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); } public function test_activateWpPlugin_install_stats_logged(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the activation button clicked transient set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as installed and active $mock_active_plugins = function() { return ['wp-rocket/wp-rocket.php']; }; add_filter('pre_option_active_plugins', $mock_active_plugins); // Track if install stats transient is set $install_stats_key = 'wp-rocket_install_stats'; delete_site_transient($install_stats_key); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_active_plugins); $this->assertJson($output); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); delete_site_transient($install_stats_key); } public function test_activateWpPlugin_provisioner_path(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the activation button clicked transient set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not installed (so it calls provisioner) $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); } public function test_activateWpPlugin_timeout_expired_queue(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set an old activation start time to trigger timeout $old_time = current_time('timestamp') - (10 * MINUTE_IN_SECONDS); set_site_transient('wp-rocket_activation_start_at', $old_time, 3600); set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active (still in progress) $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('expired_queue', $response['status']); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); delete_site_transient('wp-rocket_activation_start_at'); } public function test_activateWpPlugin_already_in_queue(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set a recent activation start time (still in progress) set_site_transient('wp-rocket_activation_start_at', current_time('timestamp'), 3600); set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('pre_http_request', [$this, 'mock_http_failure'], 10, 3); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock plugin as not active $mock_empty_plugins = function() { return []; }; add_filter('pre_option_active_plugins', $mock_empty_plugins); ob_start(); try { $this->mp->activateWpPlugin('wp-rocket'); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('pre_http_request', [$this, 'mock_http_failure'], 10); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_empty_plugins); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('already_in_queue', $response['status']); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); delete_site_transient('wp-rocket_activation_start_at'); } public function test_pluginDeactivated_sets_option(): void { $plugin_file = 'wp-rocket/wp-rocket.php'; $option_name = 'plugin_deactivated_wp-rocket'; delete_option($option_name); $this->mp->pluginDeactivated($plugin_file, false); $option = get_option($option_name); $this->assertIsArray($option); $this->assertArrayHasKey('time', $option); $this->assertArrayHasKey('network', $option); $this->assertFalse($option['network']); } public function test_pluginDeactivated_with_network_flag(): void { $plugin_file = 'seo-by-rank-math-pro/rank-math-pro.php'; $option_name = 'plugin_deactivated_rank-math'; delete_option($option_name); $this->mp->pluginDeactivated($plugin_file, true); $option = get_option($option_name); $this->assertIsArray($option); $this->assertTrue($option['network']); } public function test_pluginDeactivated_unknown_plugin(): void { $plugin_file = 'unknown/plugin.php'; // Should not throw error, just return $result = $this->mp->pluginDeactivated($plugin_file, false); $this->assertNull($result); } // ============================================================================ // SUPPORT AND BANNER TESTS // ============================================================================ public function test_get_contact_support_link_default(): void { $link = $this->mp->get_contact_support_link(); $this->assertIsString($link); $this->assertStringContainsString('help.one.com', $link); } public function test_getTryAgainBanner_wp_rocket(): void { $html = $this->mp->getTryAgainBanner('wp-rocket'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-alert', $html); $this->assertStringContainsString('error.svg', $html); $this->assertStringContainsString('Try again', $html); $this->assertStringContainsString('help.one.com', $html); } public function test_getTryAgainBanner_rank_math(): void { $html = $this->mp->getTryAgainBanner('rank-math'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-alert', $html); $this->assertStringContainsString('Rank Math Pro', $html); } public function test_getActivateBanner_wp_rocket(): void { $html = $this->mp->getActivateBanner('wp-rocket'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-info', $html); $this->assertStringContainsString('Activate WP Rocket', $html); $this->assertStringContainsString('info.svg', $html); } public function test_getActivateBanner_rank_math(): void { $html = $this->mp->getActivateBanner('rank-math'); $this->assertIsString($html); $this->assertStringContainsString('gv-notice-info', $html); $this->assertStringContainsString('Rank Math Pro', $html); } // ============================================================================ // CLASS CONSTANTS TESTS // ============================================================================ public function test_plugin_handle_constant(): void { $reflection = new \ReflectionClass(OnecomMarketplace::class); $this->assertTrue($reflection->hasConstant('PLUGIN_HANDLE')); $plugin_handles = $reflection->getConstant('PLUGIN_HANDLE'); $this->assertArrayHasKey('wp-rocket', $plugin_handles); $this->assertArrayHasKey('rank-math', $plugin_handles); $this->assertEquals('wp-rocket/wp-rocket.php', $plugin_handles['wp-rocket']); $this->assertEquals('seo-by-rank-math-pro/rank-math-pro.php', $plugin_handles['rank-math']); } public function test_addons_slugs_constant(): void { $reflection = new \ReflectionClass(OnecomMarketplace::class); $this->assertTrue($reflection->hasConstant('ADDONS_SLUGS')); $addon_slugs = $reflection->getConstant('ADDONS_SLUGS'); $this->assertArrayHasKey('wp-rocket', $addon_slugs); $this->assertArrayHasKey('rank-math', $addon_slugs); $this->assertEquals('WP_ROCKET', $addon_slugs['wp-rocket']); $this->assertEquals('RANK_MATH', $addon_slugs['rank-math']); } public function test_expiration_time_constant(): void { $reflection = new \ReflectionClass(OnecomMarketplace::class); $this->assertTrue($reflection->hasConstant('EXPIRATION_TIME_IN_MINUTES')); $expiration = $reflection->getConstant('EXPIRATION_TIME_IN_MINUTES'); $this->assertEquals(5, $expiration); } public function test_plugin_slugs_name_constant(): void { $reflection = new \ReflectionClass(OnecomMarketplace::class); $this->assertTrue($reflection->hasConstant('PLUGIN_SLUGS_NAME')); $plugin_names = $reflection->getConstant('PLUGIN_SLUGS_NAME'); $this->assertEquals('WP Rocket', $plugin_names['wp-rocket']); $this->assertEquals('Rank Math Pro', $plugin_names['rank-math']); } public function test_item_category_constant(): void { $reflection = new \ReflectionClass(OnecomMarketplace::class); $this->assertTrue($reflection->hasConstant('ITEM_CATEGORY')); $categories = $reflection->getConstant('ITEM_CATEGORY'); $this->assertEquals('performance', $categories['wp-rocket']); $this->assertEquals('seo', $categories['rank-math']); } public function test_plugin_slugs_constant(): void { $reflection = new \ReflectionClass(OnecomMarketplace::class); $this->assertTrue($reflection->hasConstant('PLUGIN_SLUGS')); $slugs = $reflection->getConstant('PLUGIN_SLUGS'); $this->assertEquals('wp-rocket', $slugs['wp-rocket']); $this->assertEquals('seo-by-rank-math-pro', $slugs['rank-math']); } // ============================================================================ // INTEGRATION TESTS // ============================================================================ public function test_full_addon_purchase_flow_with_mocks(): void { // Setup $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['callWPApiForAddon', 'callWpApiProvisioner']) ->getMock(); $addon_data = [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET' ] ]; $this->mp->method('callWPApiForAddon')->willReturn($addon_data); // Test addon info retrieval $result = $this->mp->getAddonInfo('wp-rocket', true); $this->assertTrue($result['success']); // Test addon purchase validation $addon_const = OnecomMarketplace::ADDONS_SLUGS['wp-rocket']; $is_valid = $this->mp->validateAddonPurchase($result, $addon_const); $this->assertTrue($is_valid); } public function test_check_activate_banner_on_reload_purchased_recently(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Mock addon as purchased with recent createdAt $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $createdAt = date('Y-m-d H:i:s', current_time('timestamp') - DAY_IN_SECONDS * 15); $this->mp->method('isAddonPurchased')->willReturn([ 'is_active' => true, 'addon_info' => [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET', 'createdAt' => $createdAt ] ] ]); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); } // ============================================================================ // CHECK ACTIVATE BANNER ON RELOAD - ADDITIONAL COVERAGE TESTS // ============================================================================ public function test_checkActivateBannerOnReload_plugin_deactivated_manually(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set the option indicating plugin was manually deactivated update_option('plugin_deactivated_wp-rocket', [ 'time' => time(), 'network' => false ]); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('plugin_deactivated_manually', $response['status']); $this->assertFalse($response['show_banner']); $this->assertEquals('wp-rocket', $response['addon_slug']); // Cleanup delete_option('plugin_deactivated_wp-rocket'); } public function test_checkActivateBannerOnReload_plugin_already_active(): void { $_POST['plugin_slug'] = 'wp-rocket'; add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); // Mock the plugin as active $mock_callback = function() { return ['wp-rocket/wp-rocket.php']; }; add_filter('pre_option_active_plugins', $mock_callback); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); remove_filter('pre_option_active_plugins', $mock_callback); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('plugin_already_active', $response['status']); $this->assertFalse($response['show_banner']); } public function test_checkActivateBannerOnReload_activation_in_progress(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set transient to indicate activation is in progress set_site_transient('wp-rocket_activation_button_clicked_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('activation_in_progress', $response['status']); $this->assertFalse($response['show_banner']); // Cleanup delete_site_transient('wp-rocket_activation_button_clicked_at'); } public function test_checkActivateBannerOnReload_activation_in_progress_via_pp_activation(): void { $_POST['plugin_slug'] = 'rank-math'; // Set transient to indicate provisioning activation is in progress set_site_transient('rank-math-pp-activation-start-at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('activation_in_progress', $response['status']); $this->assertFalse($response['show_banner']); // Cleanup delete_site_transient('rank-math-pp-activation-start-at'); } public function test_checkActivateBannerOnReload_purchase_too_old(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Mock addon as purchased but more than 30 days ago $createdAt = date('Y-m-d H:i:s', current_time('timestamp') - (DAY_IN_SECONDS * 45)); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn([ 'is_active' => true, 'addon_info' => [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET', 'createdAt' => $createdAt ] ] ]); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('purchase_too_old', $response['status']); $this->assertFalse($response['show_banner']); $this->assertArrayHasKey('createdAt', $response); } public function test_checkActivateBannerOnReload_rank_math_override_slug(): void { $_POST['plugin_slug'] = 'seo-by-rank-math-pro'; // Mock addon as not purchased to ensure slug override works $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); // Return proper structure for not purchased $this->mp->method('isAddonPurchased')->willReturn([ 'is_active' => false, 'addon_info' => [] ]); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); // Should show addon_not_purchased since slug was overridden correctly $this->assertEquals('addon_not_purchased', $response['status']); } public function test_checkActivateBannerOnReload_show_activate_banner(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Mock addon as purchased recently $createdAt = date('Y-m-d H:i:s', current_time('timestamp') - (DAY_IN_SECONDS * 15)); $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased', 'getActivateBanner']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn([ 'is_active' => true, 'addon_info' => [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET', 'createdAt' => $createdAt ] ] ]); $this->mp->method('getActivateBanner')->willReturn('
Activate Banner HTML
'); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('show_activate_banner', $response['status']); $this->assertTrue($response['show_banner']); $this->assertArrayHasKey('banner_html', $response); } public function test_checkActivateBannerOnReload_addon_not_purchased(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Mock addon as not purchased $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); // Return proper structure for not purchased $this->mp->method('isAddonPurchased')->willReturn([ 'is_active' => false, 'addon_info' => [] ]); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('addon_not_purchased', $response['status']); $this->assertFalse($response['show_banner']); } public function test_checkActivateBannerOnReload_activation_start_at_transient(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Set activation_start_at transient to indicate activation in progress set_site_transient('wp-rocket_activation_start_at', time(), 300); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); $this->assertEquals('activation_in_progress', $response['status']); // Cleanup delete_site_transient('wp-rocket_activation_start_at'); } public function test_checkActivateBannerOnReload_with_no_created_at(): void { $_POST['plugin_slug'] = 'wp-rocket'; // Mock addon as purchased but without createdAt $this->mp = $this->getMockBuilder(OnecomMarketplace::class) ->onlyMethods(['isAddonPurchased']) ->getMock(); $this->mp->method('isAddonPurchased')->willReturn([ 'is_active' => true, 'addon_info' => [ 'success' => true, 'data' => [ 'source' => 'PURCHASED', 'product' => 'WP_ROCKET' // No createdAt ] ] ]); add_filter('wp_doing_ajax', '__return_true'); add_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); ob_start(); try { $this->mp->checkActivateBannerOnReload(); } catch (WPAjaxDieContinueException $e) { // Expected } $output = ob_get_clean(); remove_filter('wp_doing_ajax', '__return_true'); remove_filter('wp_die_ajax_handler', [$this, 'wp_ajax_print_handler_filter']); $this->assertJson($output); $response = json_decode($output, true); // Should default to addon_not_purchased when createdAt is missing $this->assertEquals('addon_not_purchased', $response['status']); } }