fix(madao): 修复换号后国家区号残留

让 MaDao activation 使用 ISO country code,并避免 routing replace 沿用旧 ticket 的 country。
提交 OpenAI add-phone 前校验当前国家区号与手机号匹配,防止页面残留 Thailand 区号。
This commit is contained in:
Isulew
2026-05-29 12:12:19 +08:00
parent f26f7e949e
commit e50b0fbfeb
5 changed files with 325 additions and 29 deletions
+62
View File
@@ -356,6 +356,68 @@ test('phone auth can auto-select country by dial code even when number has no pl
}
});
test('phone auth rejects stale selected country when it does not match phone dial code', async () => {
const originalDocument = global.document;
const originalEvent = global.Event;
const originalLocation = global.location;
const dom = createFakeAddPhoneDom({
options: [
{ value: 'TH', textContent: 'Thailand (+66)', buttonText: 'Thailand (+66)' },
{ value: 'GB', textContent: 'United Kingdom (+44)', buttonText: 'United Kingdom (+44)' },
],
selectedIndex: 0,
});
global.document = dom.document;
global.Event = class Event {
constructor(type) {
this.type = type;
}
};
global.location = { href: 'https://auth.openai.com/add-phone' };
try {
const helpers = api.createPhoneAuthHelpers({
fillInput: (element, value) => {
element.value = value;
},
getActionText: () => '',
getPageTextSnapshot: () => '',
getVerificationErrorText: () => '',
humanPause: async () => {},
isActionEnabled: () => true,
isAddPhonePageReady: () => true,
isConsentReady: () => false,
isPhoneVerificationPageReady: () => false,
isVisibleElement: () => true,
simulateClick: (element) => {
element.click?.();
},
sleep: async () => {},
throwIfStopped: () => {},
waitForElement: async () => null,
});
await assert.rejects(
() => helpers.submitPhoneNumber({
countryLabel: 'Country #10',
phoneNumber: '+84943328460',
}),
/Failed to select "Country #10" on the add-phone page\./
);
assert.equal(dom.select.value, 'TH');
assert.equal(dom.phoneInput.value, '');
assert.equal(dom.hiddenPhoneInput.value, '');
assert.equal(dom.wasSubmitClicked(), false);
} finally {
global.document = originalDocument;
global.Event = originalEvent;
global.location = originalLocation;
}
});
test('phone auth resend stops with PHONE_ROUTE_405_RECOVERY_FAILED instead of endless Try-again loop', async () => {
const originalDocument = global.document;
const originalLocation = global.location;
+155 -1
View File
@@ -285,7 +285,6 @@ test('phone verification helper acquires, polls and releases MaDao activation th
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
createMaDaoProvider: maDaoModule.createProvider,
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
@@ -365,6 +364,161 @@ test('phone verification helper acquires, polls and releases MaDao activation th
assert.deepStrictEqual(requests[2].body, { ticket_id: 'madao-1', action: 'finish' });
});
test('phone verification helper keeps MaDao routing replacement country as ISO code on resubmit', async () => {
const requests = [];
const messages = [];
let currentState = {
phoneSmsProvider: 'madao',
madaoBaseUrl: 'http://127.0.0.1:7822/',
madaoHttpSecret: 'secret-token',
madaoMode: 'routing_plan',
madaoRoutingPlanId: 'rp-openai',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 15,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
let acquireCalls = 0;
let replaceCalls = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
const body = options.body ? JSON.parse(options.body) : null;
requests.push({ url: parsedUrl, options, body });
if (parsedUrl.pathname === '/api/acquire') {
acquireCalls += 1;
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
ticket_id: 'madao-ticket-routing-1',
provider: 'herosms',
service: 'openai',
country: 'TH',
phone_number: '+66950002222',
routing_plan_id: 'rp-openai',
routing_plan_name: 'OpenAI Plan',
routing_item_id: 'route-1',
status: 'waiting_code',
}),
};
}
if (parsedUrl.pathname === '/api/poll') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
ticket_id: body.ticket_id,
provider: body.ticket_id === 'madao-ticket-routing-1' ? 'herosms' : 'smsbower',
status: body.ticket_id === 'madao-ticket-routing-1' ? 'waiting_code' : 'code_received',
code: body.ticket_id === 'madao-ticket-routing-1' ? null : '654321',
}),
};
}
if (parsedUrl.pathname === '/api/routing/replace') {
replaceCalls += 1;
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
current_ticket_id: 'madao-ticket-routing-1',
current_ticket_release: {
ticket_id: 'madao-ticket-routing-1',
provider: 'herosms',
status: 'cancelled',
},
next_ticket: {
ticket_id: 'madao-ticket-routing-2',
provider: 'smsbower',
service: 'openai',
country: 'VN',
phone_number: '+84943328460',
routing_plan_id: 'rp-openai',
routing_plan_name: 'OpenAI Plan',
routing_item_id: 'route-2',
status: 'waiting_code',
},
}),
};
}
if (parsedUrl.pathname === '/api/release') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ ok: true }),
};
}
throw new Error(`Unexpected MaDao path: ${parsedUrl.pathname}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message);
if (message.type === 'STEP8_GET_STATE') {
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const result = await helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
});
assert.deepStrictEqual(result, {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
});
assert.equal(acquireCalls, 1);
assert.equal(replaceCalls, 1);
const phoneSubmissions = messages.filter((message) => message.type === 'SUBMIT_PHONE_NUMBER');
assert.equal(phoneSubmissions.length, 2);
assert.equal(phoneSubmissions[0].payload.phoneNumber, '+66950002222');
assert.equal(phoneSubmissions[0].payload.countryId, 'TH');
assert.equal(phoneSubmissions[0].payload.countryLabel, 'Thailand');
assert.equal(phoneSubmissions[1].payload.phoneNumber, '+84943328460');
assert.equal(phoneSubmissions[1].payload.countryId, 'VN');
assert.equal(phoneSubmissions[1].payload.countryLabel, 'Vietnam');
});
test('signup phone helper persists signup runtime state without touching add-phone activation', async () => {
const setStateCalls = [];
let currentState = {