master_trainer = $this->factory->user->create( array(
            'role' => 'hvac_master_trainer',
            'user_login' => 'master_trainer',
            'user_email' => 'master@example.com',
        ) );
        
        $this->regular_trainer = $this->factory->user->create( array(
            'role' => 'hvac_trainer',
            'user_login' => 'regular_trainer',
            'user_email' => 'trainer@example.com',
        ) );
        
        // Add capabilities
        HVAC_Announcements_Permissions::add_capabilities();
        
        // Create test announcements
        $this->test_announcements['published'] = wp_insert_post( array(
            'post_type' => HVAC_Announcements_CPT::get_post_type(),
            'post_title' => 'Published Announcement',
            'post_content' => 'Published content',
            'post_status' => 'publish',
            'post_author' => $this->master_trainer,
        ) );
        
        $this->test_announcements['draft'] = wp_insert_post( array(
            'post_type' => HVAC_Announcements_CPT::get_post_type(),
            'post_title' => 'Draft Announcement',
            'post_content' => 'Draft content',
            'post_status' => 'draft',
            'post_author' => $this->master_trainer,
        ) );
        
        $this->test_announcements['private'] = wp_insert_post( array(
            'post_type' => HVAC_Announcements_CPT::get_post_type(),
            'post_title' => 'Private Announcement',
            'post_content' => 'Private content',
            'post_status' => 'private',
            'post_author' => $this->master_trainer,
        ) );
        
        // Get ajax handler instance
        $this->ajax_handler = HVAC_Announcements_Ajax::get_instance();
    }
    
    /**
     * Teardown after each test
     */
    public function tearDown() {
        parent::tearDown();
        
        // Clean up test announcements
        foreach ( $this->test_announcements as $post_id ) {
            wp_delete_post( $post_id, true );
        }
        
        // Clean up test users
        wp_delete_user( $this->master_trainer );
        wp_delete_user( $this->regular_trainer );
        
        // Remove capabilities
        HVAC_Announcements_Permissions::remove_capabilities();
    }
    
    /**
     * Test rate limiting
     */
    public function test_rate_limiting() {
        wp_set_current_user( $this->master_trainer );
        
        $reflection = new ReflectionClass( $this->ajax_handler );
        $method = $reflection->getMethod( 'check_rate_limit' );
        $method->setAccessible( true );
        
        // First 30 requests should pass
        for ( $i = 1; $i <= 30; $i++ ) {
            $result = $method->invoke( $this->ajax_handler );
            $this->assertTrue( $result );
        }
        
        // 31st request should be rate limited
        // Note: This would normally exit, so we can't test directly
        // Instead, check the transient
        $transient_key = 'hvac_ajax_rate_' . $this->master_trainer;
        $request_count = get_transient( $transient_key );
        $this->assertEquals( 30, $request_count );
    }
    
    /**
     * Test cache key generation
     */
    public function test_cache_key_generation() {
        // Test that cache keys are different for different parameters
        $cache_version = wp_cache_get( 'announcements_cache_version', 'hvac_announcements' );
        if ( false === $cache_version ) {
            $cache_version = 1;
        }
        
        $key1 = 'hvac_announcements_v' . $cache_version . '_' . md5( serialize( array(
            'page' => 1,
            'per_page' => 20,
            'status' => 'publish',
            'search' => '',
            'is_master' => true
        ) ) );
        
        $key2 = 'hvac_announcements_v' . $cache_version . '_' . md5( serialize( array(
            'page' => 2,
            'per_page' => 20,
            'status' => 'publish',
            'search' => '',
            'is_master' => true
        ) ) );
        
        $this->assertNotEquals( $key1, $key2 );
        
        // Test that cache keys are different for different user roles
        $key_master = 'hvac_announcements_v' . $cache_version . '_' . md5( serialize( array(
            'page' => 1,
            'per_page' => 20,
            'status' => 'any',
            'search' => '',
            'is_master' => true
        ) ) );
        
        $key_regular = 'hvac_announcements_v' . $cache_version . '_' . md5( serialize( array(
            'page' => 1,
            'per_page' => 20,
            'status' => 'any',
            'search' => '',
            'is_master' => false
        ) ) );
        
        $this->assertNotEquals( $key_master, $key_regular );
    }
    
    /**
     * Test cache invalidation
     */
    public function test_cache_invalidation() {
        // Get current cache version
        $version_before = wp_cache_get( 'announcements_cache_version', 'hvac_announcements' );
        if ( false === $version_before ) {
            $version_before = 1;
        }
        
        // Clear cache
        $this->ajax_handler->clear_announcements_cache();
        
        // Get new cache version
        $version_after = wp_cache_get( 'announcements_cache_version', 'hvac_announcements' );
        
        // Version should be incremented
        $this->assertEquals( $version_before + 1, $version_after );
    }
    
    /**
     * Test content sanitization in get_announcements
     */
    public function test_content_sanitization() {
        // Create announcement with potentially malicious content
        $malicious_id = wp_insert_post( array(
            'post_type' => HVAC_Announcements_CPT::get_post_type(),
            'post_title' => 'Test  Title',
            'post_excerpt' => 'Test  excerpt',
            'post_content' => 'Test content with ',
            'post_status' => 'publish',
            'post_author' => $this->master_trainer,
        ) );
        
        // The excerpt should be sanitized with wp_kses_post
        $post = get_post( $malicious_id );
        $excerpt = wp_kses_post( get_the_excerpt( $post ) );
        
        // Should not contain script tags
        $this->assertStringNotContainsString( '', $excerpt );
        
        // Clean up
        wp_delete_post( $malicious_id, true );
    }
    
    /**
     * Test permission checks for different endpoints
     */
    public function test_permission_checks() {
        // Test that get_categories checks permissions
        $reflection = new ReflectionClass( $this->ajax_handler );
        $get_categories = $reflection->getMethod( 'get_categories' );
        $get_categories->setAccessible( true );
        
        // As regular trainer (should fail for creation endpoints)
        wp_set_current_user( $this->regular_trainer );
        $_POST['nonce'] = wp_create_nonce( 'hvac_announcements_nonce' );
        
        // This would normally send JSON and exit, so we can't test directly
        // Instead, test the permission check function
        $this->assertFalse( HVAC_Announcements_Permissions::current_user_can_create() );
        
        // As master trainer (should pass)
        wp_set_current_user( $this->master_trainer );
        $this->assertTrue( HVAC_Announcements_Permissions::current_user_can_create() );
    }
    
    /**
     * Test view announcement permission check
     */
    public function test_view_announcement_permissions() {
        // Regular trainer should be able to view published announcements
        wp_set_current_user( $this->regular_trainer );
        $this->assertTrue( HVAC_Announcements_Permissions::current_user_can_read() );
        
        // But should not see draft/private status in queries
        $is_master = HVAC_Announcements_Permissions::is_master_trainer( $this->regular_trainer );
        $this->assertFalse( $is_master );
    }
    
    /**
     * Test singleton pattern
     */
    public function test_singleton_pattern() {
        $instance1 = HVAC_Announcements_Ajax::get_instance();
        $instance2 = HVAC_Announcements_Ajax::get_instance();
        
        $this->assertSame( $instance1, $instance2 );
    }
    
    /**
     * Test nonce verification requirement
     */
    public function test_nonce_verification() {
        wp_set_current_user( $this->master_trainer );
        
        // Without nonce, requests should fail
        unset( $_POST['nonce'] );
        
        // Test with valid nonce
        $_POST['nonce'] = wp_create_nonce( 'hvac_announcements_nonce' );
        $valid_nonce = check_ajax_referer( 'hvac_announcements_nonce', 'nonce', false );
        $this->assertTrue( $valid_nonce );
        
        // Test with invalid nonce
        $_POST['nonce'] = 'invalid_nonce';
        $invalid_nonce = check_ajax_referer( 'hvac_announcements_nonce', 'nonce', false );
        $this->assertFalse( $invalid_nonce );
    }
}