/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ assertTrue(has_action('wp_ajax_care_booking_bulk_update'), 'AJAX handler should be registered'); $this->assertFalse(has_action('wp_ajax_nopriv_care_booking_bulk_update'), 'Non-privileged AJAX should not be registered'); } /** * Test successful bulk update with mixed restrictions */ public function test_successful_bulk_update() { $this->set_current_user($this->admin_user_id); $restrictions = [ [ 'restriction_type' => 'doctor', 'target_id' => 999, 'is_blocked' => true ], [ 'restriction_type' => 'doctor', 'target_id' => 998, 'is_blocked' => false ], [ 'restriction_type' => 'service', 'target_id' => 888, 'doctor_id' => 999, 'is_blocked' => true ] ]; $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, true); // Test response structure according to contract $this->assertArrayHasKey('message', $data['data']); $this->assertArrayHasKey('updated', $data['data']); $this->assertArrayHasKey('errors', $data['data']); $this->assertEquals('Bulk update completed', $data['data']['message']); $this->assertEquals(3, $data['data']['updated']); $this->assertEmpty($data['data']['errors']); } /** * Test bulk update with some failures */ public function test_bulk_update_with_partial_failures() { $this->set_current_user($this->admin_user_id); $restrictions = [ [ 'restriction_type' => 'doctor', 'target_id' => 999, 'is_blocked' => true ], [ 'restriction_type' => 'invalid_type', // This should fail 'target_id' => 998, 'is_blocked' => true ], [ 'restriction_type' => 'service', 'target_id' => 888, 'doctor_id' => 999, 'is_blocked' => true ] ]; $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); // Partial failure should return false $this->assertEquals('Partial failure in bulk update', $data['data']['message']); $this->assertEquals(2, $data['data']['updated']); // Only 2 successful $this->assertCount(1, $data['data']['errors']); // 1 error // Check error structure $error = $data['data']['errors'][0]; $this->assertArrayHasKey('restriction', $error); $this->assertArrayHasKey('error', $error); $this->assertEquals('invalid_type', $error['restriction']['restriction_type']); } /** * Test bulk update with KiviCare target validation */ public function test_bulk_update_with_target_validation() { $this->set_current_user($this->admin_user_id); $restrictions = [ [ 'restriction_type' => 'doctor', 'target_id' => 99999, // Non-existent doctor 'is_blocked' => true ] ]; $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); $this->assertEquals(0, $data['data']['updated']); $this->assertCount(1, $data['data']['errors']); $this->assertContains('Target not found in KiviCare', $data['data']['errors'][0]['error']); } /** * Test invalid nonce returns error */ public function test_invalid_nonce_error() { $this->set_current_user($this->admin_user_id); $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => 'invalid_nonce', 'restrictions' => [] ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); $this->assertEquals('Invalid nonce', $data['data']['message']); } /** * Test insufficient permissions returns error */ public function test_insufficient_permissions_error() { $subscriber_id = $this->factory->user->create(['role' => 'subscriber']); $this->set_current_user($subscriber_id); $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => [] ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); $this->assertEquals('Insufficient permissions', $data['data']['message']); } /** * Test empty restrictions array */ public function test_empty_restrictions_array() { $this->set_current_user($this->admin_user_id); $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => [] ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, true); $this->assertEquals('Bulk update completed', $data['data']['message']); $this->assertEquals(0, $data['data']['updated']); $this->assertEmpty($data['data']['errors']); } /** * Test missing restrictions parameter */ public function test_missing_restrictions_parameter() { $this->set_current_user($this->admin_user_id); $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update') // Missing 'restrictions' parameter ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); $this->assertEquals('Missing restrictions parameter', $data['data']['message']); } /** * Test invalid restrictions format */ public function test_invalid_restrictions_format() { $this->set_current_user($this->admin_user_id); $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => 'invalid_format' // Should be array ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); $this->assertEquals('Invalid restrictions format', $data['data']['message']); } /** * Test response time performance requirement for bulk operation */ public function test_response_time_performance() { $this->set_current_user($this->admin_user_id); // Create bulk data (50 items as per contract) $restrictions = []; for ($i = 1; $i <= 50; $i++) { $restrictions[] = [ 'restriction_type' => 'doctor', 'target_id' => 900 + $i, 'is_blocked' => $i % 2 === 0 ]; } $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; $start_time = microtime(true); ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $end_time = microtime(true); $response_time = ($end_time - $start_time) * 1000; $this->assertLessThan(500, $response_time, 'Bulk update should complete in under 500ms for 50 items'); $data = json_decode($response, true); $this->assert_ajax_response($data, true); $this->assertEquals(50, $data['data']['updated']); } /** * Test transaction rollback on critical errors */ public function test_transaction_rollback_on_critical_error() { $this->set_current_user($this->admin_user_id); $restrictions = [ [ 'restriction_type' => 'doctor', 'target_id' => 999, 'is_blocked' => true ], [ 'restriction_type' => 'doctor', 'target_id' => 998, 'is_blocked' => true ] ]; // Mock database error during processing global $wpdb; // Hook into database operations to simulate error add_filter('query', function($query) { if (strpos($query, 'care_booking_restrictions') !== false && strpos($query, '998') !== false) { return 'SELECT * FROM non_existent_table'; // Force error } return $query; }); $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); // Remove filter remove_all_filters('query'); $data = json_decode($response, true); $this->assert_ajax_response($data, false); // Verify partial processing occurred with error handling $this->assertIsInt($data['data']['updated']); $this->assertIsArray($data['data']['errors']); } /** * Test cache invalidation after bulk update */ public function test_cache_invalidation_after_bulk_update() { $this->set_current_user($this->admin_user_id); // Set initial cache set_transient('care_booking_doctors_blocked', [997], 3600); set_transient('care_booking_services_blocked_999', [886], 3600); $this->assertNotFalse(get_transient('care_booking_doctors_blocked')); $this->assertNotFalse(get_transient('care_booking_services_blocked_999')); $restrictions = [ [ 'restriction_type' => 'doctor', 'target_id' => 999, 'is_blocked' => true ] ]; $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, true); // Cache should be invalidated $this->assertFalse(get_transient('care_booking_doctors_blocked')); $this->assertFalse(get_transient('care_booking_services_blocked_999')); } /** * Test WordPress action triggered after bulk update */ public function test_action_triggered_after_bulk_update() { $this->set_current_user($this->admin_user_id); $actions_fired = []; add_action('care_booking_restriction_updated', function($type, $target_id, $doctor_id = null) use (&$actions_fired) { $actions_fired[] = [$type, $target_id, $doctor_id]; }, 10, 3); $restrictions = [ [ 'restriction_type' => 'doctor', 'target_id' => 999, 'is_blocked' => true ], [ 'restriction_type' => 'service', 'target_id' => 888, 'doctor_id' => 999, 'is_blocked' => false ] ]; $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, true); // Actions should have fired for each restriction $this->assertCount(2, $actions_fired); $this->assertContains(['doctor', 999, null], $actions_fired); $this->assertContains(['service', 888, 999], $actions_fired); } /** * Test maximum bulk size limit */ public function test_maximum_bulk_size_limit() { $this->set_current_user($this->admin_user_id); // Create oversized bulk request (more than allowed) $restrictions = []; for ($i = 1; $i <= 101; $i++) { // Over 100 items limit $restrictions[] = [ 'restriction_type' => 'doctor', 'target_id' => 800 + $i, 'is_blocked' => true ]; } $_POST = [ 'action' => 'care_booking_bulk_update', 'nonce' => $this->mock_wp_nonce('care_booking_bulk_update'), 'restrictions' => $restrictions ]; ob_start(); try { do_action('wp_ajax_care_booking_bulk_update'); } catch (WPAjaxDieStopException $e) {} $response = ob_get_clean(); $data = json_decode($response, true); $this->assert_ajax_response($data, false); $this->assertEquals('Bulk size limit exceeded', $data['data']['message']); } }