diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/Current_Release_Resolution_Test.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/Current_Release_Resolution_Test.php
index a6209de163..fc07c6799f 100644
--- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/Current_Release_Resolution_Test.php
+++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/Current_Release_Resolution_Test.php
@@ -471,6 +471,119 @@ public function test_cooldown_line_follows_fallback_resolution(): void {
$this->assertStringContainsString( 'Will be served', Release_Confirmation::get_approval_text( get_post( $this->plugin->ID ), $releases[ self::RENAMED_VERSION ] ) );
}
+ /**
+ * Empty version and stable_tag metas resolve no release and must not make
+ * the row write error or defer.
+ */
+ public function test_empty_metas_resolve_false_and_still_update(): void {
+ update_post_meta( $this->plugin->ID, 'version', '' );
+ update_post_meta( $this->plugin->ID, 'stable_tag', '' );
+ $this->add_release( '1.0', '1.0', array( 'date' => time() - WEEK_IN_SECONDS ) );
+
+ $this->assertFalse( API_Update_Updater::get_current_release( get_post( $this->plugin->ID ) ) );
+
+ $this->assertTrue( API_Update_Updater::update_single_plugin( $this->plugin->post_name ) );
+ $this->assertFalse( wp_next_scheduled( "release_to_update_api:{$this->plugin->post_name}" ) );
+ }
+
+ /**
+ * A legacy plugin without releases meta gets it prefilled from the tags
+ * meta, resolves by stable tag, and its old release doesn't defer the row.
+ */
+ public function test_prefilled_legacy_releases_resolve_by_stable_tag(): void {
+ update_post_meta( $this->plugin->ID, 'version', '1.0' );
+ update_post_meta( $this->plugin->ID, 'stable_tag', '1.0' );
+ delete_post_meta( $this->plugin->ID, 'releases' );
+ update_post_meta(
+ $this->plugin->ID,
+ 'tags',
+ array(
+ '1.0' => array(
+ 'tag' => '1.0',
+ 'date' => '2020-01-01',
+ 'author' => 'testuser',
+ ),
+ )
+ );
+
+ $release = API_Update_Updater::get_current_release( get_post( $this->plugin->ID ) );
+ $this->assertSame( '1.0', $release['tag'] );
+
+ $this->assertTrue( API_Update_Updater::update_single_plugin( $this->plugin->post_name ) );
+ $this->assertSame( '1.0', $this->served_version() );
+ $this->assertFalse( wp_next_scheduled( "release_to_update_api:{$this->plugin->post_name}" ) );
+ }
+
+ /**
+ * An old release whose cooldown window has long elapsed doesn't defer the
+ * row write: resolving more releases than before must not re-hold legacy
+ * version bumps.
+ */
+ public function test_elapsed_cooldown_on_old_release_does_not_defer(): void {
+ $this->add_release( self::HELD_TAG, self::RENAMED_VERSION, array( 'date' => time() - WEEK_IN_SECONDS ) );
+
+ $this->assertTrue( API_Update_Updater::update_single_plugin( $this->plugin->post_name ) );
+
+ $this->assertSame( self::RENAMED_VERSION, $this->served_version() );
+ $this->assertFalse( wp_next_scheduled( "release_to_update_api:{$this->plugin->post_name}" ) );
+ }
+
+ /**
+ * Legacy rows can carry integer-typed tags (written before the string
+ * cast); resolution and the strict add_release() merge handle them.
+ */
+ public function test_legacy_integer_tag_resolves_and_merges_strictly(): void {
+ update_post_meta( $this->plugin->ID, 'version', '2.0' );
+ update_post_meta( $this->plugin->ID, 'stable_tag', '2' );
+ update_post_meta(
+ $this->plugin->ID,
+ 'releases',
+ array(
+ array(
+ 'date' => time() - WEEK_IN_SECONDS,
+ 'tag' => 2,
+ 'version' => '2.0',
+ 'zips_built' => true,
+ 'zips_built_from_revision' => 0,
+ 'confirmations' => array(),
+ 'confirmed' => true,
+ 'confirmations_required' => 0,
+ 'committer' => array(),
+ 'revision' => array(),
+ 'release_delay' => 0,
+ ),
+ )
+ );
+
+ $release = API_Update_Updater::get_current_release( get_post( $this->plugin->ID ) );
+ $this->assertSame( '2.0', $release['version'] );
+
+ Plugin_Directory::add_release(
+ $this->plugin,
+ array(
+ 'tag' => '2',
+ 'release_block' => array( 'blocked_at' => time() ),
+ )
+ );
+
+ $releases = Plugin_Directory::get_releases( $this->plugin );
+ $this->assertCount( 1, $releases );
+ $this->assertTrue( API_Update_Updater::is_release_blocked( reset( $releases ) ) );
+ }
+
+ /**
+ * A tag that isn't the version string (e.g. `v2.0`) resolves by the stable
+ * tag; the old version-keyed lookup couldn't find these at all.
+ */
+ public function test_non_version_tag_name_resolves(): void {
+ update_post_meta( $this->plugin->ID, 'version', '2.0' );
+ update_post_meta( $this->plugin->ID, 'stable_tag', 'v2.0' );
+ $this->add_release( 'v2.0', '2.0' );
+
+ $release = API_Update_Updater::get_current_release( get_post( $this->plugin->ID ) );
+ $this->assertSame( 'v2.0', $release['tag'] );
+ }
+
/**
* The force-release audit note names the release actually unblocked — the
* stable-tag-resolved one — not the plugin's Version header.