From c962d5f1638d2b0482c642fd73d01fc46b69d052 Mon Sep 17 00:00:00 2001 From: Ramon Corrales Date: Mon, 18 May 2026 13:04:32 -0500 Subject: [PATCH 1/3] test: refine unit test linting Co-authored-by: Codex --- phpcs.xml | 11 ++-- phpunit.xml.dist | 2 +- scripts/bump-plugin-version.php | 4 ++ tests/Unit/BumpPluginVersionTest.php | 81 ++++++++++++++++++++++++---- 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/phpcs.xml b/phpcs.xml index 417f01c..abe6fa4 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -6,9 +6,6 @@ - - - @@ -17,6 +14,12 @@ + + */tests/Unit/*Test.php + + + */tests/Unit/*Test.php + @@ -27,6 +30,8 @@ */vendor/* */.github/* + */tests/bootstrap.php scripts + tests/Unit diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9282139..2387ceb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,7 +7,7 @@ > - tests/Unit + tests/Unit diff --git a/scripts/bump-plugin-version.php b/scripts/bump-plugin-version.php index 611e4ad..05bf362 100644 --- a/scripts/bump-plugin-version.php +++ b/scripts/bump-plugin-version.php @@ -5,6 +5,10 @@ * @package WPVDB_Scripts */ +// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite +// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents +// phpcs:disable WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown + declare(strict_types=1); if ( is_bump_plugin_version_entrypoint( $argv ?? [] ) ) { diff --git a/tests/Unit/BumpPluginVersionTest.php b/tests/Unit/BumpPluginVersionTest.php index c206b79..eb71c31 100644 --- a/tests/Unit/BumpPluginVersionTest.php +++ b/tests/Unit/BumpPluginVersionTest.php @@ -1,4 +1,17 @@ */ private array $temp_dirs = []; + /** + * Clean temporary fixtures and environment. + */ protected function tearDown(): void { foreach ( $this->temp_dirs as $dir ) { $this->delete_tree( $dir ); @@ -24,12 +47,17 @@ protected function tearDown(): void { } } + /** + * Test configured version surfaces are bumped together. + * + * @covers ::bump_plugin_version + */ public function test_bump_updates_all_configured_version_surfaces(): void { $root = $this->make_fixture( [ - 'demo-plugin.php' => " "{\n\t\"name\": \"demo-plugin\",\n\t\"version\": \"0.1.2\"\n}\n", - 'languages/demo.pot' => "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Demo plugin 0.1.2\\n\"\n", + 'demo-plugin.php' => " "{\n\t\"name\": \"demo-plugin\",\n\t\"version\": \"0.1.2\"\n}\n", + 'languages/demo.pot' => "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Demo plugin 0.1.2\\n\"\n", 'src/example/block.json' => "{\n\t\"name\": \"demo/example\",\n\t\"version\": \"0.1.2\"\n}\n", ] ); @@ -44,14 +72,19 @@ public function test_bump_updates_all_configured_version_surfaces(): void { ] ); - self::assertSame( '0.2.0', bump_plugin_version( $root, 'minor' ) ); - self::assertStringContainsString( "* Version: 0.2.0\n", file_get_contents( $root . '/demo-plugin.php' ) ?: '' ); - self::assertStringContainsString( "define( 'DEMO_PLUGIN_VERSION', '0.2.0' );", file_get_contents( $root . '/demo-plugin.php' ) ?: '' ); - self::assertStringContainsString( '"version": "0.2.0"', file_get_contents( $root . '/package.json' ) ?: '' ); - self::assertStringContainsString( 'Project-Id-Version: Demo plugin 0.2.0\\n', file_get_contents( $root . '/languages/demo.pot' ) ?: '' ); - self::assertStringContainsString( '"version": "0.2.0"', file_get_contents( $root . '/src/example/block.json' ) ?: '' ); + self::assertSame( '0.2.0', bump_plugin_version( $root, 'minor' ), 'Minor bumps should update the returned version.' ); + self::assertStringContainsString( "* Version: 0.2.0\n", $this->read_file( $root . '/demo-plugin.php' ), 'Plugin headers should be bumped.' ); + self::assertStringContainsString( "define( 'DEMO_PLUGIN_VERSION', '0.2.0' );", $this->read_file( $root . '/demo-plugin.php' ), 'Version constants should be bumped.' ); + self::assertStringContainsString( '"version": "0.2.0"', $this->read_file( $root . '/package.json' ), 'Package versions should be bumped.' ); + self::assertStringContainsString( 'Project-Id-Version: Demo plugin 0.2.0\\n', $this->read_file( $root . '/languages/demo.pot' ), 'POT project versions should be bumped.' ); + self::assertStringContainsString( '"version": "0.2.0"', $this->read_file( $root . '/src/example/block.json' ), 'Block metadata versions should be bumped.' ); } + /** + * Test prerelease versions are rejected. + * + * @covers ::bump_plugin_version + */ public function test_bump_rejects_prerelease_versions(): void { $root = $this->make_fixture( [ @@ -70,6 +103,11 @@ public function test_bump_rejects_prerelease_versions(): void { bump_plugin_version( $root, 'patch' ); } + /** + * Test ambiguous version surfaces fail closed. + * + * @covers ::bump_plugin_version + */ public function test_bump_fails_closed_when_a_surface_is_ambiguous(): void { $root = $this->make_fixture( [ @@ -89,6 +127,8 @@ public function test_bump_fails_closed_when_a_surface_is_ambiguous(): void { } /** + * Make a temporary fixture directory. + * * @param array $files Files keyed by relative path. */ private function make_fixture( array $files ): string { @@ -109,6 +149,8 @@ private function make_fixture( array $files ): string { } /** + * Configure environment variables for the helper. + * * @param array $env Environment values. */ private function configure_env( array $env ): void { @@ -117,6 +159,27 @@ private function configure_env( array $env ): void { } } + /** + * Read a fixture file. + * + * @param string $path File path. + * @throws RuntimeException When the fixture cannot be read. + */ + private function read_file( string $path ): string { + $contents = file_get_contents( $path ); + + if ( false === $contents ) { + throw new RuntimeException( "Unable to read fixture file: {$path}" ); + } + + return $contents; + } + + /** + * Delete a temporary fixture tree. + * + * @param string $path Directory path. + */ private function delete_tree( string $path ): void { if ( ! is_dir( $path ) ) { return; From 48f20da2aedd0d18aa7d22c3e7e4c60c7298edc8 Mon Sep 17 00:00:00 2001 From: Ramon Corrales Date: Mon, 18 May 2026 13:59:26 -0500 Subject: [PATCH 2/3] test: localize lint suppressions Co-authored-by: Codex --- scripts/bump-plugin-version.php | 14 +++---- tests/Unit/BumpPluginVersionTest.php | 61 +++++++++++++++------------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/scripts/bump-plugin-version.php b/scripts/bump-plugin-version.php index 05bf362..fa0c40c 100644 --- a/scripts/bump-plugin-version.php +++ b/scripts/bump-plugin-version.php @@ -5,10 +5,6 @@ * @package WPVDB_Scripts */ -// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite -// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents -// phpcs:disable WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown - declare(strict_types=1); if ( is_bump_plugin_version_entrypoint( $argv ?? [] ) ) { @@ -34,14 +30,14 @@ function is_bump_plugin_version_entrypoint( array $argv ): bool { */ function bump_plugin_version_cli( array $argv ): int { if ( 'cli' !== PHP_SAPI ) { - fwrite( STDERR, "This script must run from the command line.\n" ); + fwrite( STDERR, "This script must run from the command line.\n" ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite -- CLI scripts write status messages to STDERR. return 1; } $root = getcwd(); if ( false === $root ) { - fwrite( STDERR, "Unable to determine repository root.\n" ); + fwrite( STDERR, "Unable to determine repository root.\n" ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite -- CLI scripts write status messages to STDERR. return 1; } @@ -49,10 +45,10 @@ function bump_plugin_version_cli( array $argv ): int { try { $new_version = bump_plugin_version( $root, $bump_type ); - fwrite( STDOUT, "Bumped plugin version to {$new_version}\n" ); + fwrite( STDOUT, "Bumped plugin version to {$new_version}\n" ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite -- CLI scripts write status messages to STDOUT. return 0; } catch ( Throwable $throwable ) { - fwrite( STDERR, $throwable->getMessage() . "\n" ); + fwrite( STDERR, $throwable->getMessage() . "\n" ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_fwrite -- CLI scripts write status messages to STDERR. return 1; } } @@ -147,6 +143,7 @@ function bump_plugin_version( string $root, string $bump_type ): string { } foreach ( $writes as $path => $contents ) { + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents -- This CLI helper intentionally updates version metadata files. if ( file_put_contents( $path, $contents ) === false ) { throw new RuntimeException( "Unable to write {$path}" ); } @@ -198,6 +195,7 @@ function path_join( string $base, string $path ): string { * @throws RuntimeException When the file cannot be read. */ function read_required_file( string $path ): string { + // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown -- Local filesystem reads are this CLI helper's purpose. $contents = file_get_contents( $path ); if ( false === $contents ) { diff --git a/tests/Unit/BumpPluginVersionTest.php b/tests/Unit/BumpPluginVersionTest.php index eb71c31..9fa4fc4 100644 --- a/tests/Unit/BumpPluginVersionTest.php +++ b/tests/Unit/BumpPluginVersionTest.php @@ -5,13 +5,6 @@ * @package WPVDB_Scripts */ -// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.directory_mkdir -// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.directory_rmdir -// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents -// phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_unlink -// phpcs:disable WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown - declare(strict_types=1); namespace WPVDB_Scripts\Tests\Unit; @@ -20,6 +13,7 @@ use RuntimeException; use function bump_plugin_version; +use function read_required_file; /** * Tests plugin version bump behavior. @@ -34,6 +28,13 @@ final class BumpPluginVersionTest extends TestCase { */ private array $temp_dirs = []; + /** + * Previous environment values. + * + * @var array + */ + private array $previous_env = []; + /** * Clean temporary fixtures and environment. */ @@ -43,8 +44,16 @@ protected function tearDown(): void { } foreach ( [ 'PLUGIN_FILE', 'VERSION_CONSTANT', 'PACKAGE_FILE', 'POT_FILE', 'POT_PROJECT', 'BLOCK_JSON_GLOB' ] as $name ) { - putenv( $name ); + if ( array_key_exists( $name, $this->previous_env ) && false !== $this->previous_env[ $name ] ) { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- Tests isolate CLI helper environment variables. + putenv( "{$name}={$this->previous_env[ $name ]}" ); + } else { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- Tests isolate CLI helper environment variables. + putenv( $name ); + } } + + $this->previous_env = []; } /** @@ -73,11 +82,11 @@ public function test_bump_updates_all_configured_version_surfaces(): void { ); self::assertSame( '0.2.0', bump_plugin_version( $root, 'minor' ), 'Minor bumps should update the returned version.' ); - self::assertStringContainsString( "* Version: 0.2.0\n", $this->read_file( $root . '/demo-plugin.php' ), 'Plugin headers should be bumped.' ); - self::assertStringContainsString( "define( 'DEMO_PLUGIN_VERSION', '0.2.0' );", $this->read_file( $root . '/demo-plugin.php' ), 'Version constants should be bumped.' ); - self::assertStringContainsString( '"version": "0.2.0"', $this->read_file( $root . '/package.json' ), 'Package versions should be bumped.' ); - self::assertStringContainsString( 'Project-Id-Version: Demo plugin 0.2.0\\n', $this->read_file( $root . '/languages/demo.pot' ), 'POT project versions should be bumped.' ); - self::assertStringContainsString( '"version": "0.2.0"', $this->read_file( $root . '/src/example/block.json' ), 'Block metadata versions should be bumped.' ); + self::assertStringContainsString( "* Version: 0.2.0\n", read_required_file( $root . '/demo-plugin.php' ), 'Plugin headers should be bumped.' ); + self::assertStringContainsString( "define( 'DEMO_PLUGIN_VERSION', '0.2.0' );", read_required_file( $root . '/demo-plugin.php' ), 'Version constants should be bumped.' ); + self::assertStringContainsString( '"version": "0.2.0"', read_required_file( $root . '/package.json' ), 'Package versions should be bumped.' ); + self::assertStringContainsString( 'Project-Id-Version: Demo plugin 0.2.0\\n', read_required_file( $root . '/languages/demo.pot' ), 'POT project versions should be bumped.' ); + self::assertStringContainsString( '"version": "0.2.0"', read_required_file( $root . '/src/example/block.json' ), 'Block metadata versions should be bumped.' ); } /** @@ -133,6 +142,7 @@ public function test_bump_fails_closed_when_a_surface_is_ambiguous(): void { */ private function make_fixture( array $files ): string { $root = sys_get_temp_dir() . '/wpvdb-scripts-test-' . bin2hex( random_bytes( 6 ) ); + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.directory_mkdir -- Tests create isolated temporary fixture directories. mkdir( $root, 0777, true ); $this->temp_dirs[] = $root; @@ -140,8 +150,10 @@ private function make_fixture( array $files ): string { $path = $root . '/' . $relative_path; $dir = dirname( $path ); if ( ! is_dir( $dir ) ) { + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.directory_mkdir -- Tests create isolated temporary fixture directories. mkdir( $dir, 0777, true ); } + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_file_put_contents -- Tests create isolated temporary fixture files. file_put_contents( $path, $contents ); } @@ -155,24 +167,13 @@ private function make_fixture( array $files ): string { */ private function configure_env( array $env ): void { foreach ( $env as $name => $value ) { - putenv( "{$name}={$value}" ); - } - } - - /** - * Read a fixture file. - * - * @param string $path File path. - * @throws RuntimeException When the fixture cannot be read. - */ - private function read_file( string $path ): string { - $contents = file_get_contents( $path ); + if ( ! array_key_exists( $name, $this->previous_env ) ) { + $this->previous_env[ $name ] = getenv( $name ); + } - if ( false === $contents ) { - throw new RuntimeException( "Unable to read fixture file: {$path}" ); + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- Tests configure CLI helper environment variables. + putenv( "{$name}={$value}" ); } - - return $contents; } /** @@ -191,9 +192,11 @@ private function delete_tree( string $path ): void { ); foreach ( $iterator as $item ) { + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.directory_rmdir, WordPressVIPMinimum.Functions.RestrictedFunctions.file_ops_unlink -- Tests clean isolated temporary fixtures. $item->isDir() ? rmdir( $item->getPathname() ) : unlink( $item->getPathname() ); } + // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.directory_rmdir -- Tests clean isolated temporary fixture directories. rmdir( $path ); } } From 67cdd9ea8e5e084777385bd082b1c43ce6cebb7c Mon Sep 17 00:00:00 2001 From: Ramon Corrales Date: Mon, 18 May 2026 14:13:12 -0500 Subject: [PATCH 3/3] test: preserve modified env values Co-authored-by: Codex --- tests/Unit/BumpPluginVersionTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Unit/BumpPluginVersionTest.php b/tests/Unit/BumpPluginVersionTest.php index 9fa4fc4..6e61e5a 100644 --- a/tests/Unit/BumpPluginVersionTest.php +++ b/tests/Unit/BumpPluginVersionTest.php @@ -43,10 +43,10 @@ protected function tearDown(): void { $this->delete_tree( $dir ); } - foreach ( [ 'PLUGIN_FILE', 'VERSION_CONSTANT', 'PACKAGE_FILE', 'POT_FILE', 'POT_PROJECT', 'BLOCK_JSON_GLOB' ] as $name ) { - if ( array_key_exists( $name, $this->previous_env ) && false !== $this->previous_env[ $name ] ) { + foreach ( $this->previous_env as $name => $previous_value ) { + if ( false !== $previous_value ) { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- Tests isolate CLI helper environment variables. - putenv( "{$name}={$this->previous_env[ $name ]}" ); + putenv( "{$name}={$previous_value}" ); } else { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- Tests isolate CLI helper environment variables. putenv( $name );