test_users['active'] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'active_trainer', 'user_email' => 'active@example.com', ) ); $this->test_users['pending'] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'pending_trainer', 'user_email' => 'pending@example.com', ) ); update_user_meta( $this->test_users['pending'], 'account_status', 'pending' ); $this->test_users['disabled'] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'disabled_trainer', 'user_email' => 'disabled@example.com', ) ); update_user_meta( $this->test_users['disabled'], 'account_status', 'disabled' ); $this->test_users['opted_out'] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'opted_out_trainer', 'user_email' => 'optedout@example.com', ) ); update_user_meta( $this->test_users['opted_out'], 'hvac_announcement_emails_opt_out', 'yes' ); $this->test_users['no_email'] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'no_email_trainer', 'user_email' => '', ) ); $this->test_users['invalid_email'] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'invalid_email_trainer', 'user_email' => 'not_an_email', ) ); // Add capabilities HVAC_Announcements_Permissions::add_capabilities(); // Get email handler instance $this->email_handler = HVAC_Announcements_Email::get_instance(); } /** * Teardown after each test */ public function tearDown() { parent::tearDown(); // Clean up test users foreach ( $this->test_users as $user_id ) { wp_delete_user( $user_id ); } // Remove capabilities HVAC_Announcements_Permissions::remove_capabilities(); } /** * Test email queue with mixed user statuses */ public function test_queue_emails_mixed_statuses() { // Create test announcement $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Test Announcement', 'post_content' => 'Content', 'post_status' => 'draft', ) ); // Queue emails $reflection = new ReflectionClass( $this->email_handler ); $method = $reflection->getMethod( 'queue_announcement_emails' ); $method->setAccessible( true ); $method->invoke( $this->email_handler, $post_id ); // Get recipients $recipients = get_post_meta( $post_id, '_hvac_announcement_email_recipients', true ); // Should only include active trainer $this->assertContains( $this->test_users['active'], $recipients ); $this->assertNotContains( $this->test_users['pending'], $recipients ); $this->assertNotContains( $this->test_users['disabled'], $recipients ); $this->assertNotContains( $this->test_users['opted_out'], $recipients ); $this->assertNotContains( $this->test_users['no_email'], $recipients ); $this->assertNotContains( $this->test_users['invalid_email'], $recipients ); // Clean up wp_delete_post( $post_id, true ); } /** * Test email batch processing */ public function test_email_batch_processing() { // Create many users to test batching $many_users = array(); for ( $i = 1; $i <= 150; $i++ ) { $many_users[] = $this->factory->user->create( array( 'role' => 'hvac_trainer', 'user_login' => 'trainer_' . $i, 'user_email' => 'trainer' . $i . '@example.com', ) ); } // Create announcement $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Batch Test', 'post_status' => 'publish', ) ); // Test batch size $reflection = new ReflectionClass( $this->email_handler ); $batch_size_prop = $reflection->getProperty( 'batch_size' ); $batch_size_prop->setAccessible( true ); $batch_size = $batch_size_prop->getValue( $this->email_handler ); // Calculate expected batches $total_users = count( $many_users ) + 1; // +1 for active trainer $expected_batches = ceil( $total_users / $batch_size ); // Test chunking $chunks = array_chunk( $many_users, $batch_size ); $this->assertEquals( $expected_batches, count( $chunks ) ); // Clean up foreach ( $many_users as $user_id ) { wp_delete_user( $user_id ); } wp_delete_post( $post_id, true ); } /** * Test status transition handling */ public function test_status_transition_handling() { // Create announcement $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Transition Test', 'post_status' => 'draft', ) ); // Test draft to publish (should trigger email) $this->email_handler->handle_status_transition( 'publish', 'draft', get_post( $post_id ) ); // Check if email was queued $send_date = get_post_meta( $post_id, '_hvac_announcement_email_send_date', true ); $this->assertNotEmpty( $send_date ); // Mark as sent update_post_meta( $post_id, '_hvac_announcement_email_sent', true ); // Test publish to draft and back (should not re-send) $this->email_handler->handle_status_transition( 'draft', 'publish', get_post( $post_id ) ); $this->email_handler->handle_status_transition( 'publish', 'draft', get_post( $post_id ) ); // Email sent flag should still be true $email_sent = get_post_meta( $post_id, '_hvac_announcement_email_sent', true ); $this->assertTrue( $email_sent ); // Clean up wp_delete_post( $post_id, true ); } /** * Test email with featured image */ public function test_email_with_featured_image() { // Create announcement with featured image $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Image Test', 'post_content' => 'Content with image', 'post_status' => 'publish', ) ); // Add featured image $attachment_id = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg', $post_id ); set_post_thumbnail( $post_id, $attachment_id ); // Get email body $reflection = new ReflectionClass( $this->email_handler ); $method = $reflection->getMethod( 'get_email_body' ); $method->setAccessible( true ); $body = $method->invoke( $this->email_handler, get_post( $post_id ) ); // Should contain image $this->assertStringContainsString( 'assertStringContainsString( 'wp-post-image', $body ); // Clean up wp_delete_post( $post_id, true ); wp_delete_attachment( $attachment_id, true ); } /** * Test email with categories and tags */ public function test_email_with_taxonomies() { // Create announcement $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Taxonomy Test', 'post_status' => 'publish', ) ); // Add categories and tags $cat_ids = array(); $cat_ids[] = wp_insert_term( 'Email Cat 1', HVAC_Announcements_CPT::get_category_taxonomy() ); $cat_ids[] = wp_insert_term( 'Email Cat 2', HVAC_Announcements_CPT::get_category_taxonomy() ); wp_set_post_terms( $post_id, array( $cat_ids[0]['term_id'], $cat_ids[1]['term_id'] ), HVAC_Announcements_CPT::get_category_taxonomy() ); wp_set_post_terms( $post_id, array( 'email-tag-1', 'email-tag-2' ), HVAC_Announcements_CPT::get_tag_taxonomy() ); // Get email body $reflection = new ReflectionClass( $this->email_handler ); $method = $reflection->getMethod( 'get_fallback_email_body' ); $method->setAccessible( true ); $body = $method->invoke( $this->email_handler, get_post( $post_id ) ); // Should contain title and basic structure $this->assertStringContainsString( 'Taxonomy Test', $body ); $this->assertStringContainsString( 'Upskill HVAC', $body ); // Clean up wp_delete_post( $post_id, true ); foreach ( $cat_ids as $cat ) { if ( !is_wp_error( $cat ) ) { wp_delete_term( $cat['term_id'], HVAC_Announcements_CPT::get_category_taxonomy() ); } } } /** * Test email headers with different admin email scenarios */ public function test_email_headers_fallback() { $reflection = new ReflectionClass( $this->email_handler ); $method = $reflection->getMethod( 'get_email_headers' ); $method->setAccessible( true ); // Test with valid admin email $original_email = get_option( 'admin_email' ); update_option( 'admin_email', 'valid@example.com' ); $headers = $method->invoke( $this->email_handler ); $this->assertStringContainsString( 'valid@example.com', implode( "\n", $headers ) ); // Test with empty admin email update_option( 'admin_email', '' ); $headers = $method->invoke( $this->email_handler ); // Should use fallback $this->assertNotEmpty( $headers ); $from_header = $headers[1]; $this->assertStringContainsString( 'From:', $from_header ); // Test with invalid admin email update_option( 'admin_email', 'not-valid-email' ); $headers = $method->invoke( $this->email_handler ); // Should use fallback $this->assertNotEmpty( $headers ); // Restore original update_option( 'admin_email', $original_email ); } /** * Test retry mechanism exhaustion */ public function test_retry_exhaustion() { $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Retry Test', 'post_status' => 'publish', ) ); $reflection = new ReflectionClass( $this->email_handler ); $method = $reflection->getMethod( 'maybe_schedule_retry' ); $method->setAccessible( true ); $user_id = $this->test_users['active']; // Exhaust retries for ( $i = 1; $i <= 5; $i++ ) { $method->invoke( $this->email_handler, $post_id, $user_id ); } // Check retry count is capped at 3 $retry_count = get_user_meta( $user_id, '_hvac_announcement_email_retry_' . $post_id, true ); $this->assertEquals( 3, $retry_count ); // Clean up wp_delete_post( $post_id, true ); delete_user_meta( $user_id, '_hvac_announcement_email_retry_' . $post_id ); } /** * Test email logging with different statuses */ public function test_email_logging_statuses() { $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Log Test', 'post_status' => 'publish', ) ); $reflection = new ReflectionClass( $this->email_handler ); $method = $reflection->getMethod( 'log_email_send' ); $method->setAccessible( true ); // Log different statuses $method->invoke( $this->email_handler, $post_id, 1, 'success' ); $method->invoke( $this->email_handler, $post_id, 2, 'failed' ); $method->invoke( $this->email_handler, $post_id, 3, 'invalid_email' ); $method->invoke( $this->email_handler, $post_id, 4, 'success' ); // Get log $log = get_post_meta( $post_id, '_hvac_announcement_email_log', true ); $this->assertCount( 4, $log ); $this->assertEquals( 'success', $log[0]['status'] ); $this->assertEquals( 'failed', $log[1]['status'] ); $this->assertEquals( 'invalid_email', $log[2]['status'] ); $this->assertEquals( 'success', $log[3]['status'] ); // Test email status calculation $status = HVAC_Announcements_Email::get_email_status( $post_id ); $this->assertEquals( 2, $status['successful'] ); $this->assertEquals( 2, $status['failed'] ); // failed + invalid_email // Clean up wp_delete_post( $post_id, true ); } /** * Test email subject and body filters */ public function test_email_filters() { $post_id = wp_insert_post( array( 'post_type' => HVAC_Announcements_CPT::get_post_type(), 'post_title' => 'Filter Test', 'post_content' => 'Original content', 'post_status' => 'publish', ) ); $post = get_post( $post_id ); // Add filter to modify subject add_filter( 'hvac_announcement_email_subject', function( $subject, $post ) { return 'Modified: ' . $subject; }, 10, 2 ); // Add filter to modify body add_filter( 'hvac_announcement_email_body', function( $body, $post ) { return $body . ''; }, 10, 2 ); // Add filter to modify headers add_filter( 'hvac_announcement_email_headers', function( $headers ) { $headers[] = 'X-Custom-Header: Test'; return $headers; } ); $reflection = new ReflectionClass( $this->email_handler ); // Test subject filter $subject_method = $reflection->getMethod( 'get_email_subject' ); $subject_method->setAccessible( true ); $subject = $subject_method->invoke( $this->email_handler, $post ); $this->assertStringStartsWith( 'Modified:', $subject ); // Test body filter $body_method = $reflection->getMethod( 'get_email_body' ); $body_method->setAccessible( true ); $body = $body_method->invoke( $this->email_handler, $post ); $this->assertStringContainsString( '', $body ); // Test headers filter $headers_method = $reflection->getMethod( 'get_email_headers' ); $headers_method->setAccessible( true ); $headers = $headers_method->invoke( $this->email_handler ); $this->assertContains( 'X-Custom-Header: Test', $headers ); // Remove filters remove_all_filters( 'hvac_announcement_email_subject' ); remove_all_filters( 'hvac_announcement_email_body' ); remove_all_filters( 'hvac_announcement_email_headers' ); // Clean up wp_delete_post( $post_id, true ); } }