403 lines
13 KiB
JavaScript
403 lines
13 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
const flowRegistrySource = fs.readFileSync('shared/flow-registry.js', 'utf8');
|
|
const settingsSchemaSource = fs.readFileSync('shared/settings-schema.js', 'utf8');
|
|
const backgroundSource = fs.readFileSync('background.js', 'utf8');
|
|
|
|
function extractFunction(name) {
|
|
const markers = [`async function ${name}(`, `function ${name}(`];
|
|
const start = markers
|
|
.map((marker) => backgroundSource.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 < backgroundSource.length; i += 1) {
|
|
const ch = backgroundSource[i];
|
|
if (ch === '(') parenDepth += 1;
|
|
if (ch === ')') {
|
|
parenDepth -= 1;
|
|
if (parenDepth === 0) signatureEnded = true;
|
|
}
|
|
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 < backgroundSource.length; end += 1) {
|
|
const ch = backgroundSource[end];
|
|
if (ch === '{') depth += 1;
|
|
if (ch === '}') {
|
|
depth -= 1;
|
|
if (depth === 0) {
|
|
end += 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return backgroundSource.slice(start, end);
|
|
}
|
|
|
|
function buildHarness(extra = '') {
|
|
return new Function(`
|
|
const self = {};
|
|
${flowRegistrySource}
|
|
${settingsSchemaSource}
|
|
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
|
const DEFAULT_SUB2API_GROUP_NAMES = ['codex', 'openai-plus'];
|
|
const PERSISTED_SETTING_DEFAULTS = {
|
|
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
|
panelMode: 'cpa',
|
|
signupMethod: 'email',
|
|
plusModeEnabled: false,
|
|
plusPaymentMethod: 'paypal',
|
|
phoneVerificationEnabled: false,
|
|
mailProvider: '163',
|
|
ipProxyEnabled: false,
|
|
ipProxyService: '711proxy',
|
|
ipProxyMode: 'account',
|
|
kiroSourceId: 'kiro-rs',
|
|
kiroRsUrl: 'https://kiro.leftcode.xyz/admin',
|
|
kiroRsKey: '',
|
|
stepExecutionRangeByFlow: {},
|
|
};
|
|
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
|
const PERSISTED_SETTINGS_SCHEMA_KEYS = ['settingsSchemaVersion', 'settingsState'];
|
|
const LEGACY_AUTO_STEP_DELAY_KEYS = [];
|
|
const LEGACY_VERIFICATION_RESEND_COUNT_KEYS = [];
|
|
function normalizePanelMode(value = '') {
|
|
const normalized = String(value || '').trim().toLowerCase();
|
|
return normalized === 'sub2api' || normalized === 'codex2api' ? normalized : 'cpa';
|
|
}
|
|
function normalizeSignupMethod(value = '') {
|
|
return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email';
|
|
}
|
|
function normalizePlusPaymentMethod(value = '') {
|
|
const normalized = String(value || '').trim().toLowerCase();
|
|
return normalized === 'gopay' || normalized === 'gpc-helper' ? normalized : 'paypal';
|
|
}
|
|
function normalizeSub2ApiGroupNames(value) {
|
|
return Array.isArray(value) ? value.map((entry) => String(entry || '').trim()).filter(Boolean) : [];
|
|
}
|
|
function normalizeCloudflareDomains(value) { return Array.isArray(value) ? value : []; }
|
|
function normalizeCloudflareTempEmailDomains(value) { return Array.isArray(value) ? value : []; }
|
|
function normalizeCloudMailDomains(value) { return Array.isArray(value) ? value : []; }
|
|
function normalizeMailProvider(value = '') { return String(value || '163').trim().toLowerCase() || '163'; }
|
|
function normalizeStepExecutionRangeByFlow(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
|
|
function normalizeIpProxyProviderValue(value) { return String(value || '711proxy').trim() || '711proxy'; }
|
|
function normalizeIpProxyMode(value) { return String(value || 'account').trim() || 'account'; }
|
|
function normalizeIpProxyServiceProfiles(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
|
|
function buildIpProxyServiceProfileFromState() {
|
|
return {
|
|
mode: 'account',
|
|
apiUrl: '',
|
|
accountList: '',
|
|
accountSessionPrefix: '',
|
|
accountLifeMinutes: '',
|
|
poolTargetCount: '20',
|
|
host: '',
|
|
port: '',
|
|
protocol: 'http',
|
|
username: '',
|
|
password: '',
|
|
region: '',
|
|
};
|
|
}
|
|
function normalizeIpProxyAccountList(value) { return String(value || ''); }
|
|
function normalizeIpProxyAccountSessionPrefix(value) { return String(value || ''); }
|
|
function normalizeIpProxyAccountLifeMinutes(value) { return String(value || ''); }
|
|
function normalizeIpProxyPoolTargetCount(value) { return String(value || '20'); }
|
|
function normalizeIpProxyPort(value) { return String(value || '').trim(); }
|
|
function normalizeIpProxyProtocol(value) { return String(value || 'http').trim() || 'http'; }
|
|
function resolveSignupMethod(state = {}) {
|
|
const activeFlowId = String(state?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
|
|
if (activeFlowId === 'kiro') {
|
|
return 'email';
|
|
}
|
|
return String(state?.signupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email';
|
|
}
|
|
function resolveLegacyAutoStepDelaySeconds() { return undefined; }
|
|
${extractFunction('normalizePersistentSettingValue')}
|
|
${extractFunction('getSettingsSchemaApi')}
|
|
${extractFunction('buildPersistentSettingsPayload')}
|
|
${extractFunction('getPersistedSettings')}
|
|
${extractFunction('setPersistentSettings')}
|
|
${extra}
|
|
return {
|
|
buildPersistentSettingsPayload,
|
|
getPersistedSettings,
|
|
setPersistentSettings,
|
|
getRequestedKeys: typeof getRequestedKeys === 'function' ? getRequestedKeys : () => [],
|
|
getPersistedWrites: typeof getPersistedWrites === 'function' ? getPersistedWrites : () => [],
|
|
};
|
|
`)();
|
|
}
|
|
|
|
test('buildPersistentSettingsPayload writes canonical settings schema into persisted payloads when defaults are materialized', () => {
|
|
const api = buildHarness();
|
|
|
|
const payload = api.buildPersistentSettingsPayload({
|
|
activeFlowId: 'kiro',
|
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
|
kiroRsKey: 'secret-key',
|
|
}, { fillDefaults: true });
|
|
|
|
assert.equal(payload.activeFlowId, 'kiro');
|
|
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
|
assert.equal(payload.kiroRsKey, 'secret-key');
|
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
|
assert.equal(payload.settingsSchemaVersion, 3);
|
|
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
|
assert.equal(payload.settingsState.flows.kiro.source.selected, 'kiro-rs');
|
|
});
|
|
|
|
test('buildPersistentSettingsPayload accepts schema-only input when requireKnownKeys is enabled', () => {
|
|
const api = buildHarness();
|
|
|
|
const payload = api.buildPersistentSettingsPayload({
|
|
settingsSchemaVersion: 3,
|
|
settingsState: {
|
|
activeFlowId: 'kiro',
|
|
services: {
|
|
email: { provider: '163' },
|
|
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
|
|
},
|
|
flows: {
|
|
openai: {
|
|
source: { selected: 'cpa', entries: {} },
|
|
account: { customPassword: '' },
|
|
signup: {
|
|
signupMethod: 'email',
|
|
phoneVerificationEnabled: false,
|
|
phoneSignupReloginAfterBindEmailEnabled: false,
|
|
},
|
|
plus: {
|
|
plusModeEnabled: false,
|
|
plusPaymentMethod: 'paypal',
|
|
},
|
|
autoRun: {
|
|
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 11 },
|
|
},
|
|
},
|
|
kiro: {
|
|
source: {
|
|
selected: 'kiro-rs',
|
|
entries: {
|
|
'kiro-rs': {
|
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
|
kiroRsKey: 'schema-only-key',
|
|
},
|
|
},
|
|
},
|
|
options: {
|
|
},
|
|
autoRun: {
|
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, { requireKnownKeys: true });
|
|
|
|
assert.equal(payload.activeFlowId, 'kiro');
|
|
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
|
assert.equal(payload.kiroRsKey, 'schema-only-key');
|
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
|
assert.equal(payload.settingsSchemaVersion, 3);
|
|
});
|
|
|
|
test('getPersistedSettings reads schema keys alongside legacy flat settings keys', async () => {
|
|
const api = buildHarness(`
|
|
let requestedKeys = [];
|
|
const chrome = {
|
|
storage: {
|
|
local: {
|
|
async get(keys) {
|
|
requestedKeys = Array.isArray(keys) ? [...keys] : [];
|
|
return {};
|
|
},
|
|
},
|
|
},
|
|
};
|
|
function getRequestedKeys() {
|
|
return requestedKeys;
|
|
}
|
|
`);
|
|
|
|
const state = await api.getPersistedSettings();
|
|
|
|
assert.ok(api.getRequestedKeys().includes('settingsSchemaVersion'));
|
|
assert.ok(api.getRequestedKeys().includes('settingsState'));
|
|
assert.equal(state.settingsSchemaVersion, 3);
|
|
assert.equal(state.settingsState.activeFlowId, 'openai');
|
|
});
|
|
|
|
test('getPersistedSettings can project schema-only storage back into legacy flat settings', async () => {
|
|
const api = buildHarness(`
|
|
const chrome = {
|
|
storage: {
|
|
local: {
|
|
async get() {
|
|
return {
|
|
settingsSchemaVersion: 3,
|
|
settingsState: {
|
|
activeFlowId: 'kiro',
|
|
services: {
|
|
email: { provider: 'hotmail' },
|
|
proxy: { enabled: true, provider: '711proxy', mode: 'account' },
|
|
},
|
|
flows: {
|
|
openai: {
|
|
source: {
|
|
selected: 'sub2api',
|
|
entries: {},
|
|
},
|
|
account: { customPassword: '' },
|
|
signup: {
|
|
signupMethod: 'email',
|
|
phoneVerificationEnabled: false,
|
|
phoneSignupReloginAfterBindEmailEnabled: false,
|
|
},
|
|
plus: {
|
|
plusModeEnabled: false,
|
|
plusPaymentMethod: 'paypal',
|
|
},
|
|
autoRun: {
|
|
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 11 },
|
|
},
|
|
},
|
|
kiro: {
|
|
source: {
|
|
selected: 'kiro-rs',
|
|
entries: {
|
|
'kiro-rs': {
|
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
|
kiroRsKey: 'stored-key',
|
|
},
|
|
},
|
|
},
|
|
options: {
|
|
},
|
|
autoRun: {
|
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
},
|
|
},
|
|
},
|
|
};
|
|
`);
|
|
|
|
const state = await api.getPersistedSettings();
|
|
|
|
assert.equal(state.activeFlowId, 'kiro');
|
|
assert.equal(state.panelMode, 'sub2api');
|
|
assert.equal(state.mailProvider, 'hotmail');
|
|
assert.equal(state.ipProxyEnabled, true);
|
|
assert.equal(state.kiroRsUrl, 'https://kiro.example.com/admin');
|
|
assert.equal(state.kiroRsKey, 'stored-key');
|
|
assert.equal(Object.prototype.hasOwnProperty.call(state, 'kiroRegion'), false);
|
|
assert.deepEqual(state.stepExecutionRangeByFlow.kiro, {
|
|
enabled: true,
|
|
fromStep: 1,
|
|
toStep: 7,
|
|
});
|
|
});
|
|
|
|
test('setPersistentSettings materializes canonical schema keys for schema-only updates', async () => {
|
|
const api = buildHarness(`
|
|
const persistedWrites = [];
|
|
const chrome = {
|
|
storage: {
|
|
local: {
|
|
async get() {
|
|
return {};
|
|
},
|
|
async set(payload) {
|
|
persistedWrites.push(JSON.parse(JSON.stringify(payload)));
|
|
},
|
|
},
|
|
},
|
|
};
|
|
function getPersistedWrites() {
|
|
return persistedWrites;
|
|
}
|
|
`);
|
|
|
|
const persisted = await api.setPersistentSettings({
|
|
settingsSchemaVersion: 3,
|
|
settingsState: {
|
|
activeFlowId: 'kiro',
|
|
services: {
|
|
email: { provider: '163' },
|
|
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
|
|
},
|
|
flows: {
|
|
openai: {
|
|
source: { selected: 'cpa', entries: {} },
|
|
account: { customPassword: '' },
|
|
signup: {
|
|
signupMethod: 'email',
|
|
phoneVerificationEnabled: false,
|
|
phoneSignupReloginAfterBindEmailEnabled: false,
|
|
},
|
|
plus: {
|
|
plusModeEnabled: false,
|
|
plusPaymentMethod: 'paypal',
|
|
},
|
|
autoRun: {
|
|
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 11 },
|
|
},
|
|
},
|
|
kiro: {
|
|
source: {
|
|
selected: 'kiro-rs',
|
|
entries: {
|
|
'kiro-rs': {
|
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
|
kiroRsKey: 'nested-only-key',
|
|
},
|
|
},
|
|
},
|
|
options: {
|
|
},
|
|
autoRun: {
|
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const write = api.getPersistedWrites().at(-1);
|
|
|
|
assert.equal(persisted.activeFlowId, 'kiro');
|
|
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
|
|
assert.equal(persisted.kiroRsKey, 'nested-only-key');
|
|
assert.equal(Object.prototype.hasOwnProperty.call(persisted, 'kiroRegion'), false);
|
|
assert.equal(persisted.settingsSchemaVersion, 3);
|
|
assert.equal(write.activeFlowId, 'kiro');
|
|
assert.equal(write.kiroRsUrl, 'https://kiro.example.com/admin');
|
|
assert.equal(write.kiroRsKey, 'nested-only-key');
|
|
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRegion'), false);
|
|
assert.equal(write.settingsSchemaVersion, 3);
|
|
assert.equal(write.settingsState.activeFlowId, 'kiro');
|
|
});
|