| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- const test = require('node:test');
- const assert = require('node:assert/strict');
- const fs = require('node:fs');
- const source = fs.readFileSync('background.js', 'utf8');
- function extractFunction(name) {
- const markers = [`async function ${name}(`, `function ${name}(`];
- const start = markers
- .map((marker) => source.indexOf(marker))
- .find((index) => index >= 0);
- if (start < 0) {
- throw new Error(`missing function ${name}`);
- }
- let parenDepth = 0;
- let signatureEnded = false;
- let braceStart = -1;
- for (let i = start; i < source.length; i += 1) {
- const ch = source[i];
- if (ch === '(') {
- parenDepth += 1;
- } else if (ch === ')') {
- parenDepth -= 1;
- if (parenDepth === 0) {
- signatureEnded = true;
- }
- } else if (ch === '{' && signatureEnded) {
- braceStart = i;
- break;
- }
- }
- if (braceStart < 0) {
- throw new Error(`missing body for function ${name}`);
- }
- let depth = 0;
- let end = braceStart;
- for (; end < source.length; end += 1) {
- const ch = source[end];
- if (ch === '{') depth += 1;
- if (ch === '}') {
- depth -= 1;
- if (depth === 0) {
- end += 1;
- break;
- }
- }
- }
- return source.slice(start, end);
- }
- test('ensureLuckmailPurchaseForFlow buys openai mailbox and defaults email type to ms_graph', async () => {
- const bundle = [
- extractFunction('getLuckmailSessionConfig'),
- extractFunction('getCurrentLuckmailPurchase'),
- extractFunction('ensureLuckmailPurchaseForFlow'),
- ].join('\n');
- const factory = new Function('initialState', `
- let currentState = { ...initialState };
- const DEFAULT_LUCKMAIL_PROJECT_CODE = 'openai';
- const purchaseCalls = [];
- const activateCalls = [];
- function normalizeLuckmailBaseUrl(value) {
- return String(value || '').trim() || 'https://mails.luckyous.com';
- }
- function normalizeLuckmailEmailType(value) {
- return ['self_built', 'ms_imap', 'ms_graph', 'google_variant'].includes(String(value || '').trim())
- ? String(value || '').trim()
- : 'ms_graph';
- }
- function normalizeLuckmailPurchase(value) {
- return value;
- }
- function normalizeLuckmailPurchases(value) {
- return value.purchases || [];
- }
- async function getState() {
- return currentState;
- }
- function createLuckmailClient() {
- return {
- user: {
- async purchaseEmails(projectCode, quantity, options) {
- purchaseCalls.push({ projectCode, quantity, options });
- return {
- purchases: [{ id: 15, email_address: 'demo@outlook.com', token: 'tok-1' }],
- };
- },
- },
- };
- }
- async function findReusableLuckmailPurchaseForFlow() {
- return null;
- }
- async function activateLuckmailPurchaseForFlow(state, client, purchase, options) {
- activateCalls.push({ state, purchase, options });
- currentState.currentLuckmailPurchase = purchase;
- currentState.email = purchase.email_address;
- return purchase;
- }
- ${bundle}
- return {
- ensureLuckmailPurchaseForFlow,
- snapshot() {
- return { currentState, purchaseCalls, activateCalls };
- },
- };
- `);
- const api = factory({
- luckmailApiKey: 'sk-test',
- luckmailBaseUrl: '',
- luckmailEmailType: '',
- luckmailDomain: '',
- currentLuckmailPurchase: null,
- email: null,
- });
- const purchase = await api.ensureLuckmailPurchaseForFlow();
- const snapshot = api.snapshot();
- assert.equal(purchase.email_address, 'demo@outlook.com');
- assert.deepStrictEqual(snapshot.purchaseCalls, [{
- projectCode: 'openai',
- quantity: 1,
- options: {
- emailType: 'ms_graph',
- domain: undefined,
- },
- }]);
- assert.equal(snapshot.activateCalls[0].options.initializeCursor, false);
- assert.equal(snapshot.currentState.email, 'demo@outlook.com');
- });
- test('ensureLuckmailPurchaseForFlow reuses reusable openai mailbox before buying a new one', async () => {
- const bundle = [
- extractFunction('getLuckmailSessionConfig'),
- extractFunction('getCurrentLuckmailPurchase'),
- extractFunction('ensureLuckmailPurchaseForFlow'),
- ].join('\n');
- const factory = new Function('initialState', `
- let currentState = { ...initialState };
- const DEFAULT_LUCKMAIL_PROJECT_CODE = 'openai';
- const purchaseCalls = [];
- const activateCalls = [];
- function normalizeLuckmailBaseUrl(value) {
- return String(value || '').trim() || 'https://mails.luckyous.com';
- }
- function normalizeLuckmailEmailType(value) {
- return ['self_built', 'ms_imap', 'ms_graph', 'google_variant'].includes(String(value || '').trim())
- ? String(value || '').trim()
- : 'ms_graph';
- }
- function normalizeLuckmailPurchase(value) {
- return value;
- }
- function normalizeLuckmailPurchases(value) {
- return value.purchases || [];
- }
- async function getState() {
- return currentState;
- }
- function createLuckmailClient() {
- return {
- user: {
- async purchaseEmails(projectCode, quantity, options) {
- purchaseCalls.push({ projectCode, quantity, options });
- return { purchases: [] };
- },
- },
- };
- }
- async function findReusableLuckmailPurchaseForFlow() {
- return {
- id: 99,
- email_address: 'reuse@outlook.com',
- token: 'tok-reuse',
- };
- }
- async function activateLuckmailPurchaseForFlow(state, client, purchase, options) {
- activateCalls.push({ state, purchase, options });
- currentState.currentLuckmailPurchase = purchase;
- currentState.email = purchase.email_address;
- return purchase;
- }
- ${bundle}
- return {
- ensureLuckmailPurchaseForFlow,
- snapshot() {
- return { currentState, purchaseCalls, activateCalls };
- },
- };
- `);
- const api = factory({
- luckmailApiKey: 'sk-test',
- luckmailBaseUrl: 'https://mails.luckyous.com',
- luckmailEmailType: 'ms_imap',
- luckmailDomain: 'outlook.com',
- currentLuckmailPurchase: null,
- email: null,
- });
- const purchase = await api.ensureLuckmailPurchaseForFlow();
- const snapshot = api.snapshot();
- assert.equal(purchase.id, 99);
- assert.deepStrictEqual(snapshot.purchaseCalls, []);
- assert.equal(snapshot.activateCalls[0].options.initializeCursor, true);
- assert.match(snapshot.activateCalls[0].options.logMessage, /已复用 openai 邮箱/);
- });
- test('activateLuckmailPurchaseForFlow builds baseline cursor from existing mails when reusing mailbox', async () => {
- const bundle = extractFunction('activateLuckmailPurchaseForFlow');
- const factory = new Function(`
- let currentPurchase = null;
- let currentCursor = null;
- let currentEmail = null;
- const buildCalls = [];
- function normalizeLuckmailPurchase(value) {
- return value;
- }
- async function setLuckmailPurchaseState(value) {
- currentPurchase = value;
- }
- async function setLuckmailMailCursorState(value) {
- currentCursor = value;
- }
- async function setEmailState(value) {
- currentEmail = value;
- }
- async function addLog() {}
- function buildLuckmailBaselineCursor(mails) {
- buildCalls.push(mails);
- return { messageId: 'mail-new', receivedAt: '2026-04-14 13:32:05' };
- }
- ${bundle}
- return {
- activateLuckmailPurchaseForFlow,
- snapshot() {
- return { currentPurchase, currentCursor, currentEmail, buildCalls };
- },
- };
- `);
- const api = factory();
- const client = {
- user: {
- async getTokenMails() {
- return {
- mails: [
- { message_id: 'mail-old', received_at: '2026-04-14 13:31:15' },
- { message_id: 'mail-new', received_at: '2026-04-14 13:32:05' },
- ],
- };
- },
- },
- };
- await api.activateLuckmailPurchaseForFlow({}, client, {
- id: 5,
- email_address: 'reuse@outlook.com',
- token: 'tok-reuse',
- }, {
- initializeCursor: true,
- logMessage: 'reuse',
- });
- const snapshot = api.snapshot();
- assert.equal(snapshot.currentPurchase.id, 5);
- assert.deepStrictEqual(snapshot.currentCursor, {
- messageId: 'mail-new',
- receivedAt: '2026-04-14 13:32:05',
- });
- assert.equal(snapshot.currentEmail, 'reuse@outlook.com');
- assert.equal(snapshot.buildCalls.length, 1);
- });
- test('listLuckmailPurchasesByProject only keeps openai purchases', async () => {
- const bundle = extractFunction('listLuckmailPurchasesByProject');
- const factory = new Function(`
- const DEFAULT_LUCKMAIL_PROJECT_CODE = 'openai';
- function normalizeLuckmailProjectName(value) {
- return String(value || '').trim().toLowerCase();
- }
- function isLuckmailPurchaseForProject(purchase, projectCode) {
- return normalizeLuckmailProjectName(purchase.project_name || purchase.project) === normalizeLuckmailProjectName(projectCode);
- }
- async function getAllLuckmailPurchases() {
- return [
- { id: 1, project_name: 'OpenAi' },
- { id: 2, project_name: 'other' },
- { id: 3, project: 'openai' },
- ];
- }
- ${bundle}
- return { listLuckmailPurchasesByProject };
- `);
- const api = factory();
- const result = await api.listLuckmailPurchasesByProject({}, { projectCode: 'openai' });
- assert.deepStrictEqual(result.map((item) => item.id), [1, 3]);
- });
- test('disableUsedLuckmailPurchases only disables locally used and non-preserved openai mailboxes', async () => {
- const bundle = extractFunction('disableUsedLuckmailPurchases');
- const factory = new Function(`
- let clearedOptions = null;
- const disabledCalls = [];
- const DEFAULT_LUCKMAIL_PROJECT_CODE = 'openai';
- function normalizeLuckmailPurchaseId(value) {
- const numeric = Number(value);
- return Number.isFinite(numeric) && numeric > 0 ? String(Math.floor(numeric)) : '';
- }
- async function ensureManualInteractionAllowed() {
- return {
- luckmailUsedPurchases: { 1: true, 2: true, 3: true },
- luckmailPreserveTagId: 9,
- luckmailPreserveTagName: '保留',
- mailProvider: 'luckmail-api',
- };
- }
- function getLuckmailUsedPurchases(state) {
- return state.luckmailUsedPurchases;
- }
- function getLuckmailPreserveTagInfo(state) {
- return {
- id: state.luckmailPreserveTagId,
- name: state.luckmailPreserveTagName,
- };
- }
- function isLuckmailPurchasePreserved(purchase, options) {
- return purchase.tag_id === options.preserveTagId || purchase.tag_name === options.preserveTagName;
- }
- function createLuckmailClient() {
- return {
- user: {
- async batchSetPurchaseDisabled(ids, disabled) {
- disabledCalls.push({ ids, disabled });
- },
- },
- };
- }
- async function listLuckmailPurchasesByProject() {
- return [
- { id: 1, email_address: 'used-1@outlook.com', user_disabled: 0, tag_id: 0, tag_name: '' },
- { id: 2, email_address: 'preserved@outlook.com', user_disabled: 0, tag_id: 9, tag_name: '保留' },
- { id: 3, email_address: 'already-disabled@outlook.com', user_disabled: 1, tag_id: 0, tag_name: '' },
- { id: 4, email_address: 'unused@outlook.com', user_disabled: 0, tag_id: 0, tag_name: '' },
- ];
- }
- async function getState() {
- return {
- currentLuckmailPurchase: { id: 1 },
- mailProvider: 'luckmail-api',
- };
- }
- function getCurrentLuckmailPurchase(state) {
- return state.currentLuckmailPurchase;
- }
- function isLuckmailProvider(state) {
- return state.mailProvider === 'luckmail-api';
- }
- async function clearLuckmailRuntimeState(options) {
- clearedOptions = options;
- }
- async function addLog() {}
- ${bundle}
- return {
- disableUsedLuckmailPurchases,
- snapshot() {
- return { disabledCalls, clearedOptions };
- },
- };
- `);
- const api = factory();
- const result = await api.disableUsedLuckmailPurchases();
- const snapshot = api.snapshot();
- assert.deepStrictEqual(result.disabledIds, [1]);
- assert.deepStrictEqual(snapshot.disabledCalls, [{ ids: [1], disabled: 1 }]);
- assert.deepStrictEqual(snapshot.clearedOptions, { clearEmail: true });
- });
- test('resetState preserves LuckMail session config, used map, and preserve tag cache while clearing runtime purchase state', async () => {
- const bundle = extractFunction('resetState');
- const factory = new Function([
- 'let cleared = false;',
- 'let storedPayload = null;',
- "const LOG_PREFIX = '[test]';",
- "const DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME = '保留';",
- 'const DEFAULT_STATE = {',
- " luckmailApiKey: '',",
- " luckmailBaseUrl: 'https://mails.luckyous.com',",
- " luckmailEmailType: 'ms_graph',",
- " luckmailDomain: '',",
- ' luckmailUsedPurchases: {},',
- ' luckmailPreserveTagId: 0,',
- " luckmailPreserveTagName: '保留',",
- " currentLuckmailPurchase: { token: 'stale' },",
- " currentLuckmailMailCursor: { messageId: 'stale' },",
- ' email: null,',
- '};',
- 'function normalizeLuckmailBaseUrl(value) {',
- " const normalized = String(value || '').trim() || 'https://mails.luckyous.com';",
- " return normalized.replace(/\\/$/, '');",
- '}',
- 'function normalizeLuckmailEmailType(value) {',
- " return ['self_built', 'ms_imap', 'ms_graph', 'google_variant'].includes(String(value || '').trim())",
- " ? String(value || '').trim()",
- " : 'ms_graph';",
- '}',
- 'function normalizeLuckmailUsedPurchases(value) {',
- ' return value || {};',
- '}',
- 'async function getPersistedSettings() {',
- " return { mailProvider: '163' };",
- '}',
- 'const chrome = {',
- ' storage: {',
- ' session: {',
- ' async get() {',
- ' return {',
- " seenCodes: ['seen-1'],",
- " seenInbucketMailIds: ['mail-1'],",
- " accounts: [{ email: 'saved@example.com' }],",
- " tabRegistry: { foo: { tabId: 1 } },",
- " sourceLastUrls: { foo: 'https://example.com' },",
- " luckmailApiKey: 'sk-session',",
- " luckmailBaseUrl: 'https://demo.example.com/',",
- " luckmailEmailType: 'ms_imap',",
- " luckmailDomain: 'outlook.com',",
- " luckmailUsedPurchases: { 88: true },",
- ' luckmailPreserveTagId: 9,',
- " luckmailPreserveTagName: '保留',",
- ' };',
- ' },',
- ' async clear() {',
- ' cleared = true;',
- ' },',
- ' async set(payload) {',
- ' storedPayload = payload;',
- ' },',
- ' },',
- ' },',
- '};',
- bundle,
- 'return {',
- ' resetState,',
- ' snapshot() {',
- ' return { cleared, storedPayload };',
- ' },',
- '};',
- ].join('\n'));
- const api = factory();
- await api.resetState();
- const snapshot = api.snapshot();
- assert.equal(snapshot.cleared, true);
- assert.equal(snapshot.storedPayload.luckmailApiKey, 'sk-session');
- assert.equal(snapshot.storedPayload.luckmailBaseUrl, 'https://demo.example.com');
- assert.equal(snapshot.storedPayload.luckmailEmailType, 'ms_imap');
- assert.equal(snapshot.storedPayload.luckmailDomain, 'outlook.com');
- assert.deepStrictEqual(snapshot.storedPayload.luckmailUsedPurchases, { 88: true });
- assert.equal(snapshot.storedPayload.luckmailPreserveTagId, 9);
- assert.equal(snapshot.storedPayload.luckmailPreserveTagName, '保留');
- assert.equal(snapshot.storedPayload.currentLuckmailPurchase, null);
- assert.equal(snapshot.storedPayload.currentLuckmailMailCursor, null);
- });
- test('handleStepData step 9 marks current LuckMail purchase as used and clears runtime state', async () => {
- const bundle = extractFunction('handleStepData');
- const factory = new Function(`
- let clearedOptions = null;
- let usedMarker = null;
- const logs = [];
- async function closeLocalhostCallbackTabs() {}
- async function getState() {
- return {
- mailProvider: 'luckmail-api',
- currentHotmailAccountId: null,
- currentLuckmailPurchase: {
- id: 123,
- email_address: 'demo@outlook.com',
- },
- email: 'demo@outlook.com',
- };
- }
- function getCurrentLuckmailPurchase(state) {
- return state.currentLuckmailPurchase;
- }
- function isHotmailProvider() {
- return false;
- }
- async function patchHotmailAccount() {}
- function isLuckmailProvider(state) {
- return state.mailProvider === 'luckmail-api';
- }
- async function setLuckmailPurchaseUsedState(purchaseId, used) {
- usedMarker = { purchaseId, used };
- }
- async function clearLuckmailRuntimeState(options) {
- clearedOptions = options;
- }
- async function addLog(message, level) {
- logs.push({ message, level });
- }
- function buildLocalhostCleanupPrefix() {
- return '';
- }
- async function closeTabsByUrlPrefix() {}
- function shouldUseCustomRegistrationEmail() {
- return false;
- }
- async function setEmailStateSilently() {}
- async function setState() {}
- function broadcastDataUpdate() {}
- function isLocalhostOAuthCallbackUrl() {
- return true;
- }
- ${bundle}
- return {
- handleStepData,
- snapshot() {
- return { clearedOptions, usedMarker, logs };
- },
- };
- `);
- const api = factory();
- await api.handleStepData(9, {
- localhostUrl: 'http://localhost:1455/auth/callback?code=abc&state=xyz',
- });
- const snapshot = api.snapshot();
- assert.deepStrictEqual(snapshot.usedMarker, { purchaseId: 123, used: true });
- assert.deepStrictEqual(snapshot.clearedOptions, { clearEmail: true });
- assert.equal(snapshot.logs.at(-1).message, '当前 LuckMail 邮箱运行态已清空,下轮将优先复用未用邮箱或重新购买邮箱。');
- });
|