Files
wordpress-theme-aksrt/tests/admin/wechat-settings-harness.php
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00

152 lines
6.1 KiB
PHP

<?php
/**
* WeChat template-message settings harness.
*
* Drives the REAL UserController REST callbacks (get_wechat_status /
* update_wechat_settings) against WP stubs so the previously-broken routes are
* covered end to end:
* - GET /wechat/status returns the WeChatNotify status shape (masked secret);
* - POST /wechat/settings persists through Integration's key whitelist;
* - the *** mask keeps the stored secret instead of overwriting it;
* - omitted fields fall back to current values; unknown keys are dropped;
* - inputs are sanitized before storage.
*
* Usage: php wechat-settings-harness.php -> prints "WeChat settings harness passed".
*/
namespace {
define('ABSPATH', __DIR__ . '/');
final class WP_REST_Response {
public $data;
public $status;
public function __construct($data = null, int $status = 200) {
$this->data = $data;
$this->status = $status;
}
}
final class WP_REST_Request {
private array $json;
public function __construct(array $json = []) { $this->json = $json; }
public function get_json_params(): array { return $this->json; }
public function get_method(): string { return 'POST'; }
}
// ---------- WP function stubs ----------
function __($s, $d = null) { return (string) $s; }
function sanitize_text_field($s) { return trim(strip_tags((string) $s)); }
function current_user_can($c) { return true; }
function wp_send_json_success($d) { echo (string) json_encode($d); }
function get_transient($k) { return $GLOBALS['wechat_transient'] ?? false; }
function get_option($k, $default = false) { return $GLOBALS['wechat_options'][$k] ?? $default; }
function update_option($k, $v) { $GLOBALS['wechat_options'][$k] = $v; return true; }
$GLOBALS['wechat_options'] = ['jio_wechat_settings' => [
'enabled' => true,
'app_id' => 'wxAPPID',
'app_secret' => 'SECRET123',
'template_id' => 'TPL1',
]];
$GLOBALS['wechat_transient'] = 'ACCESS_TOKEN';
$GLOBALS['wpdb'] = new class {
public $usermeta = 'wp_usermeta';
public function get_var($sql) { return 7; }
};
}
namespace Jio\Settings {
// Settings 层仅为薄委托,harness 直接对接真实 Integration。
class Settings {
public static function get_wechat_settings(): array { return Integration::get_wechat_settings(); }
public static function update_wechat_settings(array $settings): void { Integration::update_wechat_settings($settings); }
}
}
namespace {
require_once dirname(__DIR__, 2) . '/src/Admin/ControllerBase.php';
require_once dirname(__DIR__, 2) . '/src/Admin/UserControllerReportOperations.php';
require_once dirname(__DIR__, 2) . '/src/Admin/UserControllerAuthPhoneOperations.php';
require_once dirname(__DIR__, 2) . '/src/Admin/UserControllerRestrictionOperations.php';
require_once dirname(__DIR__, 2) . '/src/Admin/UserControllerFollowOperations.php';
require_once dirname(__DIR__, 2) . '/src/Admin/UserControllerProfileMediaOperations.php';
require_once dirname(__DIR__, 2) . '/src/Admin/UserController.php';
require_once dirname(__DIR__, 2) . '/src/Settings/Integration.php';
require_once dirname(__DIR__, 2) . '/src/Settings/DomainGroups.php';
require_once dirname(__DIR__, 2) . '/src/Payment/WeChatNotify.php';
$failures = [];
$check = static function (string $label, bool $ok) use (&$failures): void {
if (!$ok) {
$failures[] = $label;
}
};
$stored = static function (): array {
$v = get_option('jio_wechat_settings', []);
return is_array($v) ? $v : [];
};
$controller = \Jio\Admin\UserController::get_instance();
// 1) GET /wechat/status 返回完整状态形状(密钥掩码)。
$response = $controller->get_wechat_status(new WP_REST_Request());
$data = is_array($response->data) ? ($response->data['data'] ?? []) : [];
$check('status 含 enabled', array_key_exists('enabled', $data) && !empty($data['enabled']));
$check('status 含 appid', ($data['appid'] ?? '') === 'wxAPPID');
$check('status configured', !empty($data['configured']));
$check('status has_token', !empty($data['has_token']));
$check('status has_template', !empty($data['has_template']));
$check('status bound_users', ($data['bound_users'] ?? 0) === 7);
$check('status 掩码 app_secret', ($data['app_secret'] ?? '') === '***');
$check('status allowed_types 非空', !empty($data['allowed_types']));
// 2) 完整更新写入全部字段。
$response = $controller->update_wechat_settings(new WP_REST_Request([
'enabled' => true,
'app_id' => 'wxNEW',
'app_secret' => 'NEWSECRET',
'template_id' => 'TPL2',
]));
$check('更新写入 app_id', ($stored()['app_id'] ?? '') === 'wxNEW');
$check('更新写入 app_secret', ($stored()['app_secret'] ?? '') === 'NEWSECRET');
$check('更新写入 template_id', ($stored()['template_id'] ?? '') === 'TPL2');
$check('更新返回成功消息', str_contains((string) ($response->data['message'] ?? ''), '已保存'));
// 3) 掩码 *** 保留已存密钥。
$controller->update_wechat_settings(new WP_REST_Request([
'enabled' => true,
'app_id' => 'wxNEW',
'app_secret' => '***',
]));
$check('掩码保留原密钥', ($stored()['app_secret'] ?? '') === 'NEWSECRET');
// 4) 缺省字段沿用当前值,开关可关闭。
$controller->update_wechat_settings(new WP_REST_Request(['enabled' => false]));
$check('缺省字段沿用当前值', ($stored()['app_id'] ?? '') === 'wxNEW' && ($stored()['template_id'] ?? '') === 'TPL2');
$check('enabled 可关闭', empty($stored()['enabled']));
// 5) 输入消毒 + 未知键被白名单丢弃。
$controller->update_wechat_settings(new WP_REST_Request([
'app_id' => '<b>wxXSS</b>',
'evil' => 'x',
]));
$check('app_id 经消毒', ($stored()['app_id'] ?? '') === 'wxXSS');
$check('未知键被忽略', !array_key_exists('evil', $stored()));
// 6) 未配置时空密钥可显式清空。
update_option('jio_wechat_settings', ['enabled' => true, 'app_id' => 'a', 'app_secret' => '', 'template_id' => '']);
$controller->update_wechat_settings(new WP_REST_Request(['app_secret' => '']));
$check('空密钥可清存', ($stored()['app_secret'] ?? '') === '');
if ($failures) {
echo "WeChat settings harness FAILED\n";
foreach ($failures as $failure) {
echo ' - ' . $failure . "\n";
}
exit(1);
}
echo "WeChat settings harness passed\n";
}