fix: prevent hung phone page-state probes from stalling SMS polling

This commit is contained in:
imxiaozhan
2026-05-09 22:08:47 -04:00
parent 6bd00dbd5b
commit b7b6deed5e
2 changed files with 154 additions and 25 deletions
+114 -3
View File
@@ -568,6 +568,105 @@ test('signup phone helper fails stale email-verification that appears during SMS
assert.deepStrictEqual(contentMessages.map((message) => message.type), ['STEP8_GET_STATE', 'STEP8_GET_STATE']);
});
test('signup phone helper does not let a hung page-state probe stall HeroSMS polling', async () => {
let smsPollCount = 0;
let pageReadyCalls = 0;
const statusActions = [];
const contentMessages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsReuseEnabled: false,
phoneCodeWaitSeconds: 15,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
signupPhoneNumber: '66959916439',
signupPhoneVerificationPurpose: 'signup',
signupPhoneActivation: {
activationId: 'signup-123',
phoneNumber: '66959916439',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 0,
maxUses: 3,
},
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {
pageReadyCalls += 1;
if (pageReadyCalls >= 2) {
return new Promise(() => {});
}
},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
const action = parsedUrl.searchParams.get('action');
if (action === 'getStatus') {
smsPollCount += 1;
return {
ok: true,
text: async () => 'STATUS_WAIT_CODE',
};
}
if (action === 'setStatus') {
statusActions.push(parsedUrl.searchParams.get('status'));
return {
ok: true,
text: async () => 'ACCESS_READY',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (fallback) => fallback,
getState: async () => currentState,
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'STEP8_GET_STATE') {
return {
emailVerificationPage: false,
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
throw new Error('SMS timeout should fail before submitting a code');
}
return {};
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
let caughtError = null;
try {
await Promise.race([
helpers.completeSignupPhoneVerificationFlow(77, {
state: currentState,
pageStateCheckTimeoutMs: 1,
}),
new Promise((_, reject) => setTimeout(
() => reject(new Error('hung waiting for signup phone page-state probe')),
50
)),
]);
} catch (error) {
caughtError = error;
}
assert.equal(smsPollCount, 1, 'HeroSMS polling should continue after the first waiting status');
assert.equal(pageReadyCalls, 2, 'should attempt the page-state probe during SMS polling');
assert.deepStrictEqual(contentMessages.map((message) => message.type), ['STEP8_GET_STATE']);
assert.deepStrictEqual(statusActions, ['8']);
assert.ok(caughtError, 'expected SMS timeout rather than a stalled page-state probe');
assert.doesNotMatch(caughtError.message, /hung waiting for signup phone page-state probe/);
assert.match(caughtError.message, /等待手机验证码超时/);
});
test('signup phone helper fails stale email-verification on 5sim RECEIVED without code during SMS polling', async () => {
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
@@ -5432,7 +5531,7 @@ test('phone verification helper replaces number immediately when phone-verificat
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), true);
});
test('phone verification helper directly navigates back to add-phone when replace-number recovery loses the auth page', async () => {
test('phone verification helper directly navigates back to add-phone when replace-number recovery page-state probe hangs', async () => {
const requests = [];
const messages = [];
const navigationCalls = [];
@@ -5458,13 +5557,24 @@ test('phone verification helper directly navigates back to add-phone when replac
];
let numberIndex = 0;
const realDateNow = Date.now;
const realSetTimeout = global.setTimeout;
let fakeNow = 0;
Date.now = () => fakeNow;
global.setTimeout = (callback, ms, ...args) => realSetTimeout(
callback,
Number(ms) >= 12000 ? 1 : ms,
...args
);
try {
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
ensureStep8SignupPageReady: async () => {
if (!addPhoneRouteReady) {
return new Promise(() => {});
}
return undefined;
},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
@@ -5539,7 +5649,7 @@ test('phone verification helper directly navigates back to add-phone when replac
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
addPhoneRouteReady = false;
throw new Error('Could not establish connection. Receiving end does not exist.');
return {};
}
if (message.type === 'STEP8_GET_STATE') {
if (!addPhoneRouteReady) {
@@ -5586,6 +5696,7 @@ test('phone verification helper directly navigates back to add-phone when replac
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), true);
} finally {
Date.now = realDateNow;
global.setTimeout = realSetTimeout;
}
});