Files
go_bt/tests/openai_auth_session_preservation.test.js
T

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;
});