const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); function loadCpaApi(fetchImpl) { const source = fs.readFileSync('background/cpa-api.js', 'utf8'); const scope = {}; new Function('self', 'fetch', `${source}; return self.MultiPageBackgroundCpaApi;`)(scope, fetchImpl); return scope.MultiPageBackgroundCpaApi; } function base64UrlJson(value) { return Buffer.from(JSON.stringify(value), 'utf8') .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/g, ''); } function jwt(payload) { return `${base64UrlJson({ alg: 'RS256', typ: 'JWT' })}.${base64UrlJson(payload)}.signature`; } test('buildCpaSessionAuthJson converts ChatGPT session to codex auth JSON', () => { const api = loadCpaApi(async () => { throw new Error('fetch should not be called'); }).createCpaApi(); const accessToken = jwt({ exp: 1893456000, 'https://api.openai.com/auth': { chatgpt_account_id: 'acct_123', chatgpt_user_id: 'user_123', chatgpt_plan_type: 'plus', }, 'https://api.openai.com/profile': { email: 'profile@example.com', }, }); const result = api.buildCpaSessionAuthJson({ accessToken, session: { user: { email: 'session@example.com' }, refresh_token: 'refresh-token', session_token: 'session-token', }, }, { now: new Date('2026-05-21T00:00:00.000Z'), }); assert.equal(result.fileName, 'codex-session@example.com-plus.json'); assert.equal(result.email, 'session@example.com'); assert.equal(result.accountId, 'acct_123'); assert.equal(result.hasRefreshToken, true); assert.deepEqual(result.authJson, { type: 'codex', account_id: 'acct_123', chatgpt_account_id: 'acct_123', email: 'session@example.com', name: 'session@example.com', plan_type: 'plus', chatgpt_plan_type: 'plus', id_token: result.authJson.id_token, id_token_synthetic: true, access_token: accessToken, refresh_token: 'refresh-token', session_token: 'session-token', last_refresh: '2026-05-21T00:00:00.000Z', expired: '2030-01-01T00:00:00.000Z', }); assert.match(result.authJson.id_token, /\.synthetic$/); }); test('importCurrentChatGptSession posts auth JSON to CPA management endpoint', async () => { const calls = []; const fetchImpl = async (url, options = {}) => { calls.push({ url, options }); return { ok: true, status: 200, json: async () => ({ ok: true }), }; }; const logs = []; const api = loadCpaApi(fetchImpl).createCpaApi({ addLog: async (message, level) => logs.push({ message, level }), fetchImpl, }); const accessToken = jwt({ exp: 1893456000, 'https://api.openai.com/auth': { chatgpt_account_id: 'acct_abc', chatgpt_plan_type: 'plus', }, 'https://api.openai.com/profile': { email: 'sync@example.com', }, }); const result = await api.importCurrentChatGptSession({ vpsUrl: 'http://127.0.0.1:8317/management.html#/oauth', vpsPassword: 'management-secret', session: { accessToken, user: { email: 'sync@example.com' }, }, }, { logLabel: '步骤 10', now: new Date('2026-05-21T00:00:00.000Z'), }); assert.equal(result.cpaImportedFileName, 'codex-sync@example.com-plus.json'); assert.equal(result.cpaImportedEmail, 'sync@example.com'); assert.equal(calls.length, 1); assert.equal( calls[0].url, 'http://127.0.0.1:8317/v0/management/auth-files?name=codex-sync%40example.com-plus.json' ); assert.equal(calls[0].options.method, 'POST'); assert.equal(calls[0].options.headers.Authorization, 'Bearer management-secret'); assert.equal(calls[0].options.headers['X-Management-Key'], 'management-secret'); const body = JSON.parse(calls[0].options.body); assert.equal(body.type, 'codex'); assert.equal(body.email, 'sync@example.com'); assert.equal(body.access_token, accessToken); assert.equal(body.chatgpt_account_id, 'acct_abc'); assert.equal(logs.some((entry) => /CPA 会话导入完成/.test(entry.message) && entry.level === 'ok'), true); });