feat(flow): unify sms multi-provider fallback and stabilize proxy/auth retries

This commit is contained in:
daniellee2015
2026-05-03 01:20:10 +08:00
parent 99313843c1
commit 2437e6e67d
37 changed files with 12254 additions and 1142 deletions
+94
View File
@@ -392,3 +392,97 @@ return {
}]);
assert.match(events.logs[0].message, /授权后链总超时已关闭/);
});
test('executeStep retries fetch-network errors for step 4 with cooldown and bounded attempts', async () => {
const api = new Function(`
const LOG_PREFIX = '[test]';
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
const BROWSER_SWITCH_REQUIRED_ERROR_PREFIX = 'BROWSER_SWITCH_REQUIRED::';
const AUTH_CHAIN_STEP_IDS = new Set([7, 8, 9, 10]);
const STEP_FETCH_NETWORK_RETRY_POLICIES = new Map([[4, { maxAttempts: 3, cooldownMs: 1 }]]);
let activeTopLevelAuthChainExecution = null;
let stopRequested = false;
const events = {
logs: [],
statusCalls: [],
registryCalls: [],
sleepCalls: [],
};
let runCount = 0;
async function addLog(message, level = 'info') {
events.logs.push({ message, level });
}
async function setStepStatus(step, status) {
events.statusCalls.push({ step, status });
}
async function humanStepDelay() {}
async function getState() {
return {
flowStartTime: null,
stepStatuses: {},
};
}
function getErrorMessage(error) {
return error?.message || String(error || '');
}
async function appendManualAccountRunRecordIfNeeded() {}
function isTerminalSecurityBlockedError() {
return false;
}
async function handleCloudflareSecurityBlocked() {}
function doesStepUseCompletionSignal() {
return false;
}
async function sleepWithStop(ms) {
events.sleepCalls.push(ms);
}
const stepRegistry = {
getStepDefinition(step) {
return { id: step, key: 'fetch-signup-code' };
},
async executeStep(step) {
events.registryCalls.push(step);
runCount += 1;
if (runCount < 3) {
throw new TypeError('Failed to fetch');
}
},
};
function getStepRegistryForState() {
return stepRegistry;
}
function getStepDefinitionForState(step) {
return { id: step, key: 'fetch-signup-code' };
}
${extractFunction('isStopError')}
${extractFunction('isRetryableContentScriptTransportError')}
${extractFunction('isStepFetchNetworkRetryableError')}
${extractFunction('getStepFetchNetworkRetryPolicy')}
${extractFunction('throwIfStopped')}
${extractFunction('isAuthChainStep')}
${extractFunction('acquireTopLevelAuthChainExecution')}
${extractFunction('isBrowserSwitchRequiredError')}
${extractFunction('getBrowserSwitchRequiredMessage')}
${extractFunction('handleBrowserSwitchRequired')}
${extractFunction('executeStep')}
return {
executeStep,
snapshot() {
return events;
},
};
`)();
await api.executeStep(4);
const events = api.snapshot();
assert.deepStrictEqual(events.registryCalls, [4, 4, 4]);
assert.deepStrictEqual(events.sleepCalls, [1, 1]);
assert.equal(
events.logs.filter(({ message }) => message.includes('[NETWORK_FETCH_RETRY]')).length >= 3,
true
);
});