'use strict'; const assert = require('assert'); const fs = require('fs'); const path = require('path'); const vm = require('vm'); const { RECIPES, ATOM_STEPS } = require('../src/pipeline/recipes'); const recipe = RECIPES.security_chatgpt; assert.ok(Array.isArray(recipe), '应注册独立的改密+2FA组合任务'); assert.strictEqual(recipe.length, 5); assert.strictEqual(recipe[0], ATOM_STEPS.login.step, '组合任务只能在开始时登录一次'); assert.strictEqual(recipe[1], ATOM_STEPS.detect_country.step); assert.strictEqual(recipe[2], ATOM_STEPS.change_language.step); assert.strictEqual(recipe[3], ATOM_STEPS.change_password.step, '应先改密码'); assert.strictEqual(recipe[4], ATOM_STEPS.change_2fa.step, '改密后应直接改2FA'); assert.ok(!recipe.includes(ATOM_STEPS.chatgpt_register.step), '组合任务不得再执行ChatGPT登录/注册'); assert.strictEqual( recipe.filter(step => step === ATOM_STEPS.login.step).length, 1, '组合任务中不得重复执行Google登录步骤', ); assert.deepStrictEqual( RECIPES.change_password, [ATOM_STEPS.login.step, ATOM_STEPS.detect_country.step, ATOM_STEPS.change_language.step, ATOM_STEPS.change_password.step], '单独改密码任务必须保持原流程不变', ); assert.deepStrictEqual( RECIPES.change_2fa, [ATOM_STEPS.login.step, ATOM_STEPS.detect_country.step, ATOM_STEPS.change_language.step, ATOM_STEPS.change_2fa.step], '单独更改2FA任务必须保持原流程不变', ); const root = path.join(__dirname, '..'); const html = fs.readFileSync(path.join(root, 'public', 'index.html'), 'utf8'); const app = fs.readFileSync(path.join(root, 'public', 'app.js'), 'utf8'); const runner = fs.readFileSync(path.join(root, 'src', 'workers', 'task_runner.js'), 'utf8'); assert.match(html, /@click="runSecurityChatgpt\(\)"[^>]*>改密\+2FA<\/button>/); assert.match(html, /x-model="securityRunMode"[^>]*value="overwrite"|value="overwrite"[^>]*x-model="securityRunMode"/); assert.match(html, /x-model="securityRunMode"[^>]*value="skip"|value="skip"[^>]*x-model="securityRunMode"/); assert.match(html, /覆盖修改/); assert.match(html, /跳过已完成/); assert.match(app, /skipCompletedSecuritySteps\s*=\s*this\.securityRunMode\s*===\s*'skip'/); assert.match(app, /runTask\('security_chatgpt',\s*\{\s*skipCompletedSecuritySteps\s*\}\)/); assert.match(runner, /taskType !== 'security_chatgpt'/, '组合任务不得因后续失败自动从头重跑'); assert.match( runner, /proxyRetryAttempt\+\+;\s*\/\/[^\n]*\n\s*chatgptBrowserAttempt = 1;/, '独立 ChatGPT 任务换代理后应重置当前代理的浏览器重开次数', ); const uiContext = vm.createContext({ console, Set, Map, Date, JSON, Math, String, Number, Object, Array, RegExp, Promise, Uint8Array, setTimeout, clearTimeout, }); vm.runInContext(`${app}\nthis.__geminiApp = geminiApp;`, uiContext, { filename: 'public/app.js' }); const ui = uiContext.__geminiApp(); let submitted = null; ui.runTask = (taskType, params) => { submitted = { taskType, params }; }; ui.isRunning = false; ui.runSecurityChatgpt(); assert.strictEqual(ui.showSecurityRunModal, true, '点击组合按钮应先打开执行方式弹框'); assert.strictEqual(ui.securityRunMode, 'overwrite', '弹框默认保持现有覆盖逻辑'); assert.strictEqual(submitted, null, '未确认前不得启动任务'); ui.securityRunMode = 'skip'; ui.confirmSecurityTask(); assert.strictEqual(ui.showSecurityRunModal, false); assert.strictEqual(submitted.taskType, 'security_chatgpt'); assert.strictEqual(submitted.params.skipCompletedSecuritySteps, true, '跳过模式必须传入显式布尔参数'); submitted = null; ui.runSecurityChatgpt(); ui.confirmSecurityTask(); assert.strictEqual(submitted.params.skipCompletedSecuritySteps, false, '覆盖模式必须关闭跳过判断'); submitted = null; ui.showSecurityRunModal = false; ui.isRunning = true; ui.runSecurityChatgpt(); assert.strictEqual(ui.showSecurityRunModal, false, '已有任务运行时不得再打开组合弹框'); assert.strictEqual(submitted, null); console.log('security_chatgpt_pipeline.test.js: PASS');