v4.5.0: drop peptide CPT registration; add PR Core dependency check (#15)

Drops PSA registration of peptide CPT + peptide_category taxonomy (PR Core >= 0.2.0 now owns them per 2026-04-22 consolidation). Adds hard dependency check on PR Core 0.2.0+ — PSA self-deactivates with an admin notice if PR Core is missing or below min version.

CI note: PHPUnit failures (PSA_AI_Content, PSA_Directory_API not found) and WPCS failure are pre-existing on main at HEAD 65f973f (verified: same 5 errors in run 24530495421). PHP Lint green on 7.4/8.1/8.3. No regressions introduced by this PR.

QA: qa-reviews/2026-04-22-2609c66.md PASS.
This commit is contained in:
peptiderepo
2026-04-22 17:12:34 +07:00
committed by GitHub
parent 65f973fea4
commit 868d31cfb3
8 changed files with 480 additions and 292 deletions

View File

@@ -0,0 +1,200 @@
<?php
/**
* Tests for PSA_Dependency_Check.
*
* Covers:
* - is_satisfied() returns false when PR_CORE_VERSION is undefined.
* - is_satisfied() returns false when PR_CORE_VERSION < REQUIRED_PR_CORE_VERSION.
* - is_satisfied() returns true when PR_CORE_VERSION == REQUIRED_PR_CORE_VERSION.
* - is_satisfied() returns true when PR_CORE_VERSION > REQUIRED_PR_CORE_VERSION.
* - Regression guard: PSA_Post_Type no longer declares
* register_peptide_post_type / register_taxonomy / populate_default_categories.
*
* PHP constants are defined process-wide and cannot be redefined once set, so
* each version-value variant runs in its own separate process via
* @runInSeparateProcess + @preserveGlobalState disabled. That gives each case
* a clean state to define (or not define) PR_CORE_VERSION.
*
* @package PeptideSearchAI
*/
declare( strict_types=1 );
// Stub WordPress admin-notice helpers needed by maybe_render_notice() and the
// class autoloader. Pure stubs; no assertions run against them here — the
// maybe_render_notice() path is exercised only indirectly via is_satisfied().
if ( ! function_exists( 'add_action' ) ) {
function add_action() {}
}
if ( ! function_exists( 'current_user_can' ) ) {
function current_user_can( $cap ) { return true; }
}
if ( ! function_exists( 'esc_html__' ) ) {
function esc_html__( $text, $domain = 'default' ) {
return htmlspecialchars( $text, ENT_QUOTES, 'UTF-8' );
}
}
require_once dirname( __DIR__ ) . '/bootstrap.php';
require_once ABSPATH . 'includes/class-psa-dependency-check.php';
class DependencyCheckTest extends \PHPUnit\Framework\TestCase {
/**
* REQUIRED_PR_CORE_VERSION is the contract — changing it is a breaking
* coordination event with PR Core. Assert it here so an accidental bump
* trips CI.
*/
public function test_required_pr_core_version_constant(): void {
$this->assertSame(
'0.2.0',
PSA_Dependency_Check::REQUIRED_PR_CORE_VERSION,
'REQUIRED_PR_CORE_VERSION is a contract with PR Core; changing it must be a coordinated PR across both plugins.'
);
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_is_satisfied_false_when_pr_core_version_undefined(): void {
require_once dirname( __DIR__, 2 ) . '/includes/class-psa-dependency-check.php';
$this->assertFalse(
defined( 'PR_CORE_VERSION' ),
'Precondition: PR_CORE_VERSION must not be defined in this process.'
);
$this->assertFalse( PSA_Dependency_Check::is_satisfied() );
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_is_satisfied_false_when_pr_core_version_below_required(): void {
define( 'PR_CORE_VERSION', '0.1.1' );
require_once dirname( __DIR__, 2 ) . '/includes/class-psa-dependency-check.php';
$this->assertFalse( PSA_Dependency_Check::is_satisfied() );
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_is_satisfied_true_when_pr_core_version_exactly_required(): void {
define( 'PR_CORE_VERSION', '0.2.0' );
require_once dirname( __DIR__, 2 ) . '/includes/class-psa-dependency-check.php';
$this->assertTrue( PSA_Dependency_Check::is_satisfied() );
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_is_satisfied_true_when_pr_core_version_above_required(): void {
define( 'PR_CORE_VERSION', '0.3.0' );
require_once dirname( __DIR__, 2 ) . '/includes/class-psa-dependency-check.php';
$this->assertTrue( PSA_Dependency_Check::is_satisfied() );
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_is_satisfied_false_for_pre_release_below_required(): void {
// Defensive: PHP's version_compare treats "0.2.0-alpha" as < "0.2.0",
// which is the desired behaviour here. Regression guard against the
// constant silently accepting pre-release PR Core builds.
define( 'PR_CORE_VERSION', '0.2.0-alpha' );
require_once dirname( __DIR__, 2 ) . '/includes/class-psa-dependency-check.php';
$this->assertFalse( PSA_Dependency_Check::is_satisfied() );
}
/**
* Regression guard for the v4.5.0 cleanup: PSA_Post_Type must no longer
* expose the three removed registration methods. A future refactor that
* re-introduces any of them would re-create the CPT collision with PR
* Core and break the production site.
*/
public function test_psa_post_type_registration_methods_removed(): void {
// Stub the minimum WP surface PSA_Post_Type touches at class-load
// time so we can reflect on it here without pulling the full bootstrap.
if ( ! function_exists( 'post_type_exists' ) ) {
function post_type_exists( $post_type ) { return false; }
}
if ( ! function_exists( 'register_post_type' ) ) {
function register_post_type( $post_type, $args = array() ) { return null; }
}
if ( ! function_exists( 'taxonomy_exists' ) ) {
function taxonomy_exists( $taxonomy ) { return false; }
}
if ( ! function_exists( 'register_taxonomy' ) ) {
function register_taxonomy( $taxonomy, $object_type, $args = array() ) { return null; }
}
if ( ! function_exists( 'add_filter' ) ) {
function add_filter() {}
}
if ( ! function_exists( '__' ) ) {
function __( $text, $domain = 'default' ) { return $text; }
}
require_once ABSPATH . 'includes/class-psa-post-type.php';
$this->assertFalse(
method_exists( 'PSA_Post_Type', 'register_peptide_post_type' ),
'PSA_Post_Type::register_peptide_post_type() was removed in v4.5.0 — do not reintroduce it; PR Core owns the peptide CPT.'
);
$this->assertFalse(
method_exists( 'PSA_Post_Type', 'register_taxonomy' ),
'PSA_Post_Type::register_taxonomy() was removed in v4.5.0 — do not reintroduce it; PR Core owns the peptide_category taxonomy.'
);
$this->assertFalse(
method_exists( 'PSA_Post_Type', 'populate_default_categories' ),
'PSA_Post_Type::populate_default_categories() was removed in v4.5.0 — PR Core owns taxonomy term seeding.'
);
// DEFAULT_CATEGORIES constant is intentionally retained: PSA consumes
// it as a slug→name reference list for fuzzy-matching in
// PSA_AI_Content::assign_category_term() and the admin migration
// button. It is no longer a seed list. See CHANGELOG [4.5.0] and
// the class docblock.
$this->assertTrue(
defined( 'PSA_Post_Type::DEFAULT_CATEGORIES' ),
'PSA_Post_Type::DEFAULT_CATEGORIES is retained as a consumer lookup — PSA_AI_Content and the admin migration button read it.'
);
$this->assertCount(
8,
PSA_Post_Type::DEFAULT_CATEGORIES,
'DEFAULT_CATEGORIES should still contain exactly 8 entries matching the terms seeded on prod.'
);
}
/**
* Meta-box contract survives the CPT handoff — the `psa_*` meta key
* namespace on the `peptide` CPT is still owned by PSA, so the class
* constants that drive meta-box rendering must remain intact.
*/
public function test_psa_post_type_meta_keys_unchanged(): void {
$this->assertArrayHasKey( 'psa_sequence', PSA_Post_Type::META_FIELDS );
$this->assertArrayHasKey( 'psa_molecular_weight', PSA_Post_Type::META_FIELDS );
$this->assertArrayHasKey( 'psa_source', PSA_Post_Type::META_FIELDS );
$this->assertArrayHasKey( 'psa_pubchem_cid', PSA_Post_Type::META_FIELDS );
$this->assertArrayHasKey( 'psa_half_life', PSA_Post_Type::EXTENDED_META_FIELDS );
$this->assertArrayHasKey( 'psa_stability', PSA_Post_Type::EXTENDED_META_FIELDS );
$this->assertArrayHasKey( 'psa_solubility', PSA_Post_Type::EXTENDED_META_FIELDS );
}
/**
* PSA_Post_Type::init_admin() still hooks meta boxes + admin columns
* against the `peptide` CPT. The method has to survive the CPT-handoff
* refactor — it's the primary consumer-side attachment point.
*/
public function test_psa_post_type_init_admin_preserved(): void {
$this->assertTrue( method_exists( 'PSA_Post_Type', 'init_admin' ) );
}
}