From 86c5d6fb11e82abdc8890680d2d72db8a55d402a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 20:20:17 +0000 Subject: [PATCH] Add tags for scenarios that need a stable release or a database socket Scenarios that verify an installation against the checksums published by WordPress.org cannot pass against a build WordPress.org knows nothing about, and scenarios that connect to the database through a socket cannot pass when the database server runs in a container and is only reachable over TCP. Both currently fail for reasons unrelated to what they test. `@require-wp-stable` is filtered out when `WP_CLI_TEST_CORE_ZIP` is set and when `WP_VERSION` is `trunk` or `nightly`. `@require-mysql-socket` is filtered out when no socket can be found, looking in the same locations as the scenarios that use one. Following `@require-extension-` and the operating system tags, both are only filtered out when a feature file actually carries them, so that the filter stays limited to what the suite being run uses. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CE81GsxUY597AdaMP1NXzk --- .readme-partials/USING.md | 14 +++++ README.md | 14 +++++ tests/tests/TestBehatTags.php | 114 ++++++++++++++++++++++++++++++++++ utils/behat-tags.php | 80 ++++++++++++++++++++++++ 4 files changed, 222 insertions(+) diff --git a/.readme-partials/USING.md b/.readme-partials/USING.md index 3b2528f7..f57861a4 100644 --- a/.readme-partials/USING.md +++ b/.readme-partials/USING.md @@ -148,6 +148,20 @@ WP_VERSION=6.4.2 WP_CLI_TEST_CORE_ZIP=~/Downloads/wordpress-6.4.2.zip composer b Note that steps requesting an explicit version, such as `Given a WP 6.4.2 installation`, keep downloading that version from WordPress.org and ignore the archive. +#### Environment-specific scenarios + +Some scenarios can only run in certain environments. Tagging them makes the test framework +filter them out everywhere else, rather than having them fail for reasons unrelated to what +they test. + +* `@require-wp-stable` — the scenario needs a version of WordPress that WordPress.org knows + about, such as one verifying an installation against the published checksums. It is + skipped when `WP_CLI_TEST_CORE_ZIP` is set, and when `WP_VERSION` is `trunk` or `nightly`. +* `@require-mysql-socket` — the scenario connects to the database through a socket. It is + skipped when there is none, which is the case when the database server runs in a + container and is only reachable over TCP. Set `WP_CLI_TEST_DBSOCKET` to point at the + socket if it lives somewhere unusual. + #### WP-CLI Binary You can run the tests against a specific WP-CLI binary, instead of using the one that has been built in your project's `vendor/bin` folder. diff --git a/README.md b/README.md index 6eeff87a..a2069e02 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,20 @@ WP_VERSION=6.4.2 WP_CLI_TEST_CORE_ZIP=~/Downloads/wordpress-6.4.2.zip composer b Note that steps requesting an explicit version, such as `Given a WP 6.4.2 installation`, keep downloading that version from WordPress.org and ignore the archive. +#### Environment-specific scenarios + +Some scenarios can only run in certain environments. Tagging them makes the test framework +filter them out everywhere else, rather than having them fail for reasons unrelated to what +they test. + +* `@require-wp-stable` — the scenario needs a version of WordPress that WordPress.org knows + about, such as one verifying an installation against the published checksums. It is + skipped when `WP_CLI_TEST_CORE_ZIP` is set, and when `WP_VERSION` is `trunk` or `nightly`. +* `@require-mysql-socket` — the scenario connects to the database through a socket. It is + skipped when there is none, which is the case when the database server runs in a + container and is only reachable over TCP. Set `WP_CLI_TEST_DBSOCKET` to point at the + socket if it lives somewhere unusual. + #### WP-CLI Binary You can run the tests against a specific WP-CLI binary, instead of using the one that has been built in your project's `vendor/bin` folder. diff --git a/tests/tests/TestBehatTags.php b/tests/tests/TestBehatTags.php index 5a2fe1b7..0ff7f3d2 100644 --- a/tests/tests/TestBehatTags.php +++ b/tests/tests/TestBehatTags.php @@ -459,4 +459,118 @@ public function test_behat_tags_skip_db_type(): void { putenv( false === $env_github_token ? 'GITHUB_TOKEN' : "GITHUB_TOKEN=$env_github_token" ); } + + /** + * Write a feature file carrying the given tag. + * + * @param string $tag + */ + private function write_tagged_feature( $tag ): void { + file_put_contents( + $this->temp_dir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'tagged.feature', + $tag . "\nFeature: Tagged\n" + ); + } + + /** + * Run the script with the given environment, restoring it afterwards. + * + * @param array $env Variables to set, or null to unset. + * @return string|false + */ + private function run_with_env( array $env ) { + $previous = array(); + + foreach ( $env as $name => $value ) { + $current = getenv( $name ); + $previous[ $name ] = false === $current ? null : $current; + + putenv( null === $value ? $name : "{$name}={$value}" ); + } + + try { + return $this->run_behat_tags_script(); + } finally { + foreach ( $previous as $name => $value ) { + putenv( null === $value ? $name : "{$name}={$value}" ); + } + } + } + + public function test_require_wp_stable_is_skipped_for_an_archive(): void { + $this->write_tagged_feature( '@require-wp-stable' ); + + $output = $this->run_with_env( + array( + 'WP_VERSION' => '6.4.2', + 'WP_CLI_TEST_CORE_ZIP' => $this->temp_dir . DIRECTORY_SEPARATOR . 'wordpress.zip', + ) + ); + + $this->assertStringContainsString( '~@require-wp-stable', (string) $output ); + } + + public function test_require_wp_stable_is_skipped_on_trunk(): void { + $this->write_tagged_feature( '@require-wp-stable' ); + + $output = $this->run_with_env( + array( + 'WP_VERSION' => 'trunk', + 'WP_CLI_TEST_CORE_ZIP' => null, + ) + ); + + $this->assertStringContainsString( '~@require-wp-stable', (string) $output ); + } + + public function test_require_wp_stable_runs_on_a_release(): void { + $this->write_tagged_feature( '@require-wp-stable' ); + + $output = $this->run_with_env( + array( + 'WP_VERSION' => '6.4.2', + 'WP_CLI_TEST_CORE_ZIP' => null, + ) + ); + + $this->assertStringNotContainsString( '@require-wp-stable', (string) $output ); + } + + public function test_require_mysql_socket_is_skipped_without_a_socket(): void { + $this->write_tagged_feature( '@require-mysql-socket' ); + + $output = $this->run_with_env( + array( 'WP_CLI_TEST_DBSOCKET' => $this->temp_dir . DIRECTORY_SEPARATOR . 'missing.sock' ) + ); + + $this->assertStringContainsString( '~@require-mysql-socket', (string) $output ); + } + + public function test_require_mysql_socket_runs_with_a_socket(): void { + $this->write_tagged_feature( '@require-mysql-socket' ); + + $socket = $this->temp_dir . DIRECTORY_SEPARATOR . 'mysql.sock'; + touch( $socket ); + + $output = $this->run_with_env( array( 'WP_CLI_TEST_DBSOCKET' => $socket ) ); + + $this->assertStringNotContainsString( '@require-mysql-socket', (string) $output ); + } + + /** + * Tags no scenario carries do not need to be filtered out. + */ + public function test_unused_tags_are_not_filtered_out(): void { + $this->write_tagged_feature( '@some-other-tag' ); + + $output = $this->run_with_env( + array( + 'WP_VERSION' => 'trunk', + 'WP_CLI_TEST_DBSOCKET' => $this->temp_dir . DIRECTORY_SEPARATOR . 'missing.sock', + ) + ); + + $this->assertStringNotContainsString( '@require-wp-stable', (string) $output ); + $this->assertStringNotContainsString( '@require-mysql-socket', (string) $output ); + } } diff --git a/utils/behat-tags.php b/utils/behat-tags.php index d9d7e35a..a17d7c05 100644 --- a/utils/behat-tags.php +++ b/utils/behat-tags.php @@ -131,6 +131,68 @@ function get_db_version() { return $db_info['version']; } +/** + * Whether any feature file uses the given tag. + * + * Tags that no scenario carries do not need to be filtered out, and leaving them + * out keeps the filter to what the suite being run actually uses. + * + * @param string $tag + * @param string $features_folder + * @return bool + */ +function tag_in_use( $tag, $features_folder = 'features' ) { + $feature_files = glob( $features_folder . DIRECTORY_SEPARATOR . '*.feature' ); + + if ( empty( $feature_files ) ) { + return false; + } + + foreach ( $feature_files as $feature_file ) { + if ( false !== strpos( (string) file_get_contents( $feature_file ), $tag ) ) { + return true; + } + } + + return false; +} + +/** + * Whether the database server can be reached through a socket. + * + * A database server running in a container is only reachable over TCP, so the + * scenarios that connect through a socket cannot run against one. The locations + * checked here are the same ones those scenarios look in. + * + * @return bool + */ +function has_mysql_socket() { + $socket = getenv( 'WP_CLI_TEST_DBSOCKET' ); + + if ( is_string( $socket ) && '' !== $socket ) { + return file_exists( $socket ); + } + + // Anything but a port number after the colon in the host is a socket path. + $host = getenv( 'WP_CLI_TEST_DBHOST' ); + + if ( is_string( $host ) && false !== strpos( $host, ':' ) ) { + $after_colon = substr( $host, strrpos( $host, ':' ) + 1 ); + + if ( '' !== $after_colon && ! is_numeric( $after_colon ) ) { + return file_exists( $after_colon ); + } + } + + foreach ( array( '/var/run/mysqld/mysqld.sock', '/tmp/mysql.sock' ) as $location ) { + if ( file_exists( $location ) ) { + return true; + } + } + + return false; +} + $features_folder = getenv( 'BEHAT_FEATURES_FOLDER' ) ?: 'features'; $wp_version = getenv( 'WP_VERSION' ); $wp_version_reqs = array(); @@ -176,6 +238,24 @@ function get_db_version() { $skip_tags[] = '@broken-trunk'; } +// Some scenarios need a version of WordPress that WordPress.org knows about, such +// as the ones verifying an installation against the published checksums. Neither a +// development build nor an archive supplied through WP_CLI_TEST_CORE_ZIP qualifies. +$core_zip = getenv( 'WP_CLI_TEST_CORE_ZIP' ); +if ( + tag_in_use( '@require-wp-stable', $features_folder ) + && ( + ( false !== $core_zip && '' !== $core_zip ) + || in_array( $wp_version, array( 'nightly', 'trunk' ), true ) + ) +) { + $skip_tags[] = '@require-wp-stable'; +} + +if ( tag_in_use( '@require-mysql-socket', $features_folder ) && ! has_mysql_socket() ) { + $skip_tags[] = '@require-mysql-socket'; +} + $db_info = get_db_type_and_version(); $db_version = $db_info['version']; // Use detected database type from server, unless WP_CLI_TEST_DBTYPE is 'sqlite'.