184 lines
6.8 KiB
JavaScript
184 lines
6.8 KiB
JavaScript
'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;
|
|
});
|