Resolves the rewrite slug collision that was 404'ing all 89 production peptide detail pages. PR Core v0.1.1 registered an empty pr_peptide CPT claiming /peptides/%postname%/; WP's rewrite resolver picked it over PSA's populated peptide CPT. This release consolidates both registrations onto a single peptide CPT owned by PR Core. PSA v4.5.0 drops its registration in favor of this one. ## Contract (per CTO msg 02) - POST_TYPE: pr_peptide -> peptide - TAX_CATEGORY: pr_peptide_category -> peptide_category - TAX_FAMILY const removed (pr_peptide_family was never populated) - CPT + taxonomy registration guarded with post_type_exists() / taxonomy_exists() so deploy order between PR Core and PSA is forgiving - Args harmonized as superset of PSA + prior PR Core (thumbnail added to supports; capability_type + map_meta_cap preserved) - Activator: flush_rewrite_rules(false) on activation; new maybe_flush_on_version_change() handler hooked at init:999 for in-place updates that bypass the activation hook (e.g. SCP deploys) - uninstall.php: blanket DELETE replaced with JOIN on _pr_core_authored so PSA-authored peptide posts (the 89 canonical monographs) are never destroyed; peptide_category terms preserved (shared ownership) - Scrub scope: narrow — CPT-slug-derived identifiers only; class names, file names, PR_CORE_* constants, pr_core_* options, and pr-core/v1 REST namespace remain ## Docs - ARCHITECTURE.md: new Key Decision #7 (v0.2.0 consolidation) and §2.9 Uninstall specification - CONVENTIONS.md: CPT Ownership rule + _pr_core_authored flag contract ## Tests - Lightweight harness (tests/bootstrap.php) that stubs the handful of WP functions exercised by the CPT class - tests/unit/test-peptide-cpt.php asserts: constants match contract, TAX_FAMILY is removed, guard no-ops when post_type_exists/taxonomy_exists returns true, args payload shape (thumbnail in supports, rewrite slug, REST namespace), hooks wired - CI: new 'Unit tests (lightweight, no PHPUnit)' step in lint-php job, runs across PHP 8.1/8.2/8.3. Full PHPUnit + wp-env harness is a follow-up Refs: convo/prcore/threads/2026-04-peptides-cpt-collision/02-cto-correction.md
149 lines
4.4 KiB
PHP
149 lines
4.4 KiB
PHP
<?php
|
|
/**
|
|
* Minimal test bootstrap — stubs the WordPress functions used by the classes
|
|
* under test so they can be exercised in plain PHP without a WP test install.
|
|
*
|
|
* This is intentionally NOT a full WP_UnitTestCase harness. It exists so the
|
|
* targeted unit assertions in tests/unit/ can verify guard behavior, constant
|
|
* shape, and args payload shape on CI (PHP lint job) without introducing a
|
|
* PHPUnit + wp-env dependency. A proper integration test harness is a
|
|
* follow-up (see CHANGELOG 0.2.0 notes).
|
|
*
|
|
* @package PeptideRepoCore
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
define( 'ABSPATH', __DIR__ . '/' );
|
|
}
|
|
|
|
if ( ! defined( 'PR_CORE_VERSION' ) ) {
|
|
define( 'PR_CORE_VERSION', '0.2.0' );
|
|
}
|
|
|
|
/**
|
|
* Global harness state. Tests reset this between cases.
|
|
*
|
|
* @var array<string, mixed>
|
|
*/
|
|
$GLOBALS['pr_core_test_state'] = [
|
|
'existing_post_types' => [],
|
|
'existing_taxonomies' => [],
|
|
'registered_post_types' => [],
|
|
'registered_taxonomies' => [],
|
|
'registered_meta' => [],
|
|
'added_actions' => [],
|
|
];
|
|
|
|
function pr_core_test_reset(): void {
|
|
$GLOBALS['pr_core_test_state'] = [
|
|
'existing_post_types' => [],
|
|
'existing_taxonomies' => [],
|
|
'registered_post_types' => [],
|
|
'registered_taxonomies' => [],
|
|
'registered_meta' => [],
|
|
'added_actions' => [],
|
|
];
|
|
}
|
|
|
|
function post_type_exists( string $post_type ): bool {
|
|
return in_array( $post_type, $GLOBALS['pr_core_test_state']['existing_post_types'], true );
|
|
}
|
|
|
|
function taxonomy_exists( string $taxonomy ): bool {
|
|
return in_array( $taxonomy, $GLOBALS['pr_core_test_state']['existing_taxonomies'], true );
|
|
}
|
|
|
|
function register_post_type( string $post_type, array $args = [] ) {
|
|
$GLOBALS['pr_core_test_state']['registered_post_types'][ $post_type ] = $args;
|
|
$GLOBALS['pr_core_test_state']['existing_post_types'][] = $post_type;
|
|
return (object) [ 'name' => $post_type, 'args' => $args ];
|
|
}
|
|
|
|
function register_taxonomy( string $taxonomy, $object_type, array $args = [] ): void {
|
|
$GLOBALS['pr_core_test_state']['registered_taxonomies'][ $taxonomy ] = [
|
|
'object_type' => $object_type,
|
|
'args' => $args,
|
|
];
|
|
$GLOBALS['pr_core_test_state']['existing_taxonomies'][] = $taxonomy;
|
|
}
|
|
|
|
function register_post_meta( string $post_type, string $key, array $args = [] ): bool {
|
|
$GLOBALS['pr_core_test_state']['registered_meta'][ $post_type ][ $key ] = $args;
|
|
return true;
|
|
}
|
|
|
|
function add_action( string $hook, $callback, int $priority = 10, int $accepted_args = 1 ): bool {
|
|
$GLOBALS['pr_core_test_state']['added_actions'][] = [
|
|
'hook' => $hook,
|
|
'callback' => $callback,
|
|
'priority' => $priority,
|
|
];
|
|
return true;
|
|
}
|
|
|
|
function __( string $text, string $domain = 'default' ): string {
|
|
return $text;
|
|
}
|
|
|
|
function current_user_can( string $capability ): bool {
|
|
return true;
|
|
}
|
|
|
|
function sanitize_text_field( $value ): string {
|
|
return is_scalar( $value ) ? trim( (string) $value ) : '';
|
|
}
|
|
|
|
function wp_json_encode( $data ): string {
|
|
return json_encode( $data ) ?: '[]';
|
|
}
|
|
|
|
function absint( $value ): int {
|
|
return abs( (int) $value );
|
|
}
|
|
|
|
function floatval( $value ): float {
|
|
return (float) $value;
|
|
}
|
|
|
|
/* ── Test assertion helpers ─────────────────────────────────────────── */
|
|
|
|
$GLOBALS['pr_core_test_report'] = [ 'pass' => 0, 'fail' => 0, 'failures' => [] ];
|
|
|
|
function pr_assert( bool $condition, string $label ): void {
|
|
if ( $condition ) {
|
|
$GLOBALS['pr_core_test_report']['pass']++;
|
|
echo " PASS: {$label}\n";
|
|
} else {
|
|
$GLOBALS['pr_core_test_report']['fail']++;
|
|
$GLOBALS['pr_core_test_report']['failures'][] = $label;
|
|
echo " FAIL: {$label}\n";
|
|
}
|
|
}
|
|
|
|
function pr_assert_equals( $expected, $actual, string $label ): void {
|
|
$pass = ( $expected === $actual );
|
|
pr_assert( $pass, $label . ( $pass ? '' : ' — expected ' . var_export( $expected, true ) . ', got ' . var_export( $actual, true ) ) );
|
|
}
|
|
|
|
function pr_test_summary(): int {
|
|
$r = $GLOBALS['pr_core_test_report'];
|
|
echo "\n---\n";
|
|
echo "Totals: {$r['pass']} passed, {$r['fail']} failed\n";
|
|
if ( $r['fail'] > 0 ) {
|
|
echo "Failures:\n";
|
|
foreach ( $r['failures'] as $f ) {
|
|
echo " - {$f}\n";
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ── Load the plugin's autoloader + required classes ─────────────────── */
|
|
|
|
require_once __DIR__ . '/../includes/class-pr-core-autoloader.php';
|
|
PR_Core_Autoloader::register();
|
|
require_once __DIR__ . '/../includes/cpt/class-pr-core-peptide-cpt.php';
|