fix(admin): load delegate classes + add WP test stubs (#8)

* fix(admin): load delegate classes from orchestrator + add WP stubs for tests

The admin refactor split introduced delegate classes that the orchestrator
instantiates in its constructor. CI failed because tests require the
orchestrator directly without going through the plugin loader. Fix: add
require_once for all delegate files at the top of the orchestrator, and
add missing WP function stubs to the test bootstrap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(admin): add missing sanitize_model_id and sanitize_api_key delegators

The refactored orchestrator was missing public method delegations for
sanitize_model_id() and sanitize_api_key(), which tests call directly
on the Peptide_News_Admin instance and WordPress uses as register_setting
callbacks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(phpcs): remove strict_types declaration that breaks file header order

PSR12.Files.FileHeader.IncorrectOrder requires the docblock immediately
after the opening PHP tag. The declare(strict_types=1) was inserted
between them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: peptiderepo <peptiderepo@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
peptiderepo
2026-04-16 17:01:34 +07:00
committed by GitHub
parent 0f3adf0c5d
commit 19fea30932
2 changed files with 86 additions and 1 deletions

View File

@@ -1,5 +1,4 @@
<?php <?php
declare( strict_types=1 );
/** /**
* Admin-specific functionality. * Admin-specific functionality.
* *
@@ -17,6 +16,16 @@ declare( strict_types=1 );
* @see Peptide_News_Admin_Settings_Page — Settings page + log viewer * @see Peptide_News_Admin_Settings_Page — Settings page + log viewer
* @see Peptide_News_Admin_Dashboard_Pages — Dashboard/articles/costs pages * @see Peptide_News_Admin_Dashboard_Pages — Dashboard/articles/costs pages
*/ */
// Load delegate classes that this orchestrator depends on.
require_once __DIR__ . '/class-pn-admin-assets.php';
require_once __DIR__ . '/class-pn-admin-menu.php';
require_once __DIR__ . '/class-pn-admin-field-renderers.php';
require_once __DIR__ . '/class-pn-admin-settings.php';
require_once __DIR__ . '/class-pn-admin-log-viewer.php';
require_once __DIR__ . '/class-pn-admin-settings-page.php';
require_once __DIR__ . '/class-pn-admin-dashboard-pages.php';
class Peptide_News_Admin { class Peptide_News_Admin {
/** @var string Plugin name/slug */ /** @var string Plugin name/slug */
@@ -140,4 +149,31 @@ class Peptide_News_Admin {
public function render_cost_dashboard_page(): void { public function render_cost_dashboard_page(): void {
$this->dashboard_pages->render_cost_dashboard_page(); $this->dashboard_pages->render_cost_dashboard_page();
} }
/**
* Sanitize and validate an OpenRouter model ID.
*
* Delegates to the settings class. Kept on the orchestrator because
* WordPress register_setting() callbacks may reference $this.
*
* @param string $value Raw model ID from form input.
* @return string Sanitized model ID or empty string on failure.
*/
public function sanitize_model_id( string $value ): string {
return $this->settings->sanitize_model_id( $value );
}
/**
* Sanitize an API key value — encrypt if new, preserve if masked.
*
* Delegates to the settings class. Kept on the orchestrator because
* WordPress register_setting() callbacks may reference $this.
*
* @param string $value Raw API key from form input.
* @param string $option_name The WP option being saved.
* @return string Encrypted key or previous value if input was masked.
*/
public function sanitize_api_key( string $value, string $option_name = '' ): string {
return $this->settings->sanitize_api_key( $value, $option_name );
}
} }

View File

@@ -300,6 +300,55 @@ if ( ! function_exists( 'selected' ) ) {
return $result; return $result;
} }
} }
if ( ! function_exists( 'checked' ) ) {
function checked( $checked, $current = true, $echo = true ) {
$result = (string) $checked === (string) $current ? ' checked="checked"' : '';
if ( $echo ) {
echo $result;
}
return $result;
}
}
if ( ! function_exists( 'wp_enqueue_style' ) ) {
function wp_enqueue_style( $handle, $src = '', $deps = array(), $ver = false, $media = 'all' ) {
return true;
}
}
if ( ! function_exists( 'wp_enqueue_script' ) ) {
function wp_enqueue_script( $handle, $src = '', $deps = array(), $ver = false, $args = array() ) {
return true;
}
}
if ( ! function_exists( 'wp_localize_script' ) ) {
function wp_localize_script( $handle, $object_name, $l10n ) {
return true;
}
}
if ( ! function_exists( 'add_menu_page' ) ) {
function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $icon_url = '', $position = null ) {
return $menu_slug;
}
}
if ( ! function_exists( 'add_submenu_page' ) ) {
function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) {
return $menu_slug;
}
}
if ( ! function_exists( 'register_setting' ) ) {
function register_setting( $option_group, $option_name, $args = array() ) {
return true;
}
}
if ( ! function_exists( 'add_settings_section' ) ) {
function add_settings_section( $id, $title, $callback, $page, $args = array() ) {
return true;
}
}
if ( ! function_exists( 'add_settings_field' ) ) {
function add_settings_field( $id, $title, $callback, $page, $section = 'default', $args = array() ) {
return true;
}
}
// Cost tracker provides calculate_cost(), budget checks used by tests. // Cost tracker provides calculate_cost(), budget checks used by tests.
require_once PEPTIDE_NEWS_PLUGIN_DIR . 'includes/class-peptide-news-cost-tracker.php'; require_once PEPTIDE_NEWS_PLUGIN_DIR . 'includes/class-peptide-news-cost-tracker.php';