Commit 0d74cf00d27 for woocommerce
commit 0d74cf00d27e096536e904f6d46d75621f1bed61
Author: Raluca Stan <ralucastn@gmail.com>
Date: Thu May 21 13:42:49 2026 +0300
Fix Back in Stock Notifications tests silently halting PHPUnit (#65207)
* Trap wp_safe_redirect in EmailActionControllerTests to fix silent PHPUnit halt
PR #64329 added `exit;` after `wp_safe_redirect()` in
EmailActionController::process_verification_action() and
process_unsubscribe_action(). The new EmailActionControllerTests calls
validate_and_maybe_process_request() directly with valid keys, which
now reaches `wp_safe_redirect + exit;` and silently terminates PHPUnit
mid-run with status 0 — CI stays green while the rest of the suite
never executes.
Install the same `wp_redirect` filter trap already used by the sister
NotificationManagementServiceTests so the controller's `exit;` is never
reached during tests. The four happy-path tests (verify success,
verified email dispatch, repeated-verify short-circuit, unsubscribe)
wrap their SUT calls in try/catch and assert the intercept message.
Production code is unchanged — wp_safe_redirect + exit is the correct
WP pattern.
Bug introduced in PR #64329.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add changefile(s) from automation for the following project(s): woocommerce
* Monorepo: cleanup.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Vladimir Reznichenko <kalessil@gmail.com>
diff --git a/plugins/woocommerce/changelog/65207-claude-pensive-nightingale-ad626c b/plugins/woocommerce/changelog/65207-claude-pensive-nightingale-ad626c
new file mode 100644
index 00000000000..4fa13f2a729
--- /dev/null
+++ b/plugins/woocommerce/changelog/65207-claude-pensive-nightingale-ad626c
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Trap `wp_safe_redirect()` in `EmailActionControllerTests` so PHPUnit no longer silently halts mid-suite on the Back in Stock Notifications redirect tests.
\ No newline at end of file
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index b005bd27fe0..4f007b91831 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -47265,18 +47265,6 @@ parameters:
count: 1
path: src/Admin/Features/OnboardingTasks/Tasks/Products.php
- -
- message: '#^Cannot access property \$base on WP_Screen\|null\.$#'
- identifier: property.nonObject
- count: 1
- path: src/Admin/Features/OnboardingTasks/Tasks/Products.php
-
- -
- message: '#^Cannot access property \$post_type on WP_Screen\|null\.$#'
- identifier: property.nonObject
- count: 2
- path: src/Admin/Features/OnboardingTasks/Tasks/Products.php
-
-
message: '#^Method Automattic\\WooCommerce\\Admin\\Features\\OnboardingTasks\\Tasks\\Products\:\:maybe_set_has_product_transient\(\) has no return type specified\.$#'
identifier: missingType.return
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php
index 950d3ecc800..42498aed3c7 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Emails/EmailActionControllerTests.php
@@ -35,11 +35,36 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
*/
public function setUp(): void {
parent::setUp();
+
+ // Intercept redirects so headers aren't emitted, and throw so the trailing `exit;`
+ // in production code never runs during the test.
+ add_filter( 'wp_redirect', array( $this, 'intercept_redirect' ) );
+
$this->email_manager = $this->createMock( EmailManager::class );
$this->sut = new EmailActionController();
$this->sut->init( $this->email_manager );
}
+ /**
+ * Tear down test fixtures.
+ */
+ public function tearDown(): void {
+ remove_filter( 'wp_redirect', array( $this, 'intercept_redirect' ) );
+ parent::tearDown();
+ }
+
+ /**
+ * `wp_redirect` filter callback that throws so the SUT's trailing `exit;`
+ * never executes and the test can still assert state after the method.
+ *
+ * @param string $location Redirect target.
+ * @return never
+ * @throws \RuntimeException Always.
+ */
+ public function intercept_redirect( string $location ): void {
+ throw new \RuntimeException( 'wp_redirect intercepted: ' . esc_url_raw( $location ) );
+ }
+
/**
* Persist a notification with a single action-key meta entry.
*
@@ -68,7 +93,12 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
time() . ':' . wp_fast_hash( 'test' )
);
- $this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
+ try {
+ $this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
+ $this->fail( 'Expected redirect to be intercepted via exception.' );
+ } catch ( \RuntimeException $e ) {
+ $this->assertStringContainsString( 'wp_redirect intercepted', $e->getMessage() );
+ }
$this->assertEquals( NotificationStatus::ACTIVE, Factory::get_notification( $id )->get_status() );
}
@@ -94,7 +124,12 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
)
);
- $this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
+ try {
+ $this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
+ $this->fail( 'Expected redirect to be intercepted via exception.' );
+ } catch ( \RuntimeException $e ) {
+ $this->assertStringContainsString( 'wp_redirect intercepted', $e->getMessage() );
+ }
}
/**
@@ -131,8 +166,14 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
->expects( $this->once() )
->method( 'send_verified_email' );
- // First hit transitions PENDING -> ACTIVE and dispatches the verified email.
- $this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
+ // First hit transitions PENDING -> ACTIVE and dispatches the verified email. The
+ // trailing redirect throws via the intercept filter so the SUT's `exit;` is skipped.
+ try {
+ $this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
+ $this->fail( 'Expected redirect to be intercepted via exception.' );
+ } catch ( \RuntimeException $e ) {
+ $this->assertStringContainsString( 'wp_redirect intercepted', $e->getMessage() );
+ }
// Second hit (double-click, email prefetch, bot) must short-circuit without re-dispatch.
$this->sut->validate_and_maybe_process_request( $id, 'test', 'verify' );
@@ -148,7 +189,12 @@ class EmailActionControllerTests extends \WC_Unit_Test_Case {
wp_fast_hash( 'test' )
);
- $this->sut->validate_and_maybe_process_request( $id, 'test', 'unsubscribe' );
+ try {
+ $this->sut->validate_and_maybe_process_request( $id, 'test', 'unsubscribe' );
+ $this->fail( 'Expected redirect to be intercepted via exception.' );
+ } catch ( \RuntimeException $e ) {
+ $this->assertStringContainsString( 'wp_redirect intercepted', $e->getMessage() );
+ }
$updated = Factory::get_notification( $id );
$this->assertEquals( NotificationStatus::CANCELLED, $updated->get_status() );