Skip to content

Tests: Add unit test coverage for the Plugin_Upgrader upgrade callbacks - #14581

Open
huzaifaalmesbah wants to merge 2 commits into
WordPress:trunkfrom
huzaifaalmesbah:tests/plugin-upgrader-upgrade-hooks
Open

huzaifaalmesbah wants to merge 2 commits into
WordPress:trunkfrom
huzaifaalmesbah:tests/plugin-upgrader-upgrade-hooks

Conversation

@huzaifaalmesbah

Copy link
Copy Markdown
Member

Description

Adds tests for the Plugin_Upgrader callbacks that run around a plugin upgrade: deactivate_plugin_before_upgrade(), active_before(), active_after() and delete_old_plugin(). None of them had tests.

The tests replace the filesystem global with a mock, so no files are touched, and use the wp_doing_cron filter to switch between manual and background upgrades. They cover:

  • an error response being passed through by all four callbacks without side effects;
  • a missing or empty plugin being a bad_request error;
  • an active plugin being deactivated silently (no deactivation hooks) before a manual upgrade, and being left active in cron or when it was already inactive;
  • maintenance mode being enabled and disabled for an active plugin in a background upgrade, and left alone for a manual upgrade, an inactive plugin or a bulk upgrade;
  • the old plugin being deleted recursively for a plugin in a directory and as a single file otherwise, nothing being deleted when the directory is already gone, and remove_old_failed when the deletion fails.

The maintenance mode test runs in a separate process, because the maintenance file is written with FS_CHMOD_FILE, which WP_Filesystem() defines. Removing or changing each branch of the four callbacks makes at least one test fail.

This is a test-only change; no production code is modified.

Testing Instructions

  1. Run the tests on single site:
    npm run test:php -- --filter Tests_Admin_PluginUpgraderUpgradeHooks
  2. Run them on multisite:
    npm run test:php -- --filter Tests_Admin_PluginUpgraderUpgradeHooks -c tests/phpunit/multisite.xml
  3. Run them in random order:
    npm run test:php -- --filter Tests_Admin_PluginUpgraderUpgradeHooks --order-by=random
  4. Run the plugins and upgrade groups:
    npm run test:php -- --group plugins,upgrade
  5. Run PHPCS on the test file:
    node ./tools/local-env/scripts/docker.js run --rm php ./vendor/bin/phpcs tests/phpunit/tests/admin/pluginUpgraderUpgradeHooks.php

Trac ticket: https://core.trac.wordpress.org/ticket/65819

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Brainstorming edge-case scenarios and drafting test boilerplate; test architecture, assertions, and local PHPUnit verification were written and validated by me.

@huzaifaalmesbah
huzaifaalmesbah marked this pull request as ready for review October 20, 2026 20:57
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props huzaifaalmesbah, lancewillett, mukesh27.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The maintenance test needs per-callback assertions to detect reversed enable/disable behavior.

1 open issue
What changed in this PR

Adds unit coverage for WordPress plugin-upgrade callbacks without changing production code.

Changes:

  • Tests error handling and silent plugin deactivation.
  • Covers maintenance-mode conditions and old-plugin deletion using a mocked filesystem.
File Description
tests/​phpunit/​tests/​admin/​pluginUpgraderUpgradeHooks.php Adds tests for four Plugin_Upgrader callbacks.

Review effort: Balanced

Comment on lines +239 to +246
$this->filesystem->expects( $this->once() )
->method( 'put_contents' )
->with( '/remote/.maintenance', $this->stringStartsWith( '<?php $upgrading = ' ), 0644 );
$this->filesystem->method( 'exists' )->willReturn( true );
$this->filesystem->expects( $this->exactly( 2 ) )->method( 'delete' )->with( '/remote/.maintenance' );

$this->assertTrue( $this->upgrader->active_before( true, array( 'plugin' => self::FILE ) ), 'active_before() should return the response.' );
$this->assertTrue( $this->upgrader->active_after( true, array( 'plugin' => self::FILE ) ), 'active_after() should return the response.' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed locally: swapping the maintenance-mode booleans in active_before() and active_after() still passes this test (1 test, 3 assertions). The shared call counts do not distinguish the two callbacks.

Please track the mocked maintenance-file state and assert that it exists immediately after active_before() and is absent immediately after active_after(), or test each callback independently. This should fail when their enable/disable behavior is reversed.

AI review · gpt-6

@lancewillett lancewillett left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please strengthen the maintenance-mode case as described in the inline thread. I confirmed that reversing the enable/disable callbacks leaves it green, so it does not yet protect the behavior named by the test. The other local checks pass.

AI review · gpt-6

@mukeshpanchal27 mukeshpanchal27 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests match the Plugin_Upgrader callbacks, and the mocked filesystem means no real .maintenance file is written. One suggestion inline.

* @preserveGlobalState disabled
*/
public function test_active_before_and_after_should_toggle_maintenance_mode_in_cron() {
define( 'FS_CHMOD_FILE', 0644 );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test runs in a separate process only so it can define FS_CHMOD_FILE. Each separate process starts WordPress again, which slows the suite. A guard would let it run in the normal process:

if ( ! defined( 'FS_CHMOD_FILE' ) ) {
	define( 'FS_CHMOD_FILE', 0644 );
}

The put_contents() expectation would then use FS_CHMOD_FILE instead of 0644, in case an earlier test already defined the constant with another value. With that change, @runInSeparateProcess and @preserveGlobalState disabled could go.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done in 9f4985a. The test now guards the define(), expects FS_CHMOD_FILE, and runs in the main process. The wpUpgrader.php tests that define the constant all run in separate processes, so they are not affected, and the whole tests/admin directory passes in both default and random order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants