'use strict'; const assert = require('node:assert/strict'); const { execFileSync } = require('node:child_process'); 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('account dialogs expose configured email steps, phone steps, and reference password branches', () => { const template = read('inc/helpers/user-center-renderers/settings-account.php'); const javascript = read('assets/js/routes/user-center.js'); const css = read('assets/css/routes/user-center/settings.css'); assert.match(template, /\$email_verify_required = \$account_email !== '' && !empty\(\$user_settings\['email_set_captch'\]\)/); assert.match(template, /data-account-step="email-old"[\s\S]*?data-account-step="email-new"[\s\S]*?data-account-step="email-success"/); assert.match(template, /if \(\$email_verify_required\)/); assert.match(template, /if \(\$email_verify_required\)[\s\S]*?else : \?>[\s\S]*?aksrt-settings-modal__pane/); assert.match(template, /if \(!empty\(\$user_settings\['email_set_captch'\]\)\)/); assert.match(template, /data-account-step="phone-old"[\s\S]*?data-account-step="phone-new"[\s\S]*?data-account-step="phone-success"/); assert.match(template, /if \(\$account_phone !== ''\)[\s\S]*?else : \?>[\s\S]*?aksrt-settings-modal__pane/); assert.match(template, /data-aksrt-sms-scene="verify_old_phone"/); assert.match(template, /data-aksrt-sms-scene="bind_new_phone"/); assert.match(template, /if \(!\$account_oauth_new\)[\s\S]*?name="passwordold"/); assert.match(template, /if \(!\$account_oauth_new && \$can_recover_password\)/); assert.match(template, /data-account-password-pane="recovery"/); assert.match(javascript, /\/auth\/code/); assert.match(javascript, /\/auth\/reset-password/); assert.doesNotMatch(template + javascript + css, /pwd-strength|pwd-send-code|email-send-code/); const emailConditionStart = template.indexOf(''); const emailElse = template.indexOf('', emailConditionStart); const emailEnd = template.indexOf('', emailElse); const emailSteps = template.slice(emailConditionStart, emailElse); const emailDirect = template.slice(emailElse, emailEnd); const phoneConditionStart = template.indexOf(''); const phoneElse = template.indexOf('', phoneConditionStart); const phoneEnd = template.indexOf('', phoneElse); const phoneSteps = template.slice(phoneConditionStart, phoneElse); const phoneDirect = template.slice(phoneElse, phoneEnd); assert.match(emailSteps, /account-step-list/); assert.doesNotMatch(emailDirect, /account-step-list|data-account-step=/); assert.match(phoneSteps, /account-step-list/); assert.doesNotMatch(phoneDirect, /account-step-list|data-account-step=/); }); test('account PHP harness exercises email and password state transitions', () => { const harness = path.join(root, 'tests', 'widget-system', 'account-settings-harness.php'); const output = execFileSync('php', [harness], { encoding: 'utf8' }); assert.match(output, /Account settings harness passed/); }); test('email update keeps both proofs through validation failures and consumes them only after the write', () => { const ajax = read('inc/modules/user-parts/account-ajax.php'); const send = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_send_account_email_code'"), ajax.indexOf("add_action('wp_ajax_aksrt_verify_account_email'")); const verify = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_verify_account_email'"), ajax.indexOf("add_action('wp_ajax_aksrt_change_password'")); const change = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_change_email'")); assert.match(send, /\['verify_email', 'change_email'\]/); assert.match(send, /email_exists\(\$target\)/); assert.match(verify, /aksrt_require_captcha\(\s*'change_email'/); assert.match(verify, /aksrt_account_set_proof\(\$user_id, 'email_old'/); assert.match(change, /aksrt_account_email_verification_required\(\)/); assert.match(change, /aksrt_account_consume_proof\([^;]+false\)/); assert.match(change, /aksrt_account_consume_code\([\s\S]*?false\s*\)/); assert.ok(change.indexOf('wp_update_user(') < change.indexOf("delete_transient(aksrt_account_proof_key")); assert.ok(change.indexOf('wp_update_user(') < change.indexOf("delete_transient(aksrt_account_code_key")); assert.match(change, /\$agreement\['required'\]/); assert.doesNotMatch(ajax, /wp_ajax_aksrt_send_email_code/); }); test('phone replacement requires old-number proof and a new-number code while reusing bind side effects', () => { const controller = read('src/Admin/UserControllerAuthPhoneOperations.php'); const routes = read('src/Admin/UserController.php'); const binding = read('src/Auth/ServicePart1b.php'); const code = controller.slice(controller.indexOf('public function user_phone_code('), controller.indexOf('public function user_verify_phone(')); const verify = controller.slice(controller.indexOf('public function user_verify_phone('), controller.indexOf('public function user_bind_phone(')); const bind = controller.slice(controller.indexOf('public function user_bind_phone('), controller.indexOf('public function test_sms_send(')); assert.match(routes, /\/user\/phone-verify[\s\S]*?user_verify_phone/); assert.match(code, /verify_old_phone/); assert.match(code, /bind_new_phone/); assert.match(verify, /auth_consume_code\('account_verify_old_phone_' \./); assert.match(verify, /aksrt_account_phone_old_/); assert.match(bind, /aksrt_account_phone_old_/); assert.match(bind, /auth_consume_code\(\$code_scene, \$phone, \$code, false\)/); assert.ok(bind.indexOf("Auth\\Service::bind_phone") < bind.indexOf("delete_transient($this->auth_code_key($code_scene, $phone))")); assert.match(bind, /aksrt_get_auth_agreement_view/); assert.match(bind, /Auth\\Service::bind_phone\(\$payload, true\)/); assert.match(binding, /aksrt_user_phone_changed/); assert.doesNotMatch(controller, /auth_consume_code\('bind_phone'/); }); test('password change retains old-password limits and captcha; recovery uses shared auth endpoints', () => { const ajax = read('inc/modules/user-parts/account-ajax.php'); const javascript = read('assets/js/routes/user-center.js'); const password = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_change_password'"), ajax.indexOf("add_action('wp_ajax_aksrt_change_email'")); assert.match(password, /get_user_meta\(\$user_id, 'oauth_new', true\)/); assert.match(password, /wp_check_password\(\$old/); assert.match(password, /aksrt_account_rate_limited\(\$user_id, 'change_password'\)/); assert.match(password, /aksrt_require_captcha\('change_password'/); assert.match(password, /Auth\\Service::validate_password\(\$password, \$password2\)/); assert.doesNotMatch(password, /email.*code|password.*strength/i); assert.match(javascript, /scene: 'resetpassword', target: target, channel:/); assert.match(javascript, /\/auth\/code/); assert.match(javascript, /\/auth\/reset-password/); assert.match(javascript, /data-recovery-channel/); });