prints "Baidu push harness passed". */ namespace { define('ABSPATH', __DIR__ . '/'); define('WP_DEBUG', false); final class WP_Error { public function __construct(private string $code, private string $message) {} public function get_error_message(): string { return $this->message; } } final class WP_Post { public int $ID = 0; public string $post_type = 'post'; public string $post_status = 'publish'; } final class WP_Term { public int $term_id = 0; public string $taxonomy = 'category'; } // ---------- WP function stubs ---------- function add_action(...$args) {} function apply_filters($tag, $value) { return $value; } function __($s, $d = null) { return (string) $s; } function esc_html($s) { return htmlspecialchars((string) $s, ENT_QUOTES); } function esc_attr($s) { return htmlspecialchars((string) $s, ENT_QUOTES); } function esc_html_e($s, $d = null) { echo esc_html($s); } function wp_json_encode($v, $f = 0) { return (string) json_encode($v, $f); } function wp_nonce_field($action, $name) { echo ''; } function wp_unslash($s) { return $s; } function sanitize_text_field($s) { return trim(strip_tags((string) $s)); } function wp_verify_nonce($nonce, $action) { return $nonce === 'valid-nonce' && $action === 'aksrt_push_meta_save'; } function is_wp_error($t) { return $t instanceof WP_Error; } function home_url($path = '/') { return 'https://example.test' . $path; } function current_time($type = 'mysql') { return '2026-10-13 12:00:00'; } function get_permalink($post) { return 'https://example.test/?p=' . (int) $post->ID; } function get_term_link($term, $taxonomy = '') { $id = is_object($term) ? (int) $term->term_id : (int) $term; return 'https://example.test/?cat=' . $id; } function wp_is_post_revision($id) { return false; } function wp_is_post_autosave($id) { return false; } function aksrt_searchable_post_types(): array { return ['post']; } function get_object_taxonomies($types, $output = 'names') { return ['post_tag', 'category', 'post_format']; } // Outgoing HTTP: every call is captured so channel/URL/body can be asserted. $GLOBALS['push_requests'] = []; function wp_remote_post($url, $args = []) { $GLOBALS['push_requests'][] = ['url' => (string) $url, 'body' => (string) ($args['body'] ?? '')]; return ['response' => ['code' => 200], 'body' => (string) json_encode(['success' => 1, 'remain' => 99])]; } function wp_remote_retrieve_body($r) { return is_array($r) ? (string) ($r['body'] ?? '') : ''; } function wp_remote_retrieve_response_code($r) { return is_array($r) ? (int) ($r['response']['code'] ?? 0) : 0; } // In-memory meta stores. $GLOBALS['push_post_meta'] = []; $GLOBALS['push_term_meta'] = []; function get_post_meta($id, $key, $single = false) { return $GLOBALS['push_post_meta'][$id][$key] ?? ''; } function update_post_meta($id, $key, $value) { $GLOBALS['push_post_meta'][$id][$key] = $value; return true; } function delete_post_meta($id, $key) { unset($GLOBALS['push_post_meta'][$id][$key]); return true; } function get_term_meta($id, $key, $single = false) { return $GLOBALS['push_term_meta'][$id][$key] ?? ''; } function update_term_meta($id, $key, $value) { $GLOBALS['push_term_meta'][$id][$key] = $value; return true; } function delete_term_meta($id, $key) { unset($GLOBALS['push_term_meta'][$id][$key]); return true; } } namespace AKSRT\Settings { class Settings { public static array $config = []; public static function get_search_push_settings(): array { return self::$config; } } } namespace { require_once dirname(__DIR__, 2) . '/src/Extensions/SearchPush.php'; require_once dirname(__DIR__, 2) . '/src/Extensions/SearchPushMetaBox.php'; use AKSRT\Extensions\SearchPush; use AKSRT\Extensions\SearchPushMetaBox; $failures = []; $check = static function (string $label, bool $ok) use (&$failures): void { if (!$ok) { $failures[] = $label; } }; $reset = static function (array $config): void { \AKSRT\Settings\Settings::$config = $config; $GLOBALS['push_requests'] = []; $GLOBALS['push_post_meta'] = []; $GLOBALS['push_term_meta'] = []; $_POST = []; }; $post = static function (int $id): WP_Post { $p = new WP_Post(); $p->ID = $id; return $p; }; $defaults = [ 'baidu_push_enabled' => true, 'baidu_push_token' => 'TOKEN', 'baidu_push_auto' => true, 'baidu_push_on_publish' => true, 'baidu_push_on_update' => false, 'baidu_push_normal' => true, 'baidu_push_fast' => false, 'baidu_push_log_enabled' => true, ]; $push = SearchPush::get_instance(); $api = 'http://data.zz.baidu.com/urls?site=' . rawurlencode('https://example.test/') . '&token=TOKEN'; // 1) First publish pushes once through the normal-collection endpoint. $reset($defaults); $push->on_transition_post_status('publish', 'draft', $post(11)); $check('首次发布推送一次', count($GLOBALS['push_requests']) === 1); $check('普通收录请求地址正确', ($GLOBALS['push_requests'][0]['url'] ?? '') === $api); $check('普通收录请求体为文章链接', ($GLOBALS['push_requests'][0]['body'] ?? '') === 'https://example.test/?p=11'); $record = SearchPush::get_push_record(11); $check('推送记录写入 meta', !empty($record['normal_push'])); $check('推送记录带更新时间', !empty($record['update_time'])); $check('推送记录不含快速收录', !array_key_exists('daily_push', $record)); // 2) A completed push is never re-submitted on save. $push->on_transition_post_status('publish', 'publish', $post(11)); $check('成功后更新不重复推送', count($GLOBALS['push_requests']) === 1); // 3) Failed push retries on update when the update switch is on. $reset(array_merge($defaults, ['baidu_push_on_update' => true])); SearchPush::save_push_record(12, ['normal_push' => false, 'normal_result' => null, 'update_time' => '2026-10-13 09:00:00']); $push->on_transition_post_status('publish', 'publish', $post(12)); $check('失败后更新重试一次', count($GLOBALS['push_requests']) === 1); $check('重试后记录为成功', !empty(SearchPush::get_push_record(12)['normal_push'])); $push->on_transition_post_status('publish', 'publish', $post(12)); $check('重试成功后不重复推送', count($GLOBALS['push_requests']) === 1); // 4) 「重新提交」with a valid nonce forces a push even after success. $reset($defaults); $push->on_transition_post_status('publish', 'draft', $post(13)); $_POST[SearchPush::RESUBMIT_FIELD] = '1'; $_POST[SearchPush::NONCE_FIELD] = 'valid-nonce'; $push->on_transition_post_status('publish', 'publish', $post(13)); $check('重新提交强制再次推送', count($GLOBALS['push_requests']) === 2); // 5) A forged nonce is ignored, dedup still applies. $reset($defaults); $push->on_transition_post_status('publish', 'draft', $post(14)); $_POST[SearchPush::RESUBMIT_FIELD] = '1'; $_POST[SearchPush::NONCE_FIELD] = 'forged-nonce'; $push->on_transition_post_status('publish', 'publish', $post(14)); $check('无效 nonce 的重新提交被忽略', count($GLOBALS['push_requests']) === 1); // 6) Fast collection appends type=daily and records both channels. $reset(array_merge($defaults, ['baidu_push_fast' => true])); $push->on_transition_post_status('publish', 'draft', $post(15)); $check('双通道推送两次', count($GLOBALS['push_requests']) === 2); $check('快速收录走 type=daily', str_contains($GLOBALS['push_requests'][1]['url'] ?? '', '&type=daily')); $record15 = SearchPush::get_push_record(15); $check('双通道记录完整', !empty($record15['normal_push']) && !empty($record15['daily_push'])); $push->on_transition_post_status('publish', 'publish', $post(15)); $check('双通道成功后不重复推送', count($GLOBALS['push_requests']) === 2); // 7) No channel enabled: nothing pushed, nothing recorded. $reset(array_merge($defaults, ['baidu_push_normal' => false])); $push->on_transition_post_status('publish', 'draft', $post(16)); $check('通道全关不推送', count($GLOBALS['push_requests']) === 0); $check('通道全关不写记录', SearchPush::get_push_record(16) === []); // 8) Feature off / token missing: nothing pushed, nothing recorded. $reset(array_merge($defaults, ['baidu_push_enabled' => false])); $push->on_transition_post_status('publish', 'draft', $post(17)); $check('未启用不推送', count($GLOBALS['push_requests']) === 0); $reset(array_merge($defaults, ['baidu_push_token' => ''])); $push->on_transition_post_status('publish', 'draft', $post(18)); $check('缺少 Token 不推送', count($GLOBALS['push_requests']) === 0); $check('缺少 Token 不写记录', SearchPush::get_push_record(18) === []); // 9) Terms push via saved_term with the same dedup / resubmit rules. $reset($defaults); $push->on_saved_term(21, 21, 'category'); $check('术语推送一次', count($GLOBALS['push_requests']) === 1); $check('术语推送链接正确', ($GLOBALS['push_requests'][0]['body'] ?? '') === 'https://example.test/?cat=21'); $check('术语记录写入', !empty(SearchPush::get_push_record(21, true)['normal_push'])); $push->on_saved_term(21, 21, 'category'); $check('术语成功后不重复推送', count($GLOBALS['push_requests']) === 1); $push->on_saved_term(22, 22, 'post_format'); $check('post_format 术语不推送', count($GLOBALS['push_requests']) === 1); $_POST[SearchPush::RESUBMIT_FIELD] = '1'; $_POST[SearchPush::NONCE_FIELD] = 'valid-nonce'; $push->on_saved_term(21, 21, 'category'); $check('术语重新提交强制推送', count($GLOBALS['push_requests']) === 2); // 10) Shared type / taxonomy policy. $check('可推送文章类型', SearchPush::pushable_post_types() === ['post']); $check('可推送分类法排除 post_format', SearchPush::pushable_taxonomies() === ['post_tag', 'category']); // 11) Edit panel rendering. $reset($defaults); SearchPush::save_push_record(31, [ 'normal_push' => true, 'normal_result' => ['success' => 1], 'daily_push' => false, 'daily_result' => null, 'update_time' => '2026-10-13 09:00:00', ]); SearchPush::save_push_record(31, [ 'normal_push' => true, 'normal_result' => ['success' => 1], 'daily_push' => false, 'daily_result' => null, 'update_time' => '2026-10-13 09:00:00', ], true); $box = SearchPushMetaBox::get_instance(); ob_start(); $box->render_post_panel($post(31)); $html = (string) ob_get_clean(); $check('面板含普通收录成功', str_contains($html, '普通收录:成功')); $check('面板含快速收录失败', str_contains($html, '快速收录:失败')); $check('面板含更新时间', str_contains($html, '2026-10-13 09:00:00')); $check('面板含重新提交勾选', str_contains($html, 'name="aksrt_push_resubmit"')); $check('面板含 nonce 字段', str_contains($html, 'name="aksrt_push_meta_nonce"')); $check('面板转义接口 JSON', str_contains($html, '{"success":1}')); ob_start(); $box->render_term_panel(null); $addHtml = (string) ob_get_clean(); $check('新增术语表单仅提示', str_contains($addHtml, '发布、更新后刷新页面后可查看提交结果') && !str_contains($addHtml, 'term_id = 31; ob_start(); $box->render_term_panel($term); $termHtml = (string) ob_get_clean(); $check('术语编辑表单为表单行', str_contains($termHtml, '')); $check('术语编辑表单含重新提交', str_contains($termHtml, 'name="aksrt_push_resubmit"')); if ($failures) { echo "Baidu push harness FAILED\n"; foreach ($failures as $failure) { echo ' - ' . $failure . "\n"; } exit(1); } echo "Baidu push harness passed\n"; }