重构 OAuth 总超时为 CPA 来源专属策略
This commit is contained in:
@@ -48,6 +48,33 @@ function extractFunction(name) {
|
||||
return source.slice(start, end);
|
||||
}
|
||||
|
||||
const OAUTH_TIMEOUT_BUDGET_TEST_REGISTRY = `
|
||||
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
||||
const self = {
|
||||
MultiPageFlowRegistry: {
|
||||
normalizeFlowId(value, fallback = 'openai') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return ['openai', 'kiro', 'grok'].includes(normalized) ? normalized : fallback;
|
||||
},
|
||||
normalizeTargetId(flowId, value, fallback = 'cpa') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
if (flowId === 'openai' && ['cpa', 'sub2api', 'codex2api'].includes(normalized)) return normalized;
|
||||
if (flowId === 'kiro') return 'kiro-rs';
|
||||
if (flowId === 'grok') return 'webchat2api';
|
||||
return fallback;
|
||||
},
|
||||
getDefaultTargetId(flowId) {
|
||||
return flowId === 'openai' ? 'cpa' : '';
|
||||
},
|
||||
getTargetCapabilities(flowId, targetId) {
|
||||
return flowId === 'openai' && targetId === 'cpa'
|
||||
? { usesOauthTimeoutBudget: true }
|
||||
: { usesOauthTimeoutBudget: false };
|
||||
},
|
||||
},
|
||||
};
|
||||
`;
|
||||
|
||||
test('background auth chain set does not include Plus session import nodes', () => {
|
||||
const authChainStart = source.indexOf('const AUTH_CHAIN_NODE_IDS = new Set([');
|
||||
const authChainEnd = source.indexOf(']);', authChainStart);
|
||||
@@ -431,6 +458,9 @@ test('oauth timeout budget ignores stale deadlines from an old oauth url', async
|
||||
const api = new Function(`
|
||||
const LOG_PREFIX = '[test]';
|
||||
const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
${OAUTH_TIMEOUT_BUDGET_TEST_REGISTRY}
|
||||
${extractFunction('resolveOAuthTimeoutBudgetScope')}
|
||||
${extractFunction('shouldUseOAuthTimeoutBudget')}
|
||||
${extractFunction('normalizeOAuthFlowDeadlineAt')}
|
||||
${extractFunction('normalizeOAuthFlowSourceUrl')}
|
||||
${extractFunction('getOAuthFlowRemainingMs')}
|
||||
@@ -444,6 +474,8 @@ return {
|
||||
step: 8,
|
||||
actionLabel: '登录验证码流程',
|
||||
state: {
|
||||
activeFlowId: 'openai',
|
||||
targetId: 'cpa',
|
||||
oauthUrl: 'https://oauth.example/current',
|
||||
oauthFlowDeadlineAt: Date.now() + 1200,
|
||||
oauthFlowDeadlineSourceUrl: 'https://oauth.example/old',
|
||||
@@ -457,7 +489,10 @@ test('oauth timeout budget clamps local timeout when enabled by default', async
|
||||
const api = new Function(`
|
||||
const LOG_PREFIX = '[test]';
|
||||
const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
${OAUTH_TIMEOUT_BUDGET_TEST_REGISTRY}
|
||||
${extractFunction('buildOAuthFlowTimeoutError')}
|
||||
${extractFunction('resolveOAuthTimeoutBudgetScope')}
|
||||
${extractFunction('shouldUseOAuthTimeoutBudget')}
|
||||
${extractFunction('normalizeOAuthFlowDeadlineAt')}
|
||||
${extractFunction('normalizeOAuthFlowSourceUrl')}
|
||||
${extractFunction('getOAuthFlowRemainingMs')}
|
||||
@@ -471,6 +506,8 @@ return {
|
||||
step: 8,
|
||||
actionLabel: '登录验证码流程',
|
||||
state: {
|
||||
activeFlowId: 'openai',
|
||||
targetId: 'cpa',
|
||||
oauthUrl: 'https://oauth.example/current',
|
||||
oauthFlowDeadlineAt: Date.now() + 1200,
|
||||
oauthFlowDeadlineSourceUrl: 'https://oauth.example/current',
|
||||
@@ -481,11 +518,14 @@ return {
|
||||
assert(timeoutMs >= 1000);
|
||||
});
|
||||
|
||||
test('oauth timeout budget disabled mode ignores active deadlines', async () => {
|
||||
test('oauth timeout budget ignores active deadlines outside openai cpa target', async () => {
|
||||
const api = new Function(`
|
||||
const LOG_PREFIX = '[test]';
|
||||
const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
${OAUTH_TIMEOUT_BUDGET_TEST_REGISTRY}
|
||||
${extractFunction('buildOAuthFlowTimeoutError')}
|
||||
${extractFunction('resolveOAuthTimeoutBudgetScope')}
|
||||
${extractFunction('shouldUseOAuthTimeoutBudget')}
|
||||
${extractFunction('normalizeOAuthFlowDeadlineAt')}
|
||||
${extractFunction('normalizeOAuthFlowSourceUrl')}
|
||||
${extractFunction('getOAuthFlowRemainingMs')}
|
||||
@@ -499,7 +539,8 @@ return {
|
||||
step: 9,
|
||||
actionLabel: 'OAuth localhost 回调',
|
||||
state: {
|
||||
oauthFlowTimeoutEnabled: false,
|
||||
activeFlowId: 'openai',
|
||||
targetId: 'sub2api',
|
||||
oauthUrl: 'https://oauth.example/current',
|
||||
oauthFlowDeadlineAt: Date.now() - 1000,
|
||||
oauthFlowDeadlineSourceUrl: 'https://oauth.example/current',
|
||||
@@ -509,16 +550,18 @@ return {
|
||||
assert.equal(timeoutMs, 15000);
|
||||
});
|
||||
|
||||
test('startOAuthFlowTimeoutWindow clears stale deadline when timeout is disabled', async () => {
|
||||
test('startOAuthFlowTimeoutWindow clears stale deadline outside openai cpa target', async () => {
|
||||
const events = {
|
||||
stateUpdates: [],
|
||||
logs: [],
|
||||
};
|
||||
const api = new Function('events', `
|
||||
const api = new Function('events', `
|
||||
const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
${OAUTH_TIMEOUT_BUDGET_TEST_REGISTRY}
|
||||
async function getState() {
|
||||
return {
|
||||
oauthFlowTimeoutEnabled: false,
|
||||
activeFlowId: 'openai',
|
||||
targetId: 'sub2api',
|
||||
oauthFlowDeadlineAt: Date.now() - 1000,
|
||||
oauthFlowDeadlineSourceUrl: 'https://oauth.example/old',
|
||||
};
|
||||
@@ -529,6 +572,8 @@ async function setState(update) {
|
||||
async function addLog(message, level) {
|
||||
events.logs.push({ message, level });
|
||||
}
|
||||
${extractFunction('resolveOAuthTimeoutBudgetScope')}
|
||||
${extractFunction('shouldUseOAuthTimeoutBudget')}
|
||||
${extractFunction('normalizeOAuthFlowSourceUrl')}
|
||||
${extractFunction('startOAuthFlowTimeoutWindow')}
|
||||
return {
|
||||
@@ -546,7 +591,7 @@ return {
|
||||
oauthFlowDeadlineAt: null,
|
||||
oauthFlowDeadlineSourceUrl: null,
|
||||
}]);
|
||||
assert.match(events.logs[0].message, /授权后链总超时已关闭/);
|
||||
assert.deepStrictEqual(events.logs, []);
|
||||
});
|
||||
|
||||
test('oauth localhost timeout recovery resumes from bound-email relogin tail when present', async () => {
|
||||
|
||||
@@ -156,12 +156,11 @@ return {
|
||||
});
|
||||
});
|
||||
|
||||
test('oauth flow timeout setting is persisted and normalized as boolean', () => {
|
||||
test('cloudflare temp email settings payload ignores removed UI-only flags', () => {
|
||||
const api = new Function(`
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PERSISTED_SETTING_DEFAULTS = {
|
||||
panelMode: 'cpa',
|
||||
oauthFlowTimeoutEnabled: true,
|
||||
autoStepDelaySeconds: null,
|
||||
verificationResendCount: DEFAULT_VERIFICATION_RESEND_COUNT,
|
||||
mailProvider: '163',
|
||||
@@ -222,15 +221,10 @@ return {
|
||||
};
|
||||
`)();
|
||||
|
||||
assert.equal(api.normalizePersistentSettingValue('oauthFlowTimeoutEnabled', 0), false);
|
||||
assert.equal(api.normalizePersistentSettingValue('oauthFlowTimeoutEnabled', 1), true);
|
||||
|
||||
assert.deepEqual(api.buildPersistentSettingsPayload({
|
||||
oauthFlowTimeoutEnabled: false,
|
||||
}), {
|
||||
oauthFlowTimeoutEnabled: false,
|
||||
});
|
||||
removedUiOnlyFlag: false,
|
||||
}), {});
|
||||
|
||||
const defaults = api.buildPersistentSettingsPayload({}, { fillDefaults: true });
|
||||
assert.equal(defaults.oauthFlowTimeoutEnabled, true);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(defaults, 'removedUiOnlyFlag'), false);
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ test('flow capability registry keeps OpenAI phone signup available only when run
|
||||
assert.equal(enabledState.canUsePhoneSignup, true);
|
||||
assert.equal(enabledState.effectiveSignupMethod, 'phone');
|
||||
assert.equal(enabledState.shouldWarnCpaPhoneSignup, true);
|
||||
assert.equal(enabledState.targetCapabilities.usesOauthTimeoutBudget, true);
|
||||
assert.deepEqual(enabledState.effectiveSignupMethods, ['email', 'phone']);
|
||||
|
||||
const plusLockedState = registry.resolveSidepanelCapabilities({
|
||||
@@ -43,6 +44,7 @@ test('flow capability registry keeps OpenAI phone signup available only when run
|
||||
assert.equal(plusLockedState.canUsePhoneSignup, false);
|
||||
assert.equal(plusLockedState.effectiveSignupMethod, 'email');
|
||||
assert.equal(plusLockedState.shouldWarnCpaPhoneSignup, false);
|
||||
assert.equal(plusLockedState.targetCapabilities.usesOauthTimeoutBudget, false);
|
||||
assert.deepEqual(plusLockedState.effectiveSignupMethods, ['email']);
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,14 @@ test('flow registry exposes canonical flow and target metadata', () => {
|
||||
flowRegistry.getFlowDefinition('openai')?.targets?.cpa?.defaultState,
|
||||
{ vpsUrl: '', vpsPassword: '', localCpaStep9Mode: 'submit' }
|
||||
);
|
||||
assert.equal(
|
||||
flowRegistry.getTargetCapabilities('openai', 'cpa')?.usesOauthTimeoutBudget,
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
flowRegistry.getTargetCapabilities('openai', 'sub2api')?.usesOauthTimeoutBudget,
|
||||
undefined
|
||||
);
|
||||
assert.deepEqual(
|
||||
flowRegistry.getFlowDefinition('kiro')?.targets?.['kiro-rs']?.defaultState,
|
||||
{ baseUrl: '', apiKey: '' }
|
||||
|
||||
@@ -248,7 +248,6 @@ const inputTempEmailUseRandomSubdomain = { checked: true };
|
||||
const inputAutoSkipFailures = { checked: false };
|
||||
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
|
||||
const inputAutoStepDelaySeconds = { value: '10' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '6' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
|
||||
@@ -133,7 +133,6 @@ const inputTempEmailUseRandomSubdomain = { checked: false };
|
||||
const inputAutoSkipFailures = { checked: false };
|
||||
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
|
||||
const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '4' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
@@ -406,7 +405,6 @@ const inputLuckmailDomain = { value: '' };
|
||||
const inputAutoSkipFailures = { checked: false };
|
||||
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' };
|
||||
const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '' };
|
||||
const inputPhoneVerificationEnabled = { checked: false };
|
||||
const selectPhoneSmsProvider = { value: 'hero-sms' };
|
||||
|
||||
@@ -204,7 +204,6 @@ const inputTempEmailUseRandomSubdomain = { checked: false };
|
||||
const inputAutoSkipFailures = { checked: false };
|
||||
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
|
||||
const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '4' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
|
||||
@@ -48,7 +48,6 @@ test('sidepanel splits shared auto-run controls from openai oauth controls', ()
|
||||
const step6CookieIndex = html.indexOf('id="row-step6-cookie-settings"');
|
||||
const sharedAutoRunIndex = html.indexOf('id="row-shared-auto-run"');
|
||||
const threadIntervalIndex = html.indexOf('id="row-auto-run-thread-interval"');
|
||||
const oauthTimeoutIndex = html.indexOf('id="row-oauth-flow-timeout"');
|
||||
const stepRangeIndex = html.indexOf('id="row-step-execution-range"');
|
||||
const oauthDisplayIndex = html.indexOf('id="row-oauth-display"');
|
||||
const oauthCallbackIndex = html.indexOf('id="row-oauth-callback"');
|
||||
@@ -57,15 +56,15 @@ test('sidepanel splits shared auto-run controls from openai oauth controls', ()
|
||||
assert.notEqual(step6CookieIndex, -1);
|
||||
assert.notEqual(sharedAutoRunIndex, -1);
|
||||
assert.notEqual(threadIntervalIndex, -1);
|
||||
assert.notEqual(oauthTimeoutIndex, -1);
|
||||
assert.doesNotMatch(html, /id="row-oauth-flow-timeout"/);
|
||||
assert.doesNotMatch(html, /id="input-oauth-flow-timeout-enabled"/);
|
||||
assert.notEqual(stepRangeIndex, -1);
|
||||
assert.notEqual(oauthDisplayIndex, -1);
|
||||
assert.notEqual(oauthCallbackIndex, -1);
|
||||
assert.notEqual(settingsActionsIndex, -1);
|
||||
assert.ok(sharedAutoRunIndex > step6CookieIndex, 'shared auto-run should render below the openai step6 cookie row');
|
||||
assert.ok(threadIntervalIndex > sharedAutoRunIndex, 'thread interval should be part of the shared auto-run block');
|
||||
assert.ok(threadIntervalIndex < oauthTimeoutIndex, 'thread interval should stay outside openai oauth controls');
|
||||
assert.ok(stepRangeIndex > oauthTimeoutIndex, 'step execution range should render below oauth timeout');
|
||||
assert.ok(stepRangeIndex > threadIntervalIndex, 'step execution range should render below shared thread interval');
|
||||
assert.ok(stepRangeIndex < oauthDisplayIndex, 'step execution range should stay above oauth runtime display');
|
||||
assert.ok(oauthCallbackIndex > oauthDisplayIndex, 'openai callback row should follow the oauth display');
|
||||
assert.ok(settingsActionsIndex > oauthCallbackIndex, 'save settings action should live outside the callback row');
|
||||
|
||||
Reference in New Issue
Block a user