From a25c447affd96462ba178aee44db02d1b25a1ef8 Mon Sep 17 00:00:00 2001 From: dognose24 Date: Mon, 21 Sep 2026 23:23:34 +0800 Subject: [PATCH 1/5] Feature Flags: re-read wpcom overrides after switch_to_blog() Wpcom_Feature_Flags memoized the override map on the first resolution and kept it for the rest of the request, even after switch_to_blog(). On WordPress.com's public API a flag is resolved before the request switches to the requested site, so that site's overrides never applied to its REST responses. Drop the cache on switch_blog and pin it with a test. Co-Authored-By: Claude Fable 5.1 --- ...ature-flags-reset-overrides-on-switch-blog | 4 +++ .../class-wpcom-feature-flags.php | 11 +++++--- .../Wpcom_Feature_Flags_Test.php | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog b/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog new file mode 100644 index 000000000000..e9fa46cad2ec --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Feature Flags: re-read a site's flag overrides after switch_to_blog(), so a flag resolved on WordPress.com's public API answers for the requested site rather than the blog the process started on. diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php index beb0f90db21f..267318d816c7 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php @@ -81,10 +81,13 @@ class Wpcom_Feature_Flags { const CAPABILITY = 'manage_options'; /** - * Request-scoped cache of the sanitized override map. + * Cache of the current blog's sanitized override map. * * Null means "not read from the option yet", which is distinct from the empty - * array a site with no overrides legitimately has. + * array a site with no overrides legitimately has. Dropped on `switch_blog`, + * because the option it mirrors belongs to one blog: WordPress.com's public API + * resolves flags before it switches to the requested site, and the map read + * then must not answer for that site. * * @var array|null */ @@ -97,6 +100,7 @@ class Wpcom_Feature_Flags { */ public static function init() { add_filter( 'jetpack_feature_flag_enabled', array( self::class, 'filter_enabled' ), 10, 2 ); + add_action( 'switch_blog', array( self::class, 'reset_overrides_cache' ) ); add_action( 'admin_menu', array( self::class, 'register_admin_page' ) ); } @@ -192,7 +196,8 @@ public static function save_overrides( array $overrides ) { /** * Forget the memoized override map. * - * Intended for tests, which write the option directly rather than through + * Runs on `switch_blog`, so the next resolution reads the new blog's option. + * Also for tests, which write the option directly rather than through * save_overrides(). Mirrors Feature_Flags::reset() in the registry package. * * @return void diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php index b3b5ad69fe36..ce3a07675e6b 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php @@ -46,6 +46,7 @@ public static function set_up_before_class() { self::$bootstrap_wiring = array( 'filter' => has_filter( 'jetpack_feature_flag_enabled', array( Wpcom_Feature_Flags::class, 'filter_enabled' ) ), + 'switch' => has_action( 'switch_blog', array( Wpcom_Feature_Flags::class, 'reset_overrides_cache' ) ), 'action' => has_action( 'admin_menu', array( Wpcom_Feature_Flags::class, 'register_admin_page' ) ), ); } @@ -63,6 +64,7 @@ public function tear_down() { // These tests write the option directly, which save_overrides() is not there to notice. Wpcom_Feature_Flags::reset_overrides_cache(); remove_all_filters( 'jetpack_feature_flag_enabled' ); + remove_action( 'switch_blog', array( Wpcom_Feature_Flags::class, 'reset_overrides_cache' ) ); remove_all_filters( 'jetpack_feature_flag_enabled_my-feature' ); remove_all_filters( 'wp_die_handler' ); Feature_Flags::reset(); @@ -119,6 +121,10 @@ public function test_package_bootstrap_wires_the_feature() { self::$bootstrap_wiring['filter'], 'Jetpack_Mu_Wpcom::init() must call Wpcom_Feature_Flags::init() so overrides answer the resolution filter.' ); + $this->assertNotFalse( + self::$bootstrap_wiring['switch'], + 'Jetpack_Mu_Wpcom::init() must call Wpcom_Feature_Flags::init() so overrides are re-read after a blog switch.' + ); $this->assertNotFalse( self::$bootstrap_wiring['action'], 'Jetpack_Mu_Wpcom::init() must call Wpcom_Feature_Flags::init() so the admin screen is registered.' @@ -391,6 +397,25 @@ public function test_override_applies_to_unregistered_flag() { $this->assertTrue( Feature_Flags::is_enabled( 'not-registered-anywhere' ) ); } + /** + * WordPress.com's public API resolves flags before it switches to the + * requested site, so the override map memoized for the first blog must be + * dropped on switch_blog or the requested site's overrides never apply. + */ + public function test_overrides_are_reread_after_a_blog_switch() { + Wpcom_Feature_Flags::save_overrides( array( 'my-feature' => false ) ); + Wpcom_Feature_Flags::init(); + $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ) ); + + // The next blog's option, written behind the cache the way a real switch changes it. + update_option( Wpcom_Feature_Flags::OVERRIDES_OPTION, array( 'my-feature' => true ) ); + $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ), 'The memoized map answers until the blog switches.' ); + + do_action( 'switch_blog', 2, 1, 'switch' ); + + $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); + } + /** * Overrides are stored site-wide on purpose: an Automattician flips a flag * to see how the site behaves, including for logged-out visitors. So From 4343dd5dbc3f8b9ff8450ba992cfcc5c337894c0 Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Mon, 21 Sep 2026 17:42:25 +0200 Subject: [PATCH 2/5] Feature Flags: key the wpcom override cache by blog instead of resetting on switch_blog Replace the switch_blog reset with a single cache slot guarded by get_current_blog_id(). It does not depend on switch_blog firing or on hook priority, and a switch/restore pair no longer re-reads the non-autoloaded option twice. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Ac4zq7xVjwnnmCNRV8rNeb --- ...ature-flags-reset-overrides-on-switch-blog | 2 +- .../class-wpcom-feature-flags.php | 36 ++++++++------ .../Wpcom_Feature_Flags_Test.php | 49 +++++++++++++------ 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog b/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog index e9fa46cad2ec..cb4829dc434c 100644 --- a/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-wpcom-feature-flags-reset-overrides-on-switch-blog @@ -1,4 +1,4 @@ Significance: patch Type: fixed -Feature Flags: re-read a site's flag overrides after switch_to_blog(), so a flag resolved on WordPress.com's public API answers for the requested site rather than the blog the process started on. +Feature Flags: read a site's flag overrides per blog, so a flag resolved on WordPress.com's public API answers for the requested site rather than the blog the process started on. diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php index 267318d816c7..a0e6b0e24a34 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php @@ -81,18 +81,23 @@ class Wpcom_Feature_Flags { const CAPABILITY = 'manage_options'; /** - * Cache of the current blog's sanitized override map. + * Cache of one blog's sanitized override map; valid only for $overrides_blog_id. * - * Null means "not read from the option yet", which is distinct from the empty - * array a site with no overrides legitimately has. Dropped on `switch_blog`, - * because the option it mirrors belongs to one blog: WordPress.com's public API - * resolves flags before it switches to the requested site, and the map read - * then must not answer for that site. + * Keyed by blog rather than dropped on `switch_blog`: WordPress.com's public API + * resolves flags before switching to the requested site, and a hook would miss + * earlier callbacks and re-read the non-autoloaded option on every restore. * * @var array|null */ private static $overrides = null; + /** + * The blog $overrides was read for. Null means nothing is cached. + * + * @var int|null + */ + private static $overrides_blog_id = null; + /** * Register the hooks this feature needs. * @@ -100,7 +105,6 @@ class Wpcom_Feature_Flags { */ public static function init() { add_filter( 'jetpack_feature_flag_enabled', array( self::class, 'filter_enabled' ), 10, 2 ); - add_action( 'switch_blog', array( self::class, 'reset_overrides_cache' ) ); add_action( 'admin_menu', array( self::class, 'register_admin_page' ) ); } @@ -151,7 +155,7 @@ public static function current_user_can_manage() { /** * Return this site's flag overrides. * - * Memoized for the request. filter_enabled() runs once per flag resolution, + * Memoized per blog. filter_enabled() runs once per flag resolution, * and the screen resolves every listed flag to fill its Effective column, so * without this the option read, the per-entry preg_match(), and the ksort() * in sanitize_overrides() all repeat for every flag checked. The option @@ -162,13 +166,16 @@ public static function current_user_can_manage() { * @return array Map of flag name to forced value. */ public static function get_overrides() { - if ( null !== self::$overrides ) { + $blog_id = get_current_blog_id(); + + if ( null !== self::$overrides && self::$overrides_blog_id === $blog_id ) { return self::$overrides; } $stored = get_option( self::OVERRIDES_OPTION ); - self::$overrides = is_array( $stored ) ? self::sanitize_overrides( $stored ) : array(); + self::$overrides = is_array( $stored ) ? self::sanitize_overrides( $stored ) : array(); + self::$overrides_blog_id = $blog_id; return self::$overrides; } @@ -182,7 +189,8 @@ public static function get_overrides() { public static function save_overrides( array $overrides ) { $overrides = self::sanitize_overrides( $overrides ); - self::$overrides = null; + self::$overrides = null; + self::$overrides_blog_id = null; if ( empty( $overrides ) ) { delete_option( self::OVERRIDES_OPTION ); @@ -196,14 +204,14 @@ public static function save_overrides( array $overrides ) { /** * Forget the memoized override map. * - * Runs on `switch_blog`, so the next resolution reads the new blog's option. - * Also for tests, which write the option directly rather than through + * Intended for tests, which write the option directly rather than through * save_overrides(). Mirrors Feature_Flags::reset() in the registry package. * * @return void */ public static function reset_overrides_cache() { - self::$overrides = null; + self::$overrides = null; + self::$overrides_blog_id = null; } /** diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php index ce3a07675e6b..3dee7dd6e32d 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php @@ -46,7 +46,6 @@ public static function set_up_before_class() { self::$bootstrap_wiring = array( 'filter' => has_filter( 'jetpack_feature_flag_enabled', array( Wpcom_Feature_Flags::class, 'filter_enabled' ) ), - 'switch' => has_action( 'switch_blog', array( Wpcom_Feature_Flags::class, 'reset_overrides_cache' ) ), 'action' => has_action( 'admin_menu', array( Wpcom_Feature_Flags::class, 'register_admin_page' ) ), ); } @@ -64,7 +63,8 @@ public function tear_down() { // These tests write the option directly, which save_overrides() is not there to notice. Wpcom_Feature_Flags::reset_overrides_cache(); remove_all_filters( 'jetpack_feature_flag_enabled' ); - remove_action( 'switch_blog', array( Wpcom_Feature_Flags::class, 'reset_overrides_cache' ) ); + remove_all_filters( 'pre_option_' . Wpcom_Feature_Flags::OVERRIDES_OPTION ); + $GLOBALS['blog_id'] = 1; remove_all_filters( 'jetpack_feature_flag_enabled_my-feature' ); remove_all_filters( 'wp_die_handler' ); Feature_Flags::reset(); @@ -121,10 +121,6 @@ public function test_package_bootstrap_wires_the_feature() { self::$bootstrap_wiring['filter'], 'Jetpack_Mu_Wpcom::init() must call Wpcom_Feature_Flags::init() so overrides answer the resolution filter.' ); - $this->assertNotFalse( - self::$bootstrap_wiring['switch'], - 'Jetpack_Mu_Wpcom::init() must call Wpcom_Feature_Flags::init() so overrides are re-read after a blog switch.' - ); $this->assertNotFalse( self::$bootstrap_wiring['action'], 'Jetpack_Mu_Wpcom::init() must call Wpcom_Feature_Flags::init() so the admin screen is registered.' @@ -399,21 +395,44 @@ public function test_override_applies_to_unregistered_flag() { /** * WordPress.com's public API resolves flags before it switches to the - * requested site, so the override map memoized for the first blog must be - * dropped on switch_blog or the requested site's overrides never apply. - */ - public function test_overrides_are_reread_after_a_blog_switch() { - Wpcom_Feature_Flags::save_overrides( array( 'my-feature' => false ) ); + * requested site. The suite is single-site, so the blog changes through the + * global get_current_blog_id() reads, without firing `switch_blog`. + */ + public function test_each_blog_resolves_its_own_overrides() { + add_filter( + 'pre_option_' . Wpcom_Feature_Flags::OVERRIDES_OPTION, + function () { + return array( 'my-feature' => 2 === get_current_blog_id() ); + } + ); Wpcom_Feature_Flags::init(); + $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ) ); - // The next blog's option, written behind the cache the way a real switch changes it. - update_option( Wpcom_Feature_Flags::OVERRIDES_OPTION, array( 'my-feature' => true ) ); - $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ), 'The memoized map answers until the blog switches.' ); + $GLOBALS['blog_id'] = 2; + $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); - do_action( 'switch_blog', 2, 1, 'switch' ); + $GLOBALS['blog_id'] = 1; + $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ) ); + } + /** + * Repeat resolutions on the same blog answer from the cache. + */ + public function test_overrides_are_read_once_per_blog() { + $reads = 0; + add_filter( + 'pre_option_' . Wpcom_Feature_Flags::OVERRIDES_OPTION, + function () use ( &$reads ) { + ++$reads; + return array( 'my-feature' => true ); + } + ); + Wpcom_Feature_Flags::init(); + + $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); + $this->assertSame( 1, $reads ); } /** From 090909f4166233852cf88096f8a6963ed2e7841d Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 22 Sep 2026 09:27:44 +0200 Subject: [PATCH 3/5] Feature Flags: justify the per-blog cache by correctness, and pin each blog's exact map Address review: the saving over a switch_blog reset only applies to a switch/restore pair that resolves no flag, so the docblock now argues from hook reliance instead. The blog test asserts each blog's exact override map, so an empty map can no longer satisfy it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Ac4zq7xVjwnnmCNRV8rNeb --- .../wpcom-feature-flags/class-wpcom-feature-flags.php | 4 ++-- .../wpcom-feature-flags/Wpcom_Feature_Flags_Test.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php index a0e6b0e24a34..7bad15b5773e 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php +++ b/projects/packages/jetpack-mu-wpcom/src/features/wpcom-feature-flags/class-wpcom-feature-flags.php @@ -84,8 +84,8 @@ class Wpcom_Feature_Flags { * Cache of one blog's sanitized override map; valid only for $overrides_blog_id. * * Keyed by blog rather than dropped on `switch_blog`: WordPress.com's public API - * resolves flags before switching to the requested site, and a hook would miss - * earlier callbacks and re-read the non-autoloaded option on every restore. + * resolves flags before switching to the requested site, and a hook only helps + * if it fires, and fires before any other callback that resolves a flag. * * @var array|null */ diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php index 3dee7dd6e32d..767fc81b7773 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php @@ -402,18 +402,18 @@ public function test_each_blog_resolves_its_own_overrides() { add_filter( 'pre_option_' . Wpcom_Feature_Flags::OVERRIDES_OPTION, function () { - return array( 'my-feature' => 2 === get_current_blog_id() ); + return array( 'my-feature' => 1 === get_current_blog_id() ); } ); Wpcom_Feature_Flags::init(); - $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ) ); + $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); $GLOBALS['blog_id'] = 2; - $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); + $this->assertSame( array( 'my-feature' => false ), Wpcom_Feature_Flags::get_overrides() ); $GLOBALS['blog_id'] = 1; - $this->assertFalse( Feature_Flags::is_enabled( 'my-feature' ) ); + $this->assertSame( array( 'my-feature' => true ), Wpcom_Feature_Flags::get_overrides() ); } /** From bb1b7edcfd4148b75aed7c46dac0daad72969aec Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 22 Sep 2026 09:56:14 +0200 Subject: [PATCH 4/5] Feature Flags: restore the incoming blog id after each flag test tear_down() restored blog_id to a hardcoded 1; it now restores the value the test started with, and the blog-switching test sets its starting blog itself. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Ac4zq7xVjwnnmCNRV8rNeb --- .../Wpcom_Feature_Flags_Test.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php index 767fc81b7773..cbcff12a2ce9 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php @@ -38,6 +38,13 @@ class Wpcom_Feature_Flags_Test extends \WorDBless\BaseTestCase { */ private static $bootstrap_wiring = array(); + /** + * The blog id the test started on, restored by tear_down(). + * + * @var mixed + */ + private $original_blog_id; + /** * Record the bootstrap's hook registrations before any test disturbs them. */ @@ -50,6 +57,15 @@ public static function set_up_before_class() { ); } + /** + * Remember the blog context, which the blog-switching test changes. + */ + public function set_up() { + parent::set_up(); + + $this->original_blog_id = $GLOBALS['blog_id']; + } + /** * Reset every piece of global state these tests touch. */ @@ -64,7 +80,7 @@ public function tear_down() { Wpcom_Feature_Flags::reset_overrides_cache(); remove_all_filters( 'jetpack_feature_flag_enabled' ); remove_all_filters( 'pre_option_' . Wpcom_Feature_Flags::OVERRIDES_OPTION ); - $GLOBALS['blog_id'] = 1; + $GLOBALS['blog_id'] = $this->original_blog_id; remove_all_filters( 'jetpack_feature_flag_enabled_my-feature' ); remove_all_filters( 'wp_die_handler' ); Feature_Flags::reset(); @@ -407,6 +423,8 @@ function () { ); Wpcom_Feature_Flags::init(); + $GLOBALS['blog_id'] = 1; + $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); $GLOBALS['blog_id'] = 2; From 654df590ae554851ad7757c5f3711d807c555f9c Mon Sep 17 00:00:00 2001 From: Lourens Schep Date: Tue, 22 Sep 2026 10:25:27 +0200 Subject: [PATCH 5/5] Feature Flags: read the cached overrides back directly in the cache test Phan flagged the repeated is_enabled() assertion as a duplicate adjacent statement; the second read now goes through get_overrides(). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Ac4zq7xVjwnnmCNRV8rNeb --- .../features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php index cbcff12a2ce9..b8ef43ca90da 100644 --- a/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php +++ b/projects/packages/jetpack-mu-wpcom/tests/php/features/wpcom-feature-flags/Wpcom_Feature_Flags_Test.php @@ -449,7 +449,7 @@ function () use ( &$reads ) { Wpcom_Feature_Flags::init(); $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); - $this->assertTrue( Feature_Flags::is_enabled( 'my-feature' ) ); + $this->assertSame( array( 'my-feature' => true ), Wpcom_Feature_Flags::get_overrides() ); $this->assertSame( 1, $reads ); }