'use strict'; const assert = require('assert'); const fs = require('fs'); const path = require('path'); const vm = require('vm'); function jwt(payload) { const header = Buffer.from(JSON.stringify({ alg: 'none' })).toString('base64url'); const body = Buffer.from(JSON.stringify(payload)).toString('base64url'); return `${header}.${body}.signature`; } (async () => { const appSource = fs.readFileSync(path.join(__dirname, '..', 'public', 'app.js'), 'utf8'); const indexSource = fs.readFileSync(path.join(__dirname, '..', 'public', 'index.html'), 'utf8'); const objectUrls = new Map(); const downloads = []; let objectUrlIndex = 0; class TestBlob { constructor(parts, options = {}) { this.parts = parts; this.type = options.type || ''; } } const context = vm.createContext({ console, Set, Map, Date, JSON, Math, String, Number, Object, Array, RegExp, Promise, Uint8Array, TextDecoder, setTimeout, clearTimeout, atob: value => Buffer.from(value, 'base64').toString('binary'), Blob: TestBlob, URL: { createObjectURL(blob) { const url = `blob:test/${++objectUrlIndex}`; objectUrls.set(url, blob); return url; }, revokeObjectURL() {}, }, navigator: { clipboard: { async writeText() {} } }, document: { createElement(tag) { if (tag === 'a') { return { href: '', download: '', click() { downloads.push({ filename: this.download, blob: objectUrls.get(this.href) }); }, }; } return { value: '', select() {} }; }, body: { appendChild() {}, removeChild() {} }, execCommand() { return true; }, }, }); vm.runInContext(`${appSource}\nthis.__geminiApp = geminiApp;`, context, { filename: 'public/app.js' }); const email = 'user@example.com'; const accessToken = jwt({ client_id: 'client-1', exp: 1893456000, 'https://api.openai.com/profile': { email }, 'https://api.openai.com/auth': { chatgpt_account_id: 'account-1', chatgpt_user_id: 'chat-user-1', poid: 'organization-1', chatgpt_plan_type: 'plus', }, }); const idToken = jwt({ email, 'https://api.openai.com/auth': { chatgpt_account_id: 'account-1', chatgpt_user_id: 'chat-user-1', chatgpt_plan_type: 'plus', organizations: [{ id: 'organization-1', is_default: true }], }, }); const authJson = { access_token: accessToken, refresh_token: 'refresh-1', id_token: idToken, email, }; const app = context.__geminiApp(); assert.strictEqual(app.exportMode.sub2api_json, false, 'sub2api JSON导出默认应关闭'); assert.match( indexSource, /x-model="exportMode\.sub2api_json"[^>]*>\s*导出 sub2api JSON/, '导出弹窗应显示sub2api JSON勾选项', ); app.accounts = [{ email }]; app.selectedEmails = new Set([email]); app.exportMode.sub2api_json = true; app.exportSelectedAccounts(); assert.strictEqual(app.exportMode.sub2api_json, false, '重新打开导出弹窗应恢复sub2api默认状态'); const built = app.buildSub2ApiExport( [{ email, openai_auth_json: JSON.stringify(authJson) }], new Date('2026-08-21T15:33:41Z'), ); const payload = JSON.parse(JSON.stringify(built.data)); assert.strictEqual(built.skippedCount, 0); assert.deepStrictEqual(Object.keys(payload), ['exported_at', 'proxies', 'accounts']); assert.strictEqual(payload.exported_at, '2026-08-21T15:33:41Z'); assert.deepStrictEqual(payload.proxies, []); assert.strictEqual(payload.accounts.length, 1); const exportedAccount = payload.accounts[0]; assert.strictEqual(exportedAccount.name, email); assert.strictEqual(exportedAccount.platform, 'openai'); assert.strictEqual(exportedAccount.type, 'oauth'); assert.strictEqual(exportedAccount.concurrency, 100); assert.strictEqual(exportedAccount.priority, 1); assert.strictEqual(exportedAccount.rate_multiplier, 1); assert.strictEqual(exportedAccount.auto_pause_on_expired, true); assert.strictEqual(exportedAccount.credentials.access_token, accessToken); assert.strictEqual(exportedAccount.credentials.refresh_token, 'refresh-1'); assert.strictEqual(exportedAccount.credentials.id_token, idToken); assert.strictEqual(exportedAccount.credentials.client_id, 'client-1'); assert.strictEqual(exportedAccount.credentials.email, email); assert.strictEqual(exportedAccount.credentials.expires_at, 1893456000); assert.strictEqual(exportedAccount.credentials.chatgpt_account_id, 'account-1'); assert.strictEqual(exportedAccount.credentials.chatgpt_user_id, 'chat-user-1'); assert.strictEqual(exportedAccount.credentials.organization_id, 'organization-1'); assert.strictEqual(exportedAccount.credentials.plan_type, 'plus'); assert.deepStrictEqual(exportedAccount.extra, { email }); assert.strictEqual('subscription_expires_at' in exportedAccount.credentials, false, '不得伪造订阅到期时间'); assert.strictEqual('codex_usage_updated_at' in exportedAccount.extra, false, '不得伪造用量统计'); const mixed = app.buildSub2ApiExport([ { email: 'broken-json@example.com', openai_auth_json: '{invalid' }, { email: 'missing-refresh@example.com', openai_auth_json: JSON.stringify({ access_token: accessToken }) }, { email, openai_auth_json: authJson }, ]); assert.strictEqual(mixed.skippedCount, 2, '损坏或缺少关键令牌的账号必须跳过'); assert.strictEqual(mixed.data.accounts.length, 1); const numericExpiry = app.buildSub2ApiExport([{ email, openai_auth_json: { access_token: 'opaque-access-token', refresh_token: 'opaque-refresh-token', expires_at: 1893456000000, }, }]); assert.strictEqual(numericExpiry.data.accounts[0].credentials.expires_at, 1893456000); assert.strictEqual( 'client_id' in numericExpiry.data.accounts[0].credentials, false, '授权数据没有client_id时不得写入固定值', ); Object.keys(app.exportFields).forEach(key => { app.exportFields[key] = false; }); app._exportList = [{ email }]; app.exportMode = { file: false, csv: false, sub2api_json: true, clipboard: false, staging: false, }; app.showExportModal = true; const logs = []; app.addLog = (message, type) => logs.push({ message, type }); let authLoadCount = 0; app.loadOpenAIAuthForExport = async accounts => { authLoadCount++; return { accounts: accounts.map(account => ({ ...account, openai_auth_json: JSON.stringify(authJson) })), missingCount: 0, }; }; await app.confirmExport(); assert.strictEqual(authLoadCount, 1, '只选sub2api时应读取一次授权JSON'); assert.strictEqual(downloads.length, 1, '只选sub2api且未选字段时也应下载'); assert.match(downloads[0].filename, /^sub2api-account-\d{14}\.json$/); assert.strictEqual(downloads[0].blob.type, 'application/json;charset=utf-8'); const downloaded = JSON.parse(downloads[0].blob.parts.join('')); assert.strictEqual(downloaded.accounts.length, 1); assert.strictEqual(downloaded.accounts[0].credentials.email, email); assert.strictEqual(app.showExportModal, false); assert.ok(logs.some(item => item.type === 'success' && /sub2api JSON/.test(item.message))); downloads.length = 0; authLoadCount = 0; logs.length = 0; app.exportFields.email = true; app.showExportModal = true; app.exportMode = { file: false, csv: true, sub2api_json: true, clipboard: false, staging: false, }; await app.confirmExport(); assert.strictEqual(authLoadCount, 1, 'CSV与sub2api同时导出时授权数据只读取一次'); assert.deepStrictEqual( downloads.map(item => path.extname(item.filename)).sort(), ['.csv', '.json'], ); downloads.length = 0; logs.length = 0; app.showExportModal = true; Object.keys(app.exportFields).forEach(key => { app.exportFields[key] = false; }); app.exportMode = { file: false, csv: false, sub2api_json: true, clipboard: false, staging: false, }; app.loadOpenAIAuthForExport = async accounts => ({ accounts: accounts.map(account => ({ ...account, openai_auth_json: '' })), missingCount: accounts.length, }); await app.confirmExport(); assert.strictEqual(downloads.length, 0, '没有有效授权账号时不得生成空sub2api文件'); assert.strictEqual(app.showExportModal, true, '未生成文件时应保留弹窗供用户调整'); assert.ok(logs.some(item => item.type === 'error' && /未生成 sub2api 文件/.test(item.message))); console.log('account_sub2api_export.test.js: PASS'); })().catch(error => { console.error(error); process.exitCode = 1; });