refactor: extract upgrade logic to PSA_Upgrade class (fixes CI)

CI on 61cf00c failed PHPUnit with "Call to undefined function
plugin_dir_path()" — the test required peptide-search-ai.php, which calls
plugin_dir_path() at top level (line 19) before any hook. Stubbing every
top-level WP function the plugin file touches was the wrong shape of fix.

Proper fix: move the upgrade logic into includes/class-psa-upgrade.php so
the test can require just the helper class, matching the pattern every
other includes/ module follows.

- New: includes/class-psa-upgrade.php with PSA_Upgrade::is_needed()
  (pure) and PSA_Upgrade::maybe_run() (driver). OPTION_NAME and
  DEFAULT_STORED_VERSION exposed as constants.
- peptide-search-ai.php: psa_admin_init() now calls PSA_Upgrade::maybe_run();
  the inline helper functions and docblocks are removed.
- tests/bootstrap.php: loads class-psa-upgrade.php alongside the other
  tested classes.
- tests/unit/UpgradeTest.php: rewritten against PSA_Upgrade; adds two
  extra assertions on the constants for stability.
- ARCHITECTURE.md: refreshed to reference the class and its constants.

No behavioral change. The cost-tracker schema upgrade still runs once per
admin_init when PSA_VERSION advances past psa_db_version.

Agent-Session: cowork-qa-p1-fixes-20260414
This commit is contained in:
peptiderepo
2026-04-14 19:54:23 +07:00
parent 61cf00c322
commit d69f637b06
5 changed files with 119 additions and 81 deletions

View File

@@ -1,9 +1,10 @@
<?php
/**
* Tests for the schema-upgrade decision logic in peptide-search-ai.php.
* Tests for the schema-upgrade decision logic in PSA_Upgrade.
*
* Covers psa_upgrade_needed() across the three version-comparison
* scenarios: fresh install, already-upgraded, and future-version.
* Covers PSA_Upgrade::is_needed() across the four version-comparison
* scenarios: fresh install, already-upgraded, future-version (downgrade),
* and older stored version.
*
* @package PeptideSearchAI
*/
@@ -12,37 +13,14 @@ declare( strict_types=1 );
use PHPUnit\Framework\TestCase;
// Stub WP hook registrars so requiring the plugin file does not explode.
// The plugin bootstraps itself with add_action() / register_*_hook() calls at
// top level; these must be no-ops in the test context.
if ( ! function_exists( 'add_action' ) ) {
function add_action() {}
}
if ( ! function_exists( 'add_filter' ) ) {
function add_filter() {}
}
if ( ! function_exists( 'add_shortcode' ) ) {
function add_shortcode() {}
}
if ( ! function_exists( 'register_activation_hook' ) ) {
function register_activation_hook() {}
}
if ( ! function_exists( 'register_deactivation_hook' ) ) {
function register_deactivation_hook() {}
}
// Load the plugin file once so the pure helper becomes available. The
// plugin's top-level side effects are limited to hook registrations, which
// are neutralized by the stubs above and the ones in bootstrap.php.
if ( ! function_exists( 'psa_upgrade_needed' ) ) {
require_once ABSPATH . 'peptide-search-ai.php';
}
// PSA_Upgrade is already loaded by tests/bootstrap.php via class-psa-upgrade.php.
// No plugin-file bootstrap needed — the decision helper is a pure function.
/**
* Test case for psa_upgrade_needed().
* Test case for PSA_Upgrade::is_needed().
*
* psa_upgrade_needed() is a pure function — no side effects, no WordPress
* API calls — so it can be exercised directly without a live option store.
* The helper is a pure static method (no WordPress API calls, no side
* effects), so it can be exercised directly without a live option store.
*/
class UpgradeTest extends TestCase {
@@ -52,7 +30,7 @@ class UpgradeTest extends TestCase {
*/
public function test_fresh_install_triggers_upgrade(): void {
$this->assertTrue(
psa_upgrade_needed( '0.0.0', '4.4.3' ),
PSA_Upgrade::is_needed( '0.0.0', '4.4.3' ),
'Fresh install (stored = 0.0.0) must trigger an upgrade against any released version.'
);
}
@@ -62,7 +40,7 @@ class UpgradeTest extends TestCase {
*/
public function test_same_version_skips_upgrade(): void {
$this->assertFalse(
psa_upgrade_needed( '4.4.3', '4.4.3' ),
PSA_Upgrade::is_needed( '4.4.3', '4.4.3' ),
'Stored version equal to current version must not trigger an upgrade.'
);
}
@@ -74,7 +52,7 @@ class UpgradeTest extends TestCase {
*/
public function test_future_stored_version_skips_upgrade(): void {
$this->assertFalse(
psa_upgrade_needed( '5.0.0', '4.4.3' ),
PSA_Upgrade::is_needed( '5.0.0', '4.4.3' ),
'Stored version greater than current version must not trigger an upgrade.'
);
}
@@ -86,8 +64,24 @@ class UpgradeTest extends TestCase {
*/
public function test_older_stored_version_triggers_upgrade(): void {
$this->assertTrue(
psa_upgrade_needed( '4.4.2', '4.4.3' ),
PSA_Upgrade::is_needed( '4.4.2', '4.4.3' ),
'Stored version older than current version must trigger an upgrade.'
);
}
/**
* Constants used by the driver should be stable across releases so
* existing installs keep pointing at the same option key.
*/
public function test_option_name_constant_is_stable(): void {
$this->assertSame( 'psa_db_version', PSA_Upgrade::OPTION_NAME );
}
/**
* Default stored version must be strictly less than any future release
* so fresh-install detection keeps working.
*/
public function test_default_stored_version_is_zero(): void {
$this->assertSame( '0.0.0', PSA_Upgrade::DEFAULT_STORED_VERSION );
}
}