'use strict'; /** * 消息管理后台(users.php → aksrt_messages)契约: * 1) 页面由独立 trait 承载,UsersMenus 只做组合与路由注册; * 2) 写操作走 admin_post + nonce + 能力校验; * 3) SQL 全部在 NotificationAdminQuery 内且经 prepare; * 4) 分页用共享 AdminListPagination; * 5) 旧实现与死代码已删除。 */ const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const root = path.resolve(__dirname, '../..'); const read = (file) => fs.readFileSync(path.join(root, file), 'utf8'); test('messages page is composed from its own trait', () => { const menus = read('src/Admin/Native/UsersMenus.php'); assert.ok(menus.includes('use UsersMenusMessages;')); assert.ok(menus.includes("'aksrt_messages'")); assert.ok(menus.includes("add_action('admin_post_aksrt_users_send_message', [$this, 'handle_send_message'])")); assert.ok(menus.includes("add_action('admin_post_aksrt_users_bulk_messages', [$this, 'handle_bulk_messages'])")); assert.ok(menus.includes("add_action('admin_enqueue_scripts', [$this, 'enqueue_message_assets'])")); // 旧实现不得残留在壳类中。 assert.doesNotMatch(menus, /function render_messages_page|function handle_send_message|message_sent|message_failed/); assert.doesNotMatch(menus, /aksrt_notification/); }); test('write handlers check capability and nonce', () => { const page = read('src/Admin/Native/UsersMenusMessages.php'); assert.match(page, /function handle_send_message\(\): void \{\s*\$this->ensure_cap\(\);\s*check_admin_referer\('aksrt_users_send_message'\);/); assert.match(page, /function handle_bulk_messages\(\): void \{\s*\$this->ensure_cap\(\);\s*check_admin_referer\('aksrt_users_bulk_messages'\);/); assert.ok(page.includes("wp_nonce_field('aksrt_users_send_message')")); assert.ok(page.includes("wp_nonce_field('aksrt_users_bulk_messages')")); // 页面层不直接碰数据库,分页走共享组件。 assert.doesNotMatch(page, /\$wpdb/); assert.ok(page.includes('AdminListPagination::render(')); }); test('admin query keeps SQL prepared and bulk size capped', () => { const query = read('src/Messages/NotificationAdminQuery.php'); assert.match(query, /DELETE FROM \{\$table\} WHERE id IN \(\{\$in\}\)", \$ids\)/); assert.ok(query.includes("array_fill(0, count($ids), '%d')")); assert.ok(query.includes('array_slice($ids, 0, self::MAX_BULK)')); assert.ok(query.includes('$wpdb->esc_like($s)')); }); test('dead notification helpers are removed', () => { assert.doesNotMatch(read('inc/modules/user-notifications.php'), /aksrt_notification_type_label/); assert.doesNotMatch(read('src/Messages/Message.php'), /get_all_notifications/); assert.ok(fs.existsSync(path.join(root, 'assets/css/admin/messages.css'))); });