Commit ac192ff3ac8 for woocommerce

commit ac192ff3ac8e7ba21219d21ec6d2fa93d7a0326e
Author: Seghir Nadir <nadir.seghir@gmail.com>
Date:   Thu May 21 10:54:42 2026 +0200

    Fix deferred email and checkout PHPUnit assertions (#65226)

    * Fix email and checkout CI assertions

    * Add changelog for email and checkout CI fix

    * Clarify checkout retry session assertion

diff --git a/plugins/woocommerce/changelog/fix-email-checkout-ci-tests b/plugins/woocommerce/changelog/fix-email-checkout-ci-tests
new file mode 100644
index 00000000000..871b6afcdb2
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-email-checkout-ci-tests
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Fix PHPUnit assertions for deferred email queue object round trips and Store API checkout failed-payment retries.
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
index b3bd5733a28..a918122f908 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
@@ -2246,7 +2246,18 @@ class Checkout extends MockeryTestCase {
 		remove_action( 'woocommerce_store_api_checkout_order_processed', $fail_hook, 999 );

 		// Second POST on the same session should reuse the existing pending order.
-		$second_response = rest_get_server()->dispatch( $this->build_valid_post_request() );
+		$session_order_id_during_retry = null;
+		$capture_session_order_id      = function () use ( &$session_order_id_during_retry ) {
+			$session_order_id_during_retry = (int) WC()->session->get( 'store_api_draft_order' );
+		};
+		add_action( 'woocommerce_store_api_checkout_order_processed', $capture_session_order_id, 999, 0 );
+
+		try {
+			$second_response = rest_get_server()->dispatch( $this->build_valid_post_request() );
+		} finally {
+			remove_action( 'woocommerce_store_api_checkout_order_processed', $capture_session_order_id, 999 );
+		}
+
 		$this->assertEquals( 200, $second_response->get_status(), print_r( $second_response->get_data(), true ) );

 		$second_order_id = (int) $second_response->get_data()['order_id'];
@@ -2255,7 +2266,8 @@ class Checkout extends MockeryTestCase {
 			$second_order_id,
 			'Second POST must reuse the existing pending order, not create a new one (regression: issue #64792).'
 		);
-		$this->assertSame( $first_order_id, (int) WC()->session->get( 'store_api_draft_order' ), 'Session pointer should still reference the reused order.' );
+		$this->assertSame( $first_order_id, $session_order_id_during_retry, 'Session pointer should reference the reused order while the second POST is being processed.' );
+		$this->assertEmpty( WC()->session->get( 'store_api_draft_order' ), 'Successful checkout should clear the draft order pointer when the cart is emptied.' );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php b/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php
index 5ccb2a4deb9..862fada93cd 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Email/DeferredEmailQueueTest.php
@@ -469,8 +469,7 @@ class DeferredEmailQueueTest extends WC_Unit_Test_Case {
 		$this->sut->push( $filter, $args );
 		$this->sut->dispatch();

-		$queue         = $this->get_test_queue();
-		$scheduled_arg = $queue->actions[0]['args'];
+		$scheduled_arg = $this->get_scheduled_email_action()['args'];
 		$encoded_arg   = wp_json_encode( $scheduled_arg );

 		$this->assert_queued_object_reference( $scheduled_arg[1][ $wrapped_position ], $expected_type, $expected_id );
@@ -529,6 +528,30 @@ class DeferredEmailQueueTest extends WC_Unit_Test_Case {
 		);
 	}

+	/**
+	 * Get the scheduled Action Scheduler email action from the test queue.
+	 *
+	 * Other fixture setup can enqueue unrelated actions before the email queue
+	 * dispatches, so object round-trip assertions must target the email action.
+	 *
+	 * @return array{timestamp: int, hook: string, args: array, group: string}
+	 */
+	private function get_scheduled_email_action(): array {
+		foreach ( $this->get_test_queue()->actions as $action ) {
+			if ( 'woocommerce_send_queued_transactional_email' === $action['hook'] ) {
+				return $action;
+			}
+		}
+
+		$this->fail( 'Expected a queued transactional email action to be scheduled.' );
+		return array(
+			'timestamp' => 0,
+			'hook'      => '',
+			'args'      => array(),
+			'group'     => '',
+		);
+	}
+
 	/**
 	 * Assert a queued object reference has the expected wrapper shape.
 	 *