'use strict'; const assert = require('assert'); const path = require('path'); const chatgptPath = require.resolve('../src/services/chatgpt_service'); const oauthPath = require.resolve('../src/services/openai_oauth_service'); const screenshotPath = require.resolve('../src/utils/screenshot'); const stepPath = require.resolve('../src/pipeline/steps/openai_get_auth'); const originalCache = new Map([ [chatgptPath, require.cache[chatgptPath]], [oauthPath, require.cache[oauthPath]], [screenshotPath, require.cache[screenshotPath]], [stepPath, require.cache[stepPath]], ]); function normalizeIdentityEmail(value) { const email = String(value || '').trim().toLowerCase(); const separator = email.lastIndexOf('@'); if (separator <= 0) return ''; let local = email.slice(0, separator); let domain = email.slice(separator + 1); if (domain === 'googlemail.com') domain = 'gmail.com'; if (domain === 'gmail.com') local = local.split('+', 1)[0].replace(/\./g, ''); return `${local}@${domain}`; } function installModule(filename, exports) { require.cache[filename] = { id: filename, filename, loaded: true, exports }; } function restoreCache() { for (const [filename, cached] of originalCache.entries()) { if (cached) require.cache[filename] = cached; else delete require.cache[filename]; } } function makeContext(email) { const calls = []; const storedAccount = { email, password: 'secret', password_changed: 1, fa_changed: 1, chatgpt_status: '已登录', chatgpt_session: JSON.stringify({ user: { email } }), }; return { calls, ctx: { account: storedAccount, page: { url: () => 'https://chatgpt.com/' }, params: { age: 23 }, results: {}, shouldStop: false, log: () => {}, updateMessage: async (...args) => { calls.push({ fn: 'updateMessage', args }); return true; }, updateStatus: async (...args) => { calls.push({ fn: 'updateStatus', args }); return true; }, dbWrite: async (fn, args) => { calls.push({ fn, args }); if (fn === 'getAccount') return storedAccount; return true; }, }, }; } (async () => { try { const accountEmail = 'AmistadiCaufield@gmail.com'; const session = { user: { email: 'a.mistadi.caufield+tag@googlemail.com' } }; const authJson = { code: 'code-1', access_token: 'access-1', refresh_token: 'refresh-1', id_token: 'id-1', }; installModule(chatgptPath, { loginOrRegisterChatGPT: async () => ({ ok: true, status: '已登录', session }), _private: { handleGoogleOAuth: async () => ({ ok: true }), waitForCloudflareChallenge: async () => ({ detected: false, passed: true }), }, }); installModule(oauthPath, { extractOpenAIAuthJson: async () => authJson, loginOpenAIOAuthWithGoogle: async () => ({ ok: true }), _private: { normalizeIdentityEmail }, }); installModule(screenshotPath, { saveErrorScreenshot: async () => null }); delete require.cache[stepPath]; let step = require(stepPath); const success = makeContext(accountEmail); await step(success.ctx); assert.strictEqual(success.ctx.shouldStop, false); assert.strictEqual(success.ctx.results.openaiGetAuth.success, true); assert(success.calls.some(call => call.fn === 'updateAccountOpenAIAuth')); assert(!success.calls.some(call => call.fn === 'updateAccountChatgptResult'), '不得写入 ChatGPT Session'); installModule(oauthPath, { extractOpenAIAuthJson: async () => { const error = new Error('temporary oauth failure'); error.code = 'OPENAI_TOKEN_EXCHANGE_FAILED'; throw error; }, loginOpenAIOAuthWithGoogle: async () => ({ ok: true }), _private: { normalizeIdentityEmail }, }); delete require.cache[stepPath]; step = require(stepPath); const failed = makeContext(accountEmail); await step(failed.ctx); assert.strictEqual(failed.ctx.shouldStop, true); assert.strictEqual(failed.ctx.results.openaiGetAuth.success, false); assert(failed.calls.some(call => ( call.fn === 'updateAccountOpenAIAuthStatus' && call.args[1] === '提取失败' ))); assert(!failed.calls.some(call => call.fn === 'updateAccountOpenAIAuth'), '失败不得清空旧授权 JSON'); assert(!failed.calls.some(call => call.fn === 'updateAccountChatgptResult'), '失败不得改写 ChatGPT Session'); installModule(oauthPath, { extractOpenAIAuthJson: async () => { const error = new Error('OpenAI账号已删除或停用'); error.code = 'CHATGPT_ACCOUNT_DEACTIVATED'; throw error; }, loginOpenAIOAuthWithGoogle: async () => ({ ok: true }), _private: { normalizeIdentityEmail }, }); delete require.cache[stepPath]; step = require(stepPath); const deactivated = makeContext(accountEmail); await step(deactivated.ctx); assert.strictEqual(deactivated.ctx.shouldStop, true); assert(!deactivated.calls.some(call => call.fn === 'updateAccountChatgptResult'), '账号停用只改处理结论,不得同步 ChatGPT 状态或 Session'); assert(!deactivated.calls.some(call => call.fn === 'updateStatus'), '账号停用只改处理结论,不得改账号总状态'); assert(deactivated.calls.some(call => ( call.fn === 'updateMessage' && /账号已删除\/停用/.test(call.args[1]) )), '处理结论应明确显示账号已删除/停用'); installModule(oauthPath, { extractOpenAIAuthJson: async () => { const error = new Error('OpenAI 要求补充手机号并完成验证码后才能继续提取授权 JSON'); error.code = 'OPENAI_PHONE_REQUIRED'; throw error; }, loginOpenAIOAuthWithGoogle: async () => ({ ok: true }), _private: { normalizeIdentityEmail }, }); delete require.cache[stepPath]; step = require(stepPath); const phoneRequired = makeContext(accountEmail); await step(phoneRequired.ctx); assert.strictEqual(phoneRequired.ctx.shouldStop, true); assert(phoneRequired.calls.some(call => ( call.fn === 'updateAccountOpenAIAuthStatus' && call.args[1] === '需要手机号' )), '手机号页面应写入独立授权状态'); assert(phoneRequired.calls.some(call => ( call.fn === 'updateMessage' && /需要手机号/.test(call.args[1]) )), '手机号页面的处理结论必须显示需要手机号'); assert(!phoneRequired.calls.some(call => call.fn === 'updateAccountChatgptResult'), '手机号页面不得修改 ChatGPT 状态或 Session'); assert(!phoneRequired.calls.some(call => call.fn === 'updateStatus'), '手机号页面不得修改账号总状态'); console.log(`${path.basename(__filename)}: PASS`); } finally { restoreCache(); } })().catch(error => { restoreCache(); console.error(error); process.exitCode = 1; });