Commit ddc879132d for woocommerce

commit ddc879132d6f043544ee595f15db62940314d384
Author: Seun Olorunsola <30554163+triple0t@users.noreply.github.com>
Date:   Tue Dec 2 16:48:32 2025 +0100

    Fix block email content preview (#62206)

    * Switch to using postId for checking email type.

    The postId is a more authoritative way to fetch and display the associated email editor post. If the email type for the post ID does not exist, then we can be sure that the email post has not been generated.

    * Implement caching for email editor post templates.

    * Update test for WCTransactionalEmailPostsManager  class

    * Remove unused method get_email_type_class_name_from_template_name

    * Add changelog

    * Fix lint errors and CodeRabbit feedback

diff --git a/docs/block-development/reference/block-references.md b/docs/block-development/reference/block-references.md
index 64c914591b..b595f82979 100644
--- a/docs/block-development/reference/block-references.md
+++ b/docs/block-development/reference/block-references.md
@@ -931,7 +931,7 @@ A placeholder block for email content.
 -   **Ancestor:**
 -   **Parent:**
 -	**Supports:** email, ~~inserter~~
--	**Attributes:** emailType
+-	**Attributes:** emailType, postId

 ## Featured Category - woocommerce/featured-category

diff --git a/plugins/woocommerce/changelog/wooprd-1479-ciab-woocommerce-email-content-block-does-not-show-the b/plugins/woocommerce/changelog/wooprd-1479-ciab-woocommerce-email-content-block-does-not-show-the
new file mode 100644
index 0000000000..860f44985c
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooprd-1479-ciab-woocommerce-email-content-block-does-not-show-the
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Ensure block email content preview displays the correct item.
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/block.json
index 7b11a9066b..1c6133ae52 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/block.json
@@ -11,6 +11,9 @@
 	"attributes": {
 		"emailType": {
 			"type": "string"
+		},
+		"postId": {
+			"type": "integer"
 		}
 	},
 	"apiVersion": 3,
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx
index af3d736ced..bcaff96ec2 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/email-content/edit.tsx
@@ -17,18 +17,6 @@ import { store as editorStore } from '@wordpress/editor';
  */
 import metadata from './block.json';

-declare global {
-	interface Window {
-		// eslint-disable-next-line @typescript-eslint/naming-convention
-		WooCommerceEmailEditor?: {
-			email_types: Array< {
-				value: string;
-				id: string;
-			} >;
-		};
-	}
-}
-
 function HoverContent() {
 	return (
 		<div
@@ -48,24 +36,15 @@ function HoverContent() {
 	);
 }

-const getEmailType = ( value: string ) => {
-	return window.WooCommerceEmailEditor?.email_types?.find(
-		( emailType ) => emailType.value === value
-	)?.id;
-};
-
-const DEFAULT_EMAIL_TYPE = 'WC_Email_Customer_Processing_Order';
-
 export default function Edit() {
 	const [ isHovered, setIsHovered ] = useState( false );
 	const blockProps = useBlockProps();
-	const { postSlug } = useSelect(
+	const { postId } = useSelect(
 		( select ) => ( {
-			postSlug: select( editorStore ).getCurrentPost?.()?.slug,
+			postId: select( editorStore ).getCurrentPostId?.() ?? 0,
 		} ),
 		[]
 	);
-	const emailType = getEmailType( postSlug || '' ) || DEFAULT_EMAIL_TYPE;

 	return (
 		<div
@@ -82,7 +61,7 @@ export default function Edit() {
 		>
 			<ServerSideRender
 				block={ metadata.name }
-				attributes={ { emailType } }
+				attributes={ { postId } }
 			></ServerSideRender>

 			{ isHovered && (
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php b/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php
index d7cbc0f6f2..33e590b382 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/EmailContent.php
@@ -4,8 +4,8 @@ namespace Automattic\WooCommerce\Blocks\BlockTypes;

 use Automattic\WooCommerce\Blocks\BlockTypes\AbstractBlock;
 use Automattic\WooCommerce\Internal\EmailEditor\BlockEmailRenderer;
-use Automattic\WooCommerce\Internal\EmailEditor\WooContentProcessor;
 use Automattic\WooCommerce\Internal\Admin\EmailPreview\EmailPreview;
+use Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails\WCTransactionalEmailPostsManager;

 /**
  * EmailContent class.
@@ -66,7 +66,11 @@ class EmailContent extends AbstractBlock {
 		$email_preview = wc_get_container()->get( EmailPreview::class );

 		$type_param = EmailPreview::DEFAULT_EMAIL_TYPE;
-		if ( isset( $attributes['emailType'] ) ) {
+
+		if ( isset( $attributes['postId'] ) ) {
+			$email_type_class_name = WCTransactionalEmailPostsManager::get_instance()->get_email_type_class_name_from_post_id( $attributes['postId'] );
+			$type_param            = ! empty( $email_type_class_name ) ? $email_type_class_name : $type_param;
+		} elseif ( isset( $attributes['emailType'] ) ) {
 			$type_param = sanitize_text_field( wp_unslash( $attributes['emailType'] ) );
 		}

diff --git a/plugins/woocommerce/src/Internal/EmailEditor/Integration.php b/plugins/woocommerce/src/Internal/EmailEditor/Integration.php
index 9599a1da88..332bb0bc05 100644
--- a/plugins/woocommerce/src/Internal/EmailEditor/Integration.php
+++ b/plugins/woocommerce/src/Internal/EmailEditor/Integration.php
@@ -218,7 +218,7 @@ class Integration {

 		$post_manager = WCTransactionalEmailPostsManager::get_instance();

-		$email_type = $post_manager->get_email_type_from_post_id( $post_id );
+		$email_type = $post_manager->get_email_type_from_post_id( $post_id, true );

 		if ( empty( $email_type ) ) {
 			return;
@@ -319,10 +319,10 @@ class Integration {
 	 * @return array The updated personalizer context.
 	 */
 	public function update_send_preview_email_personalizer_context( $context ) {
-		$post_manager             = WCTransactionalEmailPostsManager::get_instance();
-		$email_type_template_name = $post_manager->get_email_type_from_post_id( get_the_ID() );
-		$email_type               = $email_type_template_name ? $post_manager->get_email_type_class_name_from_template_name( $email_type_template_name ) : EmailPreview::DEFAULT_EMAIL_TYPE;
-		$email_preview            = wc_get_container()->get( EmailPreview::class );
+		$post_manager  = WCTransactionalEmailPostsManager::get_instance();
+		$email_id      = $post_manager->get_email_type_from_post_id( get_the_ID() );
+		$email_type    = $email_id ? $post_manager->get_email_type_class_name_from_email_id( $email_id ) : EmailPreview::DEFAULT_EMAIL_TYPE;
+		$email_preview = wc_get_container()->get( EmailPreview::class );

 		try {
 			$email_preview->set_email_type( $email_type );
diff --git a/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManager.php b/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManager.php
index f344cf8ef2..b523115f5b 100644
--- a/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManager.php
+++ b/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManager.php
@@ -10,6 +10,20 @@ namespace Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails;
 class WCTransactionalEmailPostsManager {
 	const WC_OPTION_NAME = 'woocommerce_email_templates_%_post_id';

+	/**
+	 * Cache group for email template lookups.
+	 *
+	 * @var string
+	 */
+	const CACHE_GROUP = 'wc_block_email_templates';
+
+	/**
+	 * Cache expiration time in seconds (1 week).
+	 *
+	 * @var int
+	 */
+	const CACHE_EXPIRATION = WEEK_IN_SECONDS;
+
 	/**
 	 * Singleton instance of the class.
 	 *
@@ -17,6 +31,20 @@ class WCTransactionalEmailPostsManager {
 	 */
 	private static $instance = null;

+	/**
+	 * In-memory cache for post_id to email_type lookups within the same request.
+	 *
+	 * @var array<int|string, string|null>
+	 */
+	private $post_id_to_email_type_cache = array();
+
+	/**
+	 * In-memory cache for email class name (e.g. 'WC_Email_Customer_New_Account') lookups within the same request.
+	 *
+	 * @var array<string, string|null>
+	 */
+	private $email_class_name_cache = array();
+
 	/**
 	 * Gets the singleton instance of the class.
 	 *
@@ -56,15 +84,40 @@ class WCTransactionalEmailPostsManager {
 	/**
 	 * Retrieves the WooCommerce email type from the options table when post ID is provided.
 	 *
+	 * Uses multi-level caching:
+	 * 1. In-memory cache for the same request
+	 * 2. WordPress object cache for cross-request caching
+	 * 3. Database query if cache is not available.
+	 *
 	 * @param int|string $post_id The post ID.
+	 * @param bool       $skip_cache Whether to skip the cache. Defaults to false.
 	 * @return string|null The WooCommerce email type if found, null otherwise.
 	 */
-	public function get_email_type_from_post_id( $post_id ) {
+	public function get_email_type_from_post_id( $post_id, $skip_cache = false ) {
 		// Early return if post_id is invalid.
 		if ( empty( $post_id ) ) {
 			return null;
 		}

+		$post_id   = (int) $post_id;
+		$cache_key = $this->get_cache_key_for_post_id( $post_id );
+
+		if ( ! $skip_cache ) {
+			// Check in-memory cache first (fastest).
+			if ( array_key_exists( $post_id, $this->post_id_to_email_type_cache ) ) {
+				return $this->post_id_to_email_type_cache[ $post_id ];
+			}
+
+			// Check WordPress object cache.
+			$email_type = wp_cache_get( $cache_key, self::CACHE_GROUP );
+
+			if ( ! empty( $email_type ) ) {
+				$this->post_id_to_email_type_cache[ $post_id ] = $email_type;
+				return $email_type;
+			}
+		}
+
+		// Cache miss - perform database query.
 		global $wpdb;

 		$option_name = $wpdb->get_var(
@@ -79,7 +132,13 @@ class WCTransactionalEmailPostsManager {
 			return null;
 		}

-		return $this->get_email_type_from_option_name( $option_name );
+		$email_type = $this->get_email_type_from_option_name( $option_name );
+
+		// Store in both caches.
+		$this->post_id_to_email_type_cache[ $post_id ] = $email_type;
+		wp_cache_set( $cache_key, $email_type, self::CACHE_GROUP, self::CACHE_EXPIRATION );
+
+		return $email_type;
 	}

 	/**
@@ -102,18 +161,50 @@ class WCTransactionalEmailPostsManager {
 	 */
 	public function save_email_template_post_id( $email_type, $post_id ) {
 		$option_name = $this->get_option_name( $email_type );
+
+		$previous_id = get_option( $option_name );
+
 		update_option( $option_name, $post_id );
+
+		// Invalidate caches for the previous mapping (if any).
+		if ( ! empty( $previous_id ) ) {
+			$this->invalidate_cache_for_template( (int) $previous_id, 'post_id' );
+		}
+
+		// Invalidate cache for the new post_id.
+		$this->invalidate_cache_for_template( $email_type, 'email_type' );
+
+		// Update in-memory caches with the new values.
+		$this->post_id_to_email_type_cache[ $post_id ] = $email_type;
+		wp_cache_set( $this->get_cache_key_for_post_id( $post_id ), $email_type, self::CACHE_GROUP, self::CACHE_EXPIRATION );
 	}

 	/**
 	 * Gets the post ID for a specific email template type.
 	 *
+	 * Uses multi-level caching for improved performance.
+	 *
 	 * @param string $email_type The type of email template e.g. 'customer_new_account' from the WC_Email->id property.
 	 * @return int|false The post ID if found, false otherwise.
 	 */
 	public function get_email_template_post_id( $email_type ) {
+		// Check in-memory cache first.
+		$post_id_from_cache = array_search( $email_type, $this->post_id_to_email_type_cache, true );
+		if ( false !== $post_id_from_cache ) {
+			return $post_id_from_cache;
+		}
+
 		$option_name = $this->get_option_name( $email_type );
-		return get_option( $option_name );
+		$post_id     = get_option( $option_name );
+
+		if ( ! empty( $post_id ) ) {
+			$post_id = (int) $post_id;
+
+			// Store in in-memory cache.
+			$this->post_id_to_email_type_cache[ $post_id ] = $email_type;
+		}
+
+		return $post_id;
 	}

 	/**
@@ -123,10 +214,62 @@ class WCTransactionalEmailPostsManager {
 	 */
 	public function delete_email_template( $email_type ) {
 		$option_name = $this->get_option_name( $email_type );
-		if ( ! get_option( $option_name ) ) {
+		$post_id     = get_option( $option_name );
+
+		if ( ! $post_id ) {
 			return;
 		}
+
 		delete_option( $option_name );
+
+		// Invalidate cache.
+		$this->invalidate_cache_for_template( $post_id, 'post_id' );
+	}
+
+	/**
+	 * Invalidates cache entries for a specific post ID or email type.
+	 *
+	 * @param int|string $value The value to invalidate cache for.
+	 * @param string     $type The type of value to invalidate cache for. Can be 'post_id' or 'email_type'.
+	 * @return void
+	 */
+	private function invalidate_cache_for_template( $value, $type = 'post_id' ) {
+		$post_id_array = array();
+		if ( 'post_id' === $type ) {
+			$post_id_array[] = (int) $value;
+		} elseif ( 'email_type' === $type ) {
+			// Get all the post IDs that map to the email type.
+			$post_id_array = array_merge( $post_id_array, array_unique( array_keys( $this->post_id_to_email_type_cache, $value, true ) ) );
+		}
+
+		foreach ( $post_id_array as $post_id ) {
+			unset( $this->post_id_to_email_type_cache[ $post_id ] );
+
+			// Delete from WordPress object cache.
+			$cache_key = $this->get_cache_key_for_post_id( $post_id );
+			wp_cache_delete( $cache_key, self::CACHE_GROUP );
+		}
+	}
+
+	/**
+	 * Clears all in-memory caches.
+	 *
+	 * Useful for testing and debugging. Note that this only clears in-memory caches,
+	 * not the WordPress object cache entries (which will expire naturally).
+	 */
+	public function clear_caches() {
+		$this->post_id_to_email_type_cache = array();
+		$this->email_class_name_cache      = array();
+	}
+
+	/**
+	 * Gets the cache key for a specific post ID.
+	 *
+	 * @param int $post_id The post ID.
+	 * @return string The cache key e.g. 'post_id_to_email_type_123'.
+	 */
+	public function get_cache_key_for_post_id( $post_id ) {
+		return 'post_id_to_email_type_' . $post_id;
 	}

 	/**
@@ -156,19 +299,11 @@ class WCTransactionalEmailPostsManager {
 		);
 	}

-	/**
-	 * Gets the email type class name from the template name.
-	 *
-	 * @param string $email_template_name The template name of the email type.
-	 * @return string The email type class name.
-	 */
-	public function get_email_type_class_name_from_template_name( $email_template_name ) {
-		return 'WC_Email_' . implode( '_', array_map( 'ucfirst', explode( '_', $email_template_name ) ) );
-	}
-
 	/**
 	 * Gets the email type class name, e.g. 'WC_Email_Customer_New_Account' from the email ID (e.g. 'customer_new_account' from the WC_Email->id property).
 	 *
+	 * Uses in-memory caching to avoid repeated iterations through all registered emails.
+	 *
 	 * @param string $email_id The email ID.
 	 * @return string|null The email type class name.
 	 */
@@ -178,18 +313,25 @@ class WCTransactionalEmailPostsManager {
 			return null;
 		}

+		// Check in-memory cache first.
+		if ( isset( $this->email_class_name_cache[ $email_id ] ) ) {
+			return $this->email_class_name_cache[ $email_id ];
+		}
+
 		/**
 		 * Get all the emails registered in WooCommerce.
 		 *
 		 * @var \WC_Email[]
 		 */
 		$emails = WC()->mailer()->get_emails();
+
+		// Build the cache for all emails at once to avoid repeated iterations.
 		foreach ( $emails as $email ) {
-			if ( $email->id === $email_id ) {
-				return get_class( $email );
-			}
+			$this->email_class_name_cache[ $email->id ] = get_class( $email );
 		}
-		return null;
+
+		// Return the requested email class name if it exists.
+		return $this->email_class_name_cache[ $email_id ] ?? null;
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManagerTest.php b/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManagerTest.php
index dc3b6bf9b3..286ddf64d0 100644
--- a/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManagerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsManagerTest.php
@@ -140,19 +140,18 @@ class WCTransactionalEmailPostsManagerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test that get_email_type_class_name_from_template_name converts correctly.
+	 * Test that get_email_type_class_name_from_email_id converts correctly.
 	 */
-	public function testGetEmailTypeClassNameFromTemplateNameConvertsCorrectly(): void {
+	public function testGetEmailTypeClassNameFromEmailIdConvertsCorrectly(): void {
 		$test_cases = array(
 			'customer_new_account'      => 'WC_Email_Customer_New_Account',
-			'admin_new_order'           => 'WC_Email_Admin_New_Order',
+			'new_order'                 => 'WC_Email_New_Order',
 			'customer_processing_order' => 'WC_Email_Customer_Processing_Order',
-			'simple_name'               => 'WC_Email_Simple_Name',
-			'single'                    => 'WC_Email_Single',
+			'customer_cancelled_order'  => 'WC_Email_Customer_Cancelled_Order',
 		);

-		foreach ( $test_cases as $template_name => $expected_class_name ) {
-			$result = $this->template_manager->get_email_type_class_name_from_template_name( $template_name );
+		foreach ( $test_cases as $email_id => $expected_class_name ) {
+			$result = $this->template_manager->get_email_type_class_name_from_email_id( $email_id );
 			$this->assertEquals( $expected_class_name, $result );
 		}
 	}
@@ -216,11 +215,219 @@ class WCTransactionalEmailPostsManagerTest extends \WC_Unit_Test_Case {
 		$this->assertEquals( $post_id, get_option( $expected_option_name ) );
 	}

+	/**
+	 * Test that get_email_type_from_post_id uses in-memory cache on subsequent calls.
+	 */
+	public function testGetEmailTypeFromPostIdUsesInMemoryCache(): void {
+		$post_id = $this->factory->post->create();
+		$this->template_manager->save_email_template_post_id( 'cached_email', $post_id );
+
+		// First call should hit database and populate cache.
+		$result1 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertEquals( 'cached_email', $result1 );
+
+		// Delete the option to verify cache is used.
+		delete_option( 'woocommerce_email_templates_cached_email_post_id' );
+
+		$cache_key = $this->template_manager->get_cache_key_for_post_id( $post_id );
+		wp_cache_delete( $cache_key, WCTransactionalEmailPostsManager::CACHE_GROUP );
+
+		// Second call should use in-memory cache, not database.
+		$result2 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertEquals( 'cached_email', $result2 );
+	}
+
+	/**
+	 * Test that get_email_type_from_post_id uses wp object cache on subsequent calls.
+	 */
+	public function testGetEmailTypeFromPostIdUsesWpObjectCache(): void {
+		$post_id = $this->factory->post->create();
+		$this->template_manager->save_email_template_post_id( 'wp_cached_email', $post_id );
+
+		// First call should hit database and populate all caches.
+		$result1 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertEquals( 'wp_cached_email', $result1 );
+
+		// Delete the option to verify cache is used.
+		delete_option( 'woocommerce_email_templates_wp_cached_email_post_id' );
+
+		// Clear the in-memory cache.
+		$this->template_manager->clear_caches();
+
+		// Second call should use wp object cache, not database.
+		$result2 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertEquals( 'wp_cached_email', $result2 );
+
+		// Clear the wp object cache.
+		$this->template_manager->clear_caches();
+		$cache_key = $this->template_manager->get_cache_key_for_post_id( $post_id );
+		wp_cache_delete( $cache_key, WCTransactionalEmailPostsManager::CACHE_GROUP );
+
+		// Third call should hit database and return null.
+		$result3 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertNull( $result3 );
+	}
+
+	/**
+	 * Test that get_email_type_from_post_id skip_cache parameter bypasses cache.
+	 */
+	public function testGetEmailTypeFromPostIdSkipCacheBypassesCache(): void {
+		$post_id = $this->factory->post->create();
+		$this->template_manager->save_email_template_post_id( 'skip_cache_email', $post_id );
+
+		// First call populates cache.
+		$result1 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertEquals( 'skip_cache_email', $result1 );
+
+		// Delete the option.
+		delete_option( 'woocommerce_email_templates_skip_cache_email_post_id' );
+
+		// Call with skip_cache should hit database and return null.
+		$result2 = $this->template_manager->get_email_type_from_post_id( $post_id, true );
+		$this->assertNull( $result2 );
+	}
+
+	/**
+	 * Test that get_email_type_from_post_id returns null for empty post_id.
+	 */
+	public function testGetEmailTypeFromPostIdReturnsNullForEmptyPostId(): void {
+		$this->assertNull( $this->template_manager->get_email_type_from_post_id( '' ) );
+		$this->assertNull( $this->template_manager->get_email_type_from_post_id( 0 ) );
+		$this->assertNull( $this->template_manager->get_email_type_from_post_id( null ) );
+	}
+
+	/**
+	 * Test that get_email_template_post_id uses wp object cache on subsequent calls.
+	 */
+	public function testGetEmailTemplatePostIdUsesWpObjectCache(): void {
+		$post_id = $this->factory->post->create();
+		$this->template_manager->save_email_template_post_id( 'template_wp_cached_email', $post_id );
+
+		// First call populates cache.
+		$result1 = $this->template_manager->get_email_template_post_id( 'template_wp_cached_email' );
+		$this->assertEquals( $post_id, $result1 );
+
+		// Delete the option to verify cache is used.
+		delete_option( 'woocommerce_email_templates_template_wp_cached_email_post_id' );
+
+		// Second call should use in-memory cache, not database.
+		$result2 = $this->template_manager->get_email_template_post_id( 'template_wp_cached_email' );
+		$this->assertEquals( $post_id, $result2 );
+
+		// Clear the in-memory cache.
+		$this->template_manager->clear_caches();
+
+		// Third call should use get_option and return false.
+		$result3 = $this->template_manager->get_email_template_post_id( 'template_wp_cached_email' );
+		$this->assertFalse( $result3 );
+	}
+
+	/**
+	 * Test that save_email_template_post_id updates in-memory cache.
+	 */
+	public function testSaveEmailTemplatePostIdUpdatesInMemoryCache(): void {
+		$post_id_1 = $this->factory->post->create();
+		$post_id_2 = $this->factory->post->create();
+
+		// Save first post ID.
+		$this->template_manager->save_email_template_post_id( 'cache_update_email', $post_id_1 );
+		$this->assertEquals( $post_id_1, $this->template_manager->get_email_template_post_id( 'cache_update_email' ) );
+		$this->assertEquals( 'cache_update_email', $this->template_manager->get_email_type_from_post_id( $post_id_1 ) );
+
+		// Save second post ID - should update cache.
+		$this->template_manager->save_email_template_post_id( 'cache_update_email', $post_id_2 );
+		$this->assertEquals( $post_id_2, $this->template_manager->get_email_template_post_id( 'cache_update_email' ) );
+		$this->assertEquals( 'cache_update_email', $this->template_manager->get_email_type_from_post_id( $post_id_2 ) );
+	}
+
+	/**
+	 * Test that delete_email_template invalidates cache.
+	 */
+	public function testDeleteEmailTemplateInvalidatesCache(): void {
+		$post_id = $this->factory->post->create();
+		$this->template_manager->save_email_template_post_id( 'delete_cache_email', $post_id );
+
+		// Verify the template exists.
+		$this->assertEquals( 'delete_cache_email', $this->template_manager->get_email_type_from_post_id( $post_id ) );
+
+		// Delete the template.
+		$this->template_manager->delete_email_template( 'delete_cache_email' );
+
+		// Cache should be invalidated - the result should be null.
+		$this->assertNull( $this->template_manager->get_email_type_from_post_id( $post_id ) );
+	}
+
+	/**
+	 * Test that get_email_type_class_name_from_email_id returns null for invalid email_id.
+	 */
+	public function testGetEmailTypeClassNameFromEmailIdReturnsNullForInvalidEmailId(): void {
+		$result = $this->template_manager->get_email_type_class_name_from_email_id( 'non_existent_email_type' );
+		$this->assertNull( $result );
+	}
+
+	/**
+	 * Test caching behavior when post_id is provided as string.
+	 */
+	public function testCachingWithStringPostId(): void {
+		$post_id = $this->factory->post->create();
+		$this->template_manager->save_email_template_post_id( 'string_id_email', $post_id );
+
+		// Pass post_id as string.
+		$result = $this->template_manager->get_email_type_from_post_id( (string) $post_id );
+		$this->assertEquals( 'string_id_email', $result );
+
+		// Second call with int should use the same cache entry.
+		$result2 = $this->template_manager->get_email_type_from_post_id( $post_id );
+		$this->assertEquals( 'string_id_email', $result2 );
+	}
+
+	/**
+	 * Test that invalidate_cache_for_template invalidates cache.
+	 */
+	public function testInvalidateCacheForTemplate(): void {
+		$post_id   = $this->factory->post->create();
+		$cache_key = $this->template_manager->get_cache_key_for_post_id( $post_id );
+
+		$this->template_manager->save_email_template_post_id( 'custom_test_email', $post_id );
+		$this->assertEquals( 'custom_test_email', $this->template_manager->get_email_type_from_post_id( $post_id ) );
+
+		$reflection = new \ReflectionClass( WCTransactionalEmailPostsManager::class );
+		$method     = $reflection->getMethod( 'invalidate_cache_for_template' );
+		$method->setAccessible( true );
+		$method->invoke( $this->template_manager, $post_id, 'post_id' );
+
+		// delete option in database.
+		delete_option( 'woocommerce_email_templates_custom_test_email_post_id' );
+
+		$this->assertNull( $this->template_manager->get_email_type_from_post_id( $post_id ) );
+
+		// does nothing if the post_id is not found in the cache.
+		$method->invoke( $this->template_manager, 999999, 'post_id' );
+
+		wp_cache_set( $cache_key, 'custom_test_email', WCTransactionalEmailPostsManager::CACHE_GROUP );
+		// set post_id_to_email_type_cache using reflection.
+		$reflection = new \ReflectionClass( WCTransactionalEmailPostsManager::class );
+		$property   = $reflection->getProperty( 'post_id_to_email_type_cache' );
+		$property->setAccessible( true );
+		$property->setValue( $this->template_manager, array( $post_id => 'custom_test_email' ) );
+
+		// confirm wp cache is set.
+		$this->assertEquals( 'custom_test_email', wp_cache_get( $cache_key, WCTransactionalEmailPostsManager::CACHE_GROUP ) );
+
+		// confirm cache is cleared for email_type.
+		$method->invoke( $this->template_manager, 'custom_test_email', 'email_type' );
+		$this->assertNull( $this->template_manager->get_email_type_from_post_id( $post_id ) );
+
+		// confirm wp cache is cleared.
+		$this->assertFalse( wp_cache_get( $cache_key, WCTransactionalEmailPostsManager::CACHE_GROUP ) );
+	}
+
 	/**
 	 * Cleanup after test.
 	 */
 	public function tearDown(): void {
 		parent::tearDown();
+		$this->template_manager->clear_caches();
 		update_option( 'woocommerce_feature_block_email_editor_enabled', 'no' );
 	}
 }