Files
FlowPilot/tests/phone-verification-flow.test.js
T
朴圣佑 2eb913e00b feat(phone-sms): support 5sim provider
(cherry picked from commit 7d10cab9765ed662b5088f791c8635fd8cc63e44)
2026-05-04 03:30:43 +08:00

4315 lines
139 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('background/phone-verification-flow.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundPhoneVerification;`)(globalScope);
function buildHeroSmsPricesPayload({ country = '52', service = 'dr', cost = 0.08, count = 25370, physicalCount = 14528 } = {}) {
return JSON.stringify({
[country]: {
[service]: {
cost,
count,
physicalCount,
},
},
});
}
function buildHeroSmsStatusV2Payload({ smsCode = '', smsText = '', callCode = '' } = {}) {
return JSON.stringify({
verificationType: 2,
sms: {
dateTime: '2026-02-18T16:11:33+00:00',
code: smsCode,
text: smsText,
},
call: {
code: callCode,
},
});
}
test('phone verification helper requests HeroSMS numbers with fixed OpenAI and Thailand parameters', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
return {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key' });
assert.deepStrictEqual(activation, {
activationId: '123456',
phoneNumber: '66959916439',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 0,
maxUses: 3,
});
assert.equal(requests.length, 2);
assert.equal(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[0].searchParams.get('service'), 'dr');
assert.equal(requests[0].searchParams.get('country'), '52');
assert.equal(requests[0].searchParams.get('api_key'), 'demo-key');
assert.equal(requests[1].searchParams.get('action'), 'getNumber');
assert.equal(requests[1].searchParams.get('maxPrice'), '0.08');
assert.equal(requests[1].searchParams.get('fixedPrice'), 'true');
assert.equal(requests[1].searchParams.get('service'), 'dr');
assert.equal(requests[1].searchParams.get('country'), '52');
assert.equal(requests[1].searchParams.get('api_key'), 'demo-key');
});
test('phone verification helper retries HeroSMS getPrices until it receives a usable lowest price', async () => {
const requests = [];
let getPricesAttempt = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
getPricesAttempt += 1;
return getPricesAttempt < 3
? {
ok: true,
text: async () => JSON.stringify({ unavailable: true }),
}
: {
ok: true,
text: async () => buildHeroSmsPricesPayload({ cost: 0.09 }),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key' });
assert.equal(requests.length, 4);
assert.equal(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[1].searchParams.get('action'), 'getPrices');
assert.equal(requests[2].searchParams.get('action'), 'getPrices');
assert.equal(requests[3].searchParams.get('action'), 'getNumber');
assert.equal(requests[3].searchParams.get('maxPrice'), '0.09');
assert.equal(requests[3].searchParams.get('fixedPrice'), 'true');
});
test('phone verification helper falls back to plain getNumber only after HeroSMS getPrices fails three times', async () => {
const requests = [];
let getPricesAttempt = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
getPricesAttempt += 1;
return {
ok: true,
text: async () => JSON.stringify({ unavailable: getPricesAttempt }),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key' });
assert.equal(requests.length, 4);
assert.equal(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[1].searchParams.get('action'), 'getPrices');
assert.equal(requests[2].searchParams.get('action'), 'getPrices');
assert.equal(requests[2].searchParams.get('service'), 'dr');
assert.equal(requests[2].searchParams.get('country'), '52');
assert.equal(requests[2].searchParams.get('api_key'), 'demo-key');
assert.equal(requests[3].searchParams.get('action'), 'getNumber');
assert.equal(requests[3].searchParams.get('maxPrice'), null);
assert.equal(requests[3].searchParams.get('fixedPrice'), null);
});
test('phone verification helper retries with HeroSMS getNumberV2 when getNumber reports NO_NUMBERS', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: '16' }),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'NO_NUMBERS',
};
}
if (action === 'getNumberV2') {
return {
ok: true,
text: async () => JSON.stringify({
activationId: '654321',
phoneNumber: '447911123456',
}),
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key', heroSmsCountryId: 16 }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 16,
});
assert.deepStrictEqual(activation, {
activationId: '654321',
phoneNumber: '447911123456',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 16,
successfulUses: 0,
maxUses: 3,
statusAction: 'getStatusV2',
});
assert.equal(requests.length, 3);
assert.equal(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[0].searchParams.get('country'), '16');
assert.equal(requests[1].searchParams.get('action'), 'getNumber');
assert.equal(requests[1].searchParams.get('country'), '16');
assert.equal(requests[1].searchParams.get('maxPrice'), '0.08');
assert.equal(requests[1].searchParams.get('fixedPrice'), 'true');
assert.equal(requests[2].searchParams.get('action'), 'getNumberV2');
assert.equal(requests[2].searchParams.get('country'), '16');
assert.equal(requests[2].searchParams.get('maxPrice'), '0.08');
assert.equal(requests[2].searchParams.get('fixedPrice'), 'true');
});
test('phone verification helper applies ordered fallback countries when primary country has no numbers', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const country = parsedUrl.searchParams.get('country');
if (action === 'getPrices') {
return {
ok: true,
text: async () => JSON.stringify({
[country]: {
dr: {
cost: 0.08,
count: 100,
},
},
}),
};
}
if (action === 'getNumber' || action === 'getNumberV2') {
if (country === '52') {
return { ok: true, text: async () => 'NO_NUMBERS' };
}
if (country === '16' && action === 'getNumber') {
return { ok: true, text: async () => 'ACCESS_NUMBER:861234:447955001122' };
}
}
throw new Error(`Unexpected HeroSMS action: ${action} @ country ${country}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key', heroSmsCountryId: 52 }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [{ id: 16, label: 'United Kingdom' }],
});
assert.equal(activation.countryId, 16);
assert.equal(activation.phoneNumber, '447955001122');
const actionTrace = requests.map((requestUrl) => `${requestUrl.searchParams.get('action')}:${requestUrl.searchParams.get('country')}`);
assert.deepStrictEqual(actionTrace, [
'getPrices:52',
'getNumber:52',
'getNumberV2:52',
'getPrices:16',
'getNumber:16',
]);
});
test('phone verification helper honors price-priority acquisition mode across selected countries', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const country = parsedUrl.searchParams.get('country');
if (action === 'getPrices') {
const cost = country === '52' ? 0.08 : 0.05;
return {
ok: true,
text: async () => JSON.stringify({
[country]: {
dr: {
cost,
count: 100,
},
},
}),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => `ACCESS_NUMBER:${country}001:44795500${country}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action} @ country ${country}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key', heroSmsCountryId: 52 }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [{ id: 16, label: 'United Kingdom' }],
heroSmsAcquirePriority: 'price',
});
assert.equal(activation.countryId, 16);
const actionTrace = requests.map((requestUrl) => `${requestUrl.searchParams.get('action')}:${requestUrl.searchParams.get('country')}`);
assert.deepStrictEqual(actionTrace, [
'getPrices:52',
'getPrices:16',
'getNumber:16',
]);
});
test('phone verification helper retries acquisition rounds when at least one country reports transient NO_NUMBERS', async () => {
const requests = [];
const logs = [];
const sleeps = [];
let thailandGetNumberCalls = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async (message, level) => {
logs.push({ message, level });
},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const country = parsedUrl.searchParams.get('country');
if (action === 'getPrices') {
if (country === '52') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: '52', cost: 0.05, count: 20 }),
};
}
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country, cost: 0.3, count: 20 }),
};
}
if (action === 'getNumber' || action === 'getNumberV2') {
if (country === '52') {
if (action === 'getNumber') {
thailandGetNumberCalls += 1;
if (thailandGetNumberCalls >= 2) {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:991122:66951112233',
};
}
}
return { ok: true, text: async () => 'NO_NUMBERS: Numbers Not Found. Try Later' };
}
return { ok: true, text: async () => 'NO_NUMBERS: Numbers Not Found. Try Later' };
}
throw new Error(`Unexpected HeroSMS action: ${action} @ country ${country}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async (ms) => {
sleeps.push(ms);
},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
heroSmsApiKey: 'demo-key',
heroSmsMaxPrice: '0.06',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [
{ id: 6, label: 'Canada' },
{ id: 5, label: 'Japan' },
],
// Simulate stale state value; helper should still perform at least 2 rounds.
heroSmsActivationRetryRounds: 1,
});
assert.equal(activation.countryId, 52);
assert.equal(activation.phoneNumber, '66951112233');
assert.equal(sleeps.length, 1);
assert.equal(sleeps[0], 2000);
assert.equal(
logs.filter((entry) => String(entry.message || '').includes('HeroSMS 正在获取手机号')).length >= 2,
true
);
assert.equal(
logs.some((entry) => String(entry.message || '').includes('HeroSMS 暂无可用号码(第 1/2 轮)')),
true
);
});
test('phone verification helper fails fast when HeroSMS country list is empty', async () => {
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async () => {
throw new Error('Unexpected fetch when no countries are configured.');
},
getState: async () => ({ heroSmsApiKey: 'demo-key', heroSmsCountryId: 0, heroSmsCountryFallback: [] }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await assert.rejects(
helpers.requestPhoneActivation({
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 0,
heroSmsCountryFallback: [],
}),
/HeroSMS countries are empty/i
);
});
test('phone verification helper uses HeroSMS getStatusV2 after acquiring a number via getNumberV2', async () => {
const requests = [];
const stateUpdates = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 16,
heroSmsCountryLabel: 'United Kingdom',
verificationResendCount: 0,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
let statusPollCount = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: '16' }),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'NO_NUMBERS',
};
}
if (action === 'getNumberV2') {
return {
ok: true,
text: async () => JSON.stringify({
activationId: '654321',
phoneNumber: '447911123456',
}),
};
}
if (action === 'getStatusV2') {
statusPollCount += 1;
return {
ok: true,
text: async () => (
statusPollCount === 1
? buildHeroSmsStatusV2Payload()
: buildHeroSmsStatusV2Payload({ smsCode: '112233', smsText: 'Your code is 112233' })
),
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => 'ACCESS_ACTIVATION',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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) => {
stateUpdates.push(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(Array.isArray(stateUpdates[0]?.heroSmsLastPriceTiers), true);
assert.equal(stateUpdates[0]?.heroSmsLastPriceCountryId, 16);
assert.equal(stateUpdates[0]?.heroSmsLastPriceCountryLabel, 'United Kingdom');
assert.equal(
stateUpdates.some((entry) => entry?.currentPhoneActivation?.activationId === '654321'),
true
);
assert.equal(
stateUpdates.some((entry) => entry?.currentPhoneVerificationCode === '112233'),
true
);
assert.equal(
stateUpdates.some((entry) => entry?.reusablePhoneActivation?.activationId === '654321'),
true
);
assert.equal(
stateUpdates.some((entry) => entry?.currentPhoneActivation === null && entry?.currentPhoneVerificationCode === ''),
true
);
const actions = requests.map((url) => url.searchParams.get('action'));
assert.deepStrictEqual(actions, [
'getPrices',
'getNumber',
'getNumberV2',
'getStatusV2',
'getStatusV2',
'setStatus',
]);
});
test('phone verification helper refreshes maxPrice when HeroSMS returns WRONG_MAX_PRICE', async () => {
const requests = [];
let getNumberAttempt = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
getNumberAttempt += 1;
return getNumberAttempt === 1
? {
ok: false,
text: async () => 'WRONG_MAX_PRICE:0.09',
}
: {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key' });
assert.deepStrictEqual(activation, {
activationId: '123456',
phoneNumber: '66959916439',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 0,
maxUses: 3,
});
assert.equal(requests.length, 3);
assert.equal(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[1].searchParams.get('action'), 'getNumber');
assert.equal(requests[1].searchParams.get('maxPrice'), '0.08');
assert.equal(requests[2].searchParams.get('action'), 'getNumber');
assert.equal(requests[2].searchParams.get('maxPrice'), '0.09');
assert.equal(requests[2].searchParams.get('fixedPrice'), 'true');
});
test('phone verification helper climbs price tiers when NO_NUMBERS is returned at lower prices', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const maxPrice = parsedUrl.searchParams.get('maxPrice');
if (action === 'getPrices') {
return {
ok: true,
text: async () => JSON.stringify({
52: {
dr: {
starter: { cost: 0.08, count: 100 },
premium: { cost: 0.12, count: 100 },
},
},
}),
};
}
if (action === 'getNumber' && maxPrice === '0.08') {
return {
ok: true,
text: async () => 'NO_NUMBERS',
};
}
if (action === 'getNumberV2' && maxPrice === '0.08') {
return {
ok: true,
text: async () => 'NO_NUMBERS',
};
}
if (action === 'getNumber' && maxPrice === '0.12') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:989898:66951112222',
};
}
throw new Error(`Unexpected HeroSMS action: ${action} @ ${maxPrice || 'no-price'}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key' });
assert.equal(activation.activationId, '989898');
const actions = requests.map((requestUrl) => `${requestUrl.searchParams.get('action')}:${requestUrl.searchParams.get('maxPrice') || ''}`);
assert.deepStrictEqual(actions, [
'getPrices:',
'getNumber:0.08',
'getNumberV2:0.08',
'getNumber:0.12',
]);
});
test('phone verification helper stops when WRONG_MAX_PRICE exceeds configured max price limit', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ cost: 0.08 }),
};
}
if (action === 'getNumber' || action === 'getNumberV2') {
return {
ok: false,
text: async () => 'WRONG_MAX_PRICE:0.08',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key', heroSmsMaxPrice: '0.05' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await assert.rejects(
helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key', heroSmsMaxPrice: '0.05' }),
/exceeds configured maxPrice=0\.05/i
);
const actions = requests.map((requestUrl) => `${requestUrl.searchParams.get('action')}:${requestUrl.searchParams.get('maxPrice') || ''}`);
assert.deepStrictEqual(actions, [
'getPrices:',
'getNumber:0.05',
'getNumberV2:0.05',
]);
});
test('phone verification helper falls back to plain getNumber when priced request fails to fetch', async () => {
const requests = [];
let getNumberAttempt = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
getNumberAttempt += 1;
if (getNumberAttempt === 1) {
throw new TypeError('Failed to fetch');
}
return {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({ heroSmsApiKey: 'demo-key' }),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({ heroSmsApiKey: 'demo-key' });
assert.deepStrictEqual(activation, {
activationId: '123456',
phoneNumber: '66959916439',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 0,
maxUses: 3,
});
assert.equal(requests.length, 3);
assert.equal(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[1].searchParams.get('action'), 'getNumber');
assert.equal(requests[1].searchParams.get('maxPrice'), '0.08');
assert.equal(requests[1].searchParams.get('fixedPrice'), 'true');
assert.equal(requests[2].searchParams.get('action'), 'getNumber');
assert.equal(requests[2].searchParams.get('maxPrice'), null);
assert.equal(requests[2].searchParams.get('fixedPrice'), null);
});
test('phone verification helper acquires a number from 5sim with fallback countries', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
requests.push({
pathname: parsedUrl.pathname,
search: parsedUrl.searchParams,
headers: options?.headers || {},
});
if (parsedUrl.pathname === '/v1/user/buy/activation/thailand/any/openai') {
return {
ok: false,
status: 400,
text: async () => JSON.stringify({ message: 'no free phones' }),
};
}
if (parsedUrl.pathname === '/v1/guest/prices') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
openai: {
thailand: {
any: {
cost: 0.08,
count: 12,
},
},
},
}),
};
}
if (parsedUrl.pathname === '/v1/user/buy/activation/england/any/openai') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: 9876543,
phone: '+447911123456',
country: 'england',
country_name: 'England',
product: 'openai',
}),
};
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand', 'england'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsMaxPrice: '0.1',
heroSmsReuseEnabled: true,
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand', 'england'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsMaxPrice: '0.1',
heroSmsReuseEnabled: true,
heroSmsActivationRetryRounds: 1,
});
assert.deepStrictEqual(activation, {
activationId: '9876543',
phoneNumber: '+447911123456',
provider: '5sim',
serviceCode: 'openai',
countryId: 'england',
countryCode: 'england',
countryLabel: 'England',
successfulUses: 0,
maxUses: 3,
});
assert.equal(requests.length, 4);
assert.equal(requests[0].pathname, '/v1/guest/prices');
assert.equal(requests[0].search.get('country'), 'thailand');
assert.equal(requests[0].search.get('product'), 'openai');
assert.equal(requests[1].pathname, '/v1/user/buy/activation/thailand/any/openai');
assert.equal(requests[1].search.get('maxPrice'), '0.08');
assert.equal(requests[1].search.get('reuse'), '1');
assert.equal(requests[1].headers.Authorization, 'Bearer five-token');
assert.equal(requests[2].pathname, '/v1/guest/prices');
assert.equal(requests[2].search.get('country'), 'england');
assert.equal(requests[2].search.get('product'), 'openai');
assert.equal(requests[3].pathname, '/v1/user/buy/activation/england/any/openai');
});
test('phone verification helper honors price-priority ordering for 5sim countries', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl.pathname + parsedUrl.search);
if (parsedUrl.pathname === '/v1/guest/prices') {
const country = parsedUrl.searchParams.get('country');
if (country === 'thailand') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
openai: {
thailand: { any: { cost: 0.2, count: 20 } },
},
}),
};
}
if (country === 'england') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
openai: {
england: { any: { cost: 0.05, count: 8 } },
},
}),
};
}
}
if (parsedUrl.pathname === '/v1/user/buy/activation/england/any/openai') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: 900001,
phone: '+447900100200',
country: 'england',
country_name: 'England',
product: 'openai',
}),
};
}
if (parsedUrl.pathname === '/v1/user/buy/activation/thailand/any/openai') {
return {
ok: false,
status: 400,
text: async () => JSON.stringify({ message: 'no free phones' }),
};
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand', 'england'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsAcquirePriority: 'price',
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand', 'england'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsAcquirePriority: 'price',
heroSmsActivationRetryRounds: 1,
});
assert.equal(activation.countryCode, 'england');
const firstBuyPath = requests.find((entry) => entry.includes('/v1/user/buy/activation'));
assert.equal(firstBuyPath?.startsWith('/v1/user/buy/activation/england/any/openai'), true);
});
test('phone verification helper tries multiple 5sim price tiers within the same country before fallback', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl.pathname + parsedUrl.search);
if (parsedUrl.pathname === '/v1/guest/prices') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
openai: {
thailand: {
any: {
low: { cost: 0.05, count: 3 },
mid: { cost: 0.08, count: 2 },
},
},
},
}),
};
}
if (parsedUrl.pathname === '/v1/user/buy/activation/thailand/any/openai') {
const maxPrice = parsedUrl.searchParams.get('maxPrice');
if (maxPrice === '0.05') {
return {
ok: false,
status: 400,
text: async () => JSON.stringify({ message: 'no free phones' }),
};
}
if (maxPrice === '0.08') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: 800001,
phone: '+66951112233',
country: 'thailand',
country_name: 'Thailand',
product: 'openai',
}),
};
}
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}${parsedUrl.search}`);
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsMaxPrice: '0.1',
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
heroSmsMaxPrice: '0.1',
heroSmsActivationRetryRounds: 1,
});
assert.equal(activation.countryCode, 'thailand');
assert.equal(activation.phoneNumber, '+66951112233');
const buyRequests = requests.filter((entry) => entry.startsWith('/v1/user/buy/activation/thailand/any/openai'));
assert.equal(buyRequests.length, 2);
assert.equal(buyRequests[0].includes('maxPrice=0.05'), true);
assert.equal(buyRequests[1].includes('maxPrice=0.08'), true);
});
test('phone verification helper polls and parses 5sim verification codes', async () => {
let checkCount = 0;
const statusUpdates = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
if (parsedUrl.pathname !== '/v1/user/check/600001') {
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
}
checkCount += 1;
if (checkCount === 1) {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: 'PENDING', sms: [] }),
};
}
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
status: 'RECEIVED',
sms: [{ text: 'Your OpenAI code is 246810' }],
}),
};
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const code = await helpers.pollPhoneActivationCode(
{
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['thailand'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
},
{
activationId: '600001',
phoneNumber: '+66900000000',
provider: '5sim',
serviceCode: 'openai',
countryId: 'thailand',
countryCode: 'thailand',
maxUses: 1,
successfulUses: 0,
},
{
timeoutMs: 5000,
intervalMs: 1,
maxRounds: 5,
onStatus: async (payload) => {
statusUpdates.push(payload.statusText);
},
}
);
assert.equal(code, '246810');
assert.equal(checkCount, 2);
assert.deepStrictEqual(statusUpdates, ['PENDING']);
});
test('phone verification helper treats HeroSMS STATUS_WAIT_RETRY payload status as pending', async () => {
let pollCount = 0;
const statusUpdates = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
const action = parsedUrl.searchParams.get('action');
if (action === 'getStatus') {
pollCount += 1;
if (pollCount === 1) {
return {
ok: true,
text: async () => 'STATUS_WAIT_RETRY:846171',
};
}
return {
ok: true,
text: async () => 'STATUS_OK:Your OpenAI code is 246810',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getState: async () => ({
phoneSmsProvider: 'hero-sms',
heroSmsApiKey: 'demo-key',
heroSmsCountry: 52,
heroSmsServiceCode: 'dr',
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const code = await helpers.pollPhoneActivationCode(
{
phoneSmsProvider: 'hero-sms',
heroSmsApiKey: 'demo-key',
heroSmsCountry: 52,
heroSmsServiceCode: 'dr',
heroSmsActivationRetryRounds: 1,
},
{
activationId: '123456',
phoneNumber: '66800000000',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 0,
maxUses: 1,
},
{
timeoutMs: 2000,
intervalMs: 1,
maxRounds: 5,
onStatus: async ({ statusText }) => {
statusUpdates.push(statusText);
},
}
);
assert.equal(code, '246810');
assert.equal(pollCount, 2);
assert.equal(statusUpdates[0], 'STATUS_WAIT_RETRY:846171');
});
test('phone verification helper reuses 5sim number via product-plus-number endpoint', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl.pathname);
if (parsedUrl.pathname !== '/v1/user/reuse/openai/447911123456') {
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
}
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: 700002,
phone: '+447911123456',
country: 'england',
country_name: 'England',
product: 'openai',
}),
};
},
getState: async () => ({
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['england'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const nextActivation = await helpers.reactivatePhoneActivation(
{
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['england'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
},
{
activationId: '600001',
phoneNumber: '+44 7911-123-456',
provider: '5sim',
serviceCode: 'openai',
countryId: 'england',
countryCode: 'england',
maxUses: 1,
successfulUses: 0,
}
);
assert.deepStrictEqual(nextActivation, {
activationId: '700002',
phoneNumber: '+447911123456',
provider: '5sim',
serviceCode: 'openai',
countryId: 'england',
countryCode: 'england',
countryLabel: 'England',
successfulUses: 0,
maxUses: 1,
});
assert.deepStrictEqual(requests, ['/v1/user/reuse/openai/447911123456']);
});
test('phone verification helper acquires a number from NexSMS with ordered fallback countries', async () => {
const requests = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
const method = String(options?.method || 'GET').toUpperCase();
const body = options?.body ? JSON.parse(options.body) : null;
requests.push({
pathname: parsedUrl.pathname,
search: parsedUrl.searchParams,
method,
body,
});
if (parsedUrl.pathname === '/api/getCountryByService') {
const countryId = Number(parsedUrl.searchParams.get('countryId'));
if (countryId === 1) {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
code: 0,
data: {
countryId: 1,
countryName: 'Ukraine',
minPrice: 0.06,
priceMap: { '0.06': 1 },
},
}),
};
}
if (countryId === 6) {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
code: 0,
data: {
countryId: 6,
countryName: 'Indonesia',
minPrice: 0.05,
priceMap: { '0.05': 2 },
},
}),
};
}
}
if (parsedUrl.pathname === '/api/order/purchase') {
if (body?.countryId === 1) {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
code: 1001,
msg: 'NO_NUMBERS',
}),
};
}
if (body?.countryId === 6) {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
code: 0,
data: {
countryId: 6,
countryName: 'Indonesia',
serviceCode: 'ot',
phoneNumbers: ['+6281234567890'],
},
}),
};
}
}
throw new Error(`Unexpected NexSMS request: ${parsedUrl.pathname}`);
},
getState: async () => ({
phoneSmsProvider: 'nexsms',
nexSmsApiKey: 'nex-key',
nexSmsCountryOrder: [1, 6],
nexSmsServiceCode: 'ot',
heroSmsActivationRetryRounds: 1,
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const activation = await helpers.requestPhoneActivation({
phoneSmsProvider: 'nexsms',
nexSmsApiKey: 'nex-key',
nexSmsCountryOrder: [1, 6],
nexSmsServiceCode: 'ot',
heroSmsActivationRetryRounds: 1,
});
assert.deepStrictEqual(activation, {
activationId: '+6281234567890',
phoneNumber: '+6281234567890',
provider: 'nexsms',
serviceCode: 'ot',
countryId: 6,
countryLabel: 'Indonesia',
successfulUses: 0,
maxUses: 1,
});
assert.equal(requests[0].pathname, '/api/getCountryByService');
assert.equal(requests[0].search.get('apiKey'), 'nex-key');
assert.equal(requests[0].search.get('serviceCode'), 'ot');
assert.equal(requests[0].search.get('countryId'), '1');
assert.equal(requests[1].pathname, '/api/order/purchase');
assert.equal(requests[1].method, 'POST');
assert.equal(requests[1].body?.countryId, 1);
assert.equal(requests[2].pathname, '/api/getCountryByService');
assert.equal(requests[2].search.get('countryId'), '6');
assert.equal(requests[3].pathname, '/api/order/purchase');
assert.equal(requests[3].body?.countryId, 6);
});
test('phone verification helper polls and parses NexSMS verification codes', async () => {
let pollCount = 0;
const statusUpdates = [];
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
assert.equal(parsedUrl.pathname, '/api/sms/messages');
assert.equal(parsedUrl.searchParams.get('apiKey'), 'nex-key');
assert.equal(parsedUrl.searchParams.get('phoneNumber'), '+6281234567890');
assert.equal(parsedUrl.searchParams.get('format'), 'json_latest');
pollCount += 1;
if (pollCount === 1) {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ code: 0, data: {} }),
};
}
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
code: 0,
data: {
code: '998877',
text: 'Your OpenAI code is 998877',
},
}),
};
},
getState: async () => ({
phoneSmsProvider: 'nexsms',
nexSmsApiKey: 'nex-key',
nexSmsCountryOrder: [6],
nexSmsServiceCode: 'ot',
}),
sendToContentScriptResilient: async () => ({}),
setState: async () => {},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const code = await helpers.pollPhoneActivationCode(
{
phoneSmsProvider: 'nexsms',
nexSmsApiKey: 'nex-key',
nexSmsCountryOrder: [6],
nexSmsServiceCode: 'ot',
},
{
activationId: '+6281234567890',
phoneNumber: '+6281234567890',
provider: 'nexsms',
serviceCode: 'ot',
countryId: 6,
countryLabel: 'Indonesia',
successfulUses: 0,
maxUses: 1,
},
{
timeoutMs: 5000,
intervalMs: 1,
maxRounds: 5,
onStatus: async (payload) => {
statusUpdates.push(payload.statusText);
},
}
);
assert.equal(code, '998877');
assert.equal(pollCount, 2);
assert.equal(statusUpdates.length >= 1, true);
});
test('phone verification helper completes add-phone flow, clears current activation, and stores reusable number state', async () => {
const requests = [];
const stateUpdates = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => 'ACCESS_ACTIVATION',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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) => {
stateUpdates.push(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(Array.isArray(stateUpdates[0]?.heroSmsLastPriceTiers), true);
assert.equal(stateUpdates[0]?.heroSmsLastPriceCountryId, 52);
assert.equal(stateUpdates[0]?.heroSmsLastPriceCountryLabel, 'Thailand');
assert.equal(
stateUpdates.some((entry) => entry?.currentPhoneActivation?.activationId === '123456'),
true
);
assert.equal(
stateUpdates.some((entry) => entry?.currentPhoneVerificationCode === '654321'),
true
);
assert.equal(
stateUpdates.some((entry) => entry?.reusablePhoneActivation?.activationId === '123456'),
true
);
assert.equal(
stateUpdates.some((entry) => entry?.currentPhoneActivation === null && entry?.currentPhoneVerificationCode === ''),
true
);
const actions = requests.map((url) => url.searchParams.get('action'));
assert.deepStrictEqual(actions, ['getPrices', 'getNumber', 'getStatus', 'setStatus']);
});
test('phone verification helper uses the configured HeroSMS country for both number acquisition and add-phone submission', async () => {
const requests = [];
const submittedPayloads = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 16,
heroSmsCountryLabel: 'United Kingdom',
verificationResendCount: 0,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: '16' }),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:654321:447911123456',
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:112233',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => 'ACCESS_ACTIVATION',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
submittedPayloads.push(message.payload);
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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(requests[0].searchParams.get('action'), 'getPrices');
assert.equal(requests[0].searchParams.get('country'), '16');
assert.equal(requests[1].searchParams.get('action'), 'getNumber');
assert.equal(requests[1].searchParams.get('country'), '16');
assert.equal(requests[1].searchParams.get('maxPrice'), '0.08');
assert.equal(requests[1].searchParams.get('fixedPrice'), 'true');
assert.deepStrictEqual(submittedPayloads, [{
phoneNumber: '447911123456',
countryId: 16,
countryLabel: 'United Kingdom',
}]);
});
test('phone verification helper skips reusable activation when reuse toggle is disabled', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsReuseEnabled: false,
verificationResendCount: 0,
currentPhoneActivation: null,
reusablePhoneActivation: {
activationId: 'reuse-001',
phoneNumber: '66950012345',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 0,
maxUses: 3,
},
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'reactivate') {
throw new Error('reactivate should not be called when reuse is disabled');
}
if (action === 'getPrices') {
return { ok: true, text: async () => buildHeroSmsPricesPayload() };
}
if (action === 'getNumber') {
return { ok: true, text: async () => 'ACCESS_NUMBER:900001:66958887777' };
}
if (action === 'getStatus') {
return { ok: true, text: async () => 'STATUS_OK:777111' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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.equal(result.success, true);
assert.equal(requests.some((requestUrl) => requestUrl.searchParams.get('action') === 'reactivate'), false);
assert.equal(currentState.reusablePhoneActivation, null);
});
test('phone verification helper replaces numbers in step 9 and stops after replacement limit when SMS never arrives', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 0,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const statusCallsById = {};
const realDateNow = Date.now;
let fakeNow = 0;
Date.now = () => fakeNow;
try {
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:123456:66959916439',
};
}
if (action === 'getStatus') {
statusCallsById[id] = (statusCallsById[id] || 0) + 1;
return {
ok: true,
text: async () => 'STATUS_WAIT_CODE',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => 'ACCESS_ACTIVATION',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
return {
resent: true,
url: 'https://auth.openai.com/phone-verification',
};
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {
fakeNow += 61000;
},
throwIfStopped: () => {},
});
await assert.rejects(
helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
}),
/更换 3 次号码后手机号验证仍未成功/
);
assert.ok(statusCallsById['123456'] >= 2, 'first number should be polled twice before being replaced');
assert.ok(messages.includes('SUBMIT_PHONE_NUMBER'));
assert.ok(messages.includes('RESEND_PHONE_VERIFICATION_CODE'));
assert.ok(messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER').length > 1);
const actions = requests.map((url) => `${url.searchParams.get('action')}:${url.searchParams.get('id') || ''}`);
assert.ok(actions.filter((action) => action === 'getNumber:').length > 1);
assert.ok(actions.filter((action) => action === 'getStatus:123456').length >= 2);
assert.ok(actions.filter((action) => action === 'setStatus:123456').length >= 2);
assert.equal(currentState.currentPhoneActivation, null);
} finally {
Date.now = realDateNow;
}
});
test('phone verification helper honors timeout-window and poll-round settings before replacing numbers', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 1,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return { ok: true, text: async () => buildHeroSmsPricesPayload() };
}
if (action === 'getNumber') {
return { ok: true, text: async () => 'ACCESS_NUMBER:500001:66957776666' };
}
if (action === 'getStatus') {
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
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 === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('resend should not be called when timeout windows is 1');
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await assert.rejects(
helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
}),
/更换 1 次号码后手机号验证仍未成功/
);
assert.equal(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), false);
assert.ok(
requests.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getStatus').length >= 2,
'each replacement attempt should still poll HeroSMS at least once'
);
});
test('phone verification helper respects configured number replacement limit', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
let submitCodeCount = 0;
const numbers = [
{ activationId: '411111', phoneNumber: '66950000111' },
{ activationId: '422222', phoneNumber: '66950000222' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
submitCodeCount += 1;
return {
invalidCode: true,
errorText: `This phone number is already linked to the maximum number of accounts. (${submitCodeCount})`,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
url: 'https://auth.openai.com/add-phone',
};
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await assert.rejects(
helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
}),
/更换 1 次号码后手机号验证仍未成功/
);
const actions = requests.map((requestUrl) => requestUrl.searchParams.get('action'));
assert.deepStrictEqual(actions, [
'getPrices',
'getNumber',
'getStatus',
'setStatus',
'getPrices',
'getNumber',
'getStatus',
'setStatus',
]);
});
test('phone verification helper reuses the current number first when code submission returns to add-phone', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '111111', phoneNumber: '66950000001' },
{ activationId: '222222', phoneNumber: '66950000002' },
];
let numberIndex = 0;
let submitCodeCount = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
submitCodeCount += 1;
return submitCodeCount === 1
? {
returnedToAddPhone: true,
url: 'https://auth.openai.com/add-phone',
}
: {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
return {
resent: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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.deepStrictEqual(messages, [
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
]);
const actions = requests.map((url) => `${url.searchParams.get('action')}:${url.searchParams.get('id') || ''}`);
assert.deepStrictEqual(actions, [
'getPrices:',
'getNumber:',
'getStatus:111111',
'getStatus:111111',
'setStatus:111111',
]);
assert.deepStrictEqual(currentState.currentPhoneActivation, null);
assert.deepStrictEqual(currentState.reusablePhoneActivation, {
activationId: '111111',
phoneNumber: '66950000001',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 1,
maxUses: 3,
});
});
test('phone verification helper immediately replaces number when page says the phone number was already used', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '311111', phoneNumber: '66950000011' },
{ activationId: '322222', phoneNumber: '66950000022' },
];
let numberIndex = 0;
let submitCodeCount = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
submitCodeCount += 1;
if (submitCodeCount === 1) {
return {
invalidCode: true,
errorText: 'This phone number is already linked to the maximum number of accounts.',
url: 'https://auth.openai.com/phone-verification',
};
}
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('should not resend for already-used number');
}
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.deepStrictEqual(messages, [
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
'RETURN_TO_ADD_PHONE',
'STEP8_GET_STATE',
'RETURN_TO_ADD_PHONE',
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
]);
});
test('phone verification helper treats phone_max_usage_exceeded as used-number and rotates immediately', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '711111', phoneNumber: '66950001011' },
{ activationId: '722222', phoneNumber: '66950001022' },
];
let numberIndex = 0;
let submitCodeCount = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
submitCodeCount += 1;
if (submitCodeCount === 1) {
return {
invalidCode: true,
errorText: 'An error occurred during authentication (phone_max_usage_exceeded). Please try again.',
url: 'https://auth.openai.com/phone-verification',
};
}
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
url: 'https://auth.openai.com/add-phone',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('should not resend for phone_max_usage_exceeded');
}
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.deepStrictEqual(messages, [
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
'RETURN_TO_ADD_PHONE',
'STEP8_GET_STATE',
'RETURN_TO_ADD_PHONE',
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
]);
});
test('phone verification helper rotates number when submitPhoneVerificationCode throws phone_max_usage_exceeded', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '811111', phoneNumber: '66950002011' },
{ activationId: '822222', phoneNumber: '66950002022' },
];
let numberIndex = 0;
let submitCodeCount = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload(),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
submitCodeCount += 1;
if (submitCodeCount === 1) {
return {
error: 'An error occurred during authentication (phone_max_usage_exceeded). Please try again.',
};
}
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE') {
return {
addPhonePage: true,
url: 'https://auth.openai.com/add-phone',
};
}
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.deepStrictEqual(messages, [
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
'RETURN_TO_ADD_PHONE',
'STEP8_GET_STATE',
'RETURN_TO_ADD_PHONE',
'SUBMIT_PHONE_NUMBER',
'SUBMIT_PHONE_VERIFICATION_CODE',
]);
});
test('phone verification helper replaces number when add-phone submission fails with country selection mismatch', async () => {
const requests = [];
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 16,
heroSmsCountryLabel: 'United Kingdom',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '611111', phoneNumber: '447999221823' },
{ activationId: '622222', phoneNumber: '447777000111' },
];
let numberIndex = 0;
let submitNumberCount = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: '16', cost: 0.09 }),
};
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}`,
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => `STATUS_UPDATED:${id}`,
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
submitNumberCount += 1;
if (submitNumberCount === 1) {
throw new Error('Failed to select "Country #16" on the add-phone page.');
}
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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(messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER').length, 2);
assert.equal(requests.filter((url) => url.searchParams.get('action') === 'getNumber').length, 2);
});
test('phone verification helper reuses the same number up to three successful registrations', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
verificationResendCount: 0,
currentPhoneActivation: null,
reusablePhoneActivation: {
activationId: '123456',
phoneNumber: '66959916439',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 52,
successfulUses: 2,
maxUses: 3,
},
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'reactivate') {
return {
ok: true,
text: async () => JSON.stringify({
activationId: '222333',
phoneNumber: '66959916439',
}),
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => 'STATUS_OK:654321',
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => 'ACCESS_ACTIVATION',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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(requests[0].searchParams.get('action'), 'reactivate');
assert.equal(requests[0].searchParams.get('id'), '123456');
assert.deepStrictEqual(currentState.reusablePhoneActivation, null);
});
test('phone verification helper keeps maxUses behavior for reused V2 activations', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 16,
heroSmsCountryLabel: 'United Kingdom',
verificationResendCount: 0,
currentPhoneActivation: null,
reusablePhoneActivation: {
activationId: '123456',
phoneNumber: '447911123456',
provider: 'hero-sms',
serviceCode: 'dr',
countryId: 16,
successfulUses: 2,
maxUses: 3,
statusAction: 'getStatusV2',
},
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'reactivate') {
return {
ok: true,
text: async () => JSON.stringify({
activationId: '222333',
phoneNumber: '447911123456',
}),
};
}
if (action === 'getStatusV2') {
return {
ok: true,
text: async () => buildHeroSmsStatusV2Payload({ smsCode: '654321', smsText: 'Your code is 654321' }),
};
}
if (action === 'setStatus') {
return {
ok: true,
text: async () => 'ACCESS_ACTIVATION',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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',
});
const actions = requests.map((url) => url.searchParams.get('action'));
assert.deepStrictEqual(actions, ['reactivate', 'getStatusV2', 'setStatus']);
assert.deepStrictEqual(currentState.reusablePhoneActivation, null);
});
test('phone verification helper replaces number immediately when resend is throttled and does not spam resend clicks', async () => {
const requests = [];
const messages = [];
let resendCalls = 0;
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 3,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '900001', phoneNumber: '66951110001' },
{ activationId: '900002', phoneNumber: '66951110002' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return { ok: true, text: async () => buildHeroSmsPricesPayload() };
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return { ok: true, text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}` };
}
if (action === 'getStatus') {
if (id === '900001') {
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
return { ok: true, text: async () => 'STATUS_OK:654321' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => `STATUS_UPDATED:${id}` };
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
resendCalls += 1;
throw new Error('PHONE_RESEND_THROTTLED::Tried to resend too many times. Please try again later.');
}
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(resendCalls, 1, 'resend should be attempted once for the number before replacement');
assert.equal(messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER').length, 2);
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), true);
});
test('phone verification helper replaces number immediately when phone-verification route is stuck on 405 retry page', async () => {
const requests = [];
const messages = [];
let resendCalls = 0;
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 2,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '910001', phoneNumber: '66952220001' },
{ activationId: '910002', phoneNumber: '66952220002' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'getPrices') {
return { ok: true, text: async () => buildHeroSmsPricesPayload() };
}
if (action === 'getNumber') {
const nextNumber = numbers[numberIndex];
numberIndex += 1;
return { ok: true, text: async () => `ACCESS_NUMBER:${nextNumber.activationId}:${nextNumber.phoneNumber}` };
}
if (action === 'getStatus') {
if (id === '910001') {
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
return { ok: true, text: async () => 'STATUS_OK:654321' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => `STATUS_UPDATED:${id}` };
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
resendCalls += 1;
throw new Error('PHONE_ROUTE_405_RECOVERY_FAILED::Phone verification route stayed on 405 after 3 retry click(s). 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(resendCalls, 1, 'resend should not loop endlessly on 405 route error');
assert.equal(messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER').length, 2);
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), true);
});
test('phone verification helper skips page resend for 5sim timeouts and rotates number directly', async () => {
const requests = [];
const messages = [];
let currentState = {
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['indonesia'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 2,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '500001', phoneNumber: '+628111111111' },
{ activationId: '500002', phoneNumber: '+628222222222' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl.pathname);
if (parsedUrl.pathname === '/v1/user/buy/activation/indonesia/any/openai') {
const next = numbers[Math.min(numberIndex, numbers.length - 1)];
numberIndex += 1;
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: next.activationId,
phone: next.phoneNumber,
country: 'indonesia',
country_name: 'Indonesia',
product: 'openai',
}),
};
}
if (parsedUrl.pathname === '/v1/user/check/500001') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
status: 'RECEIVED',
sms: [],
}),
};
}
if (parsedUrl.pathname === '/v1/user/check/500002') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
status: 'RECEIVED',
sms: [{ text: 'OpenAI code 123456' }],
}),
};
}
if (parsedUrl.pathname === '/v1/user/cancel/500001') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: 'CANCELED' }),
};
}
if (parsedUrl.pathname === '/v1/user/finish/500002') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: 'FINISHED' }),
};
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
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,
phoneVerificationPage: false,
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',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('5sim flow should not trigger page resend.');
}
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(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), false);
assert.equal(messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER').length, 2);
assert.equal(
requests.filter((pathname) => pathname === '/v1/user/check/500001').length,
2,
'first 5sim number should be polled across both timeout windows before replacement'
);
});
test('phone verification helper rotates number immediately when 5sim activation is missing (order not found)', async () => {
const requests = [];
let currentState = {
phoneSmsProvider: '5sim',
fiveSimApiKey: 'five-token',
fiveSimCountryOrder: ['indonesia'],
fiveSimOperator: 'any',
fiveSimProduct: 'openai',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 2,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 2,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const numbers = [
{ activationId: '510001', phoneNumber: '+628111111111' },
{ activationId: '510002', phoneNumber: '+628222222222' },
];
let numberIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl.pathname);
if (parsedUrl.pathname === '/v1/user/buy/activation/indonesia/any/openai') {
const next = numbers[Math.min(numberIndex, numbers.length - 1)];
numberIndex += 1;
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
id: next.activationId,
phone: next.phoneNumber,
country: 'indonesia',
country_name: 'Indonesia',
product: 'openai',
}),
};
}
if (parsedUrl.pathname === '/v1/user/check/510001') {
return {
ok: false,
status: 404,
text: async () => JSON.stringify({
status: 'error',
message: 'order not found',
}),
};
}
if (parsedUrl.pathname === '/v1/user/check/510002') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({
status: 'RECEIVED',
sms: [{ text: 'OpenAI code 123456' }],
}),
};
}
if (parsedUrl.pathname === '/v1/user/cancel/510001') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: 'CANCELED' }),
};
}
if (parsedUrl.pathname === '/v1/user/finish/510002') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: 'FINISHED' }),
};
}
throw new Error(`Unexpected 5sim request: ${parsedUrl.pathname}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
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,
phoneVerificationPage: false,
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',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('order-not-found path should replace number before page resend.');
}
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.ok(requests.includes('/v1/user/check/510001'));
assert.ok(requests.includes('/v1/user/check/510002'));
assert.equal(
requests.filter((pathname) => pathname === '/v1/user/check/510001').length,
1,
'missing activation should trigger immediate number replacement instead of repeated polling'
);
});
test('phone verification helper propagates stop errors instead of swallowing resend failures', async () => {
const messages = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 3,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 2,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return { ok: true, text: async () => buildHeroSmsPricesPayload() };
}
if (action === 'getNumber') {
return { ok: true, text: async () => 'ACCESS_NUMBER:700001:66955550001' };
}
if (action === 'getStatus') {
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RESEND_PHONE_VERIFICATION_CODE') {
throw new Error('流程已被用户停止。');
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
await assert.rejects(
helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
}),
/流程已被用户停止/
);
assert.equal(messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER').length, 1);
assert.equal(messages.filter((type) => type === 'RESEND_PHONE_VERIFICATION_CODE').length, 1);
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), false);
});
test('phone verification helper falls back to the next country after repeated sms timeout on the same country', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [{ id: 16, label: 'United Kingdom' }],
verificationResendCount: 0,
phoneVerificationReplacementLimit: 3,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
let thailandAcquireIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
const country = parsedUrl.searchParams.get('country');
if (action === 'getPrices') {
return {
ok: true,
text: async () => JSON.stringify({
[country]: {
dr: {
cost: country === '52' ? 0.08 : 0.09,
count: 100,
},
},
}),
};
}
if (action === 'getNumber') {
if (country === '52') {
thailandAcquireIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:52${thailandAcquireIndex}:66950000${thailandAcquireIndex}`,
};
}
if (country === '16') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:160001:447955001122',
};
}
}
if (action === 'getStatus') {
if (id === '160001') {
return { ok: true, text: async () => 'STATUS_OK:888999' };
}
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action} @ country ${country || 'n/a'}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
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,
phoneVerificationPage: false,
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',
});
const getNumberCountries = requests
.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getNumber')
.map((requestUrl) => requestUrl.searchParams.get('country'));
assert.deepStrictEqual(getNumberCountries, ['52', '16']);
});
test('phone verification helper escalates HeroSMS price tier in the same country after sms timeout before changing country', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [{ id: 16, label: 'United Kingdom' }],
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
let thailandAcquireIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
const country = parsedUrl.searchParams.get('country');
const maxPrice = parsedUrl.searchParams.get('maxPrice');
if (action === 'getPrices') {
if (country === '52') {
return {
ok: true,
text: async () => JSON.stringify({
52: {
dr: {
starter: { cost: 0.05, count: 10 },
premium: { cost: 0.08, count: 10 },
},
},
}),
};
}
return {
ok: true,
text: async () => JSON.stringify({
[country]: {
dr: {
cost: 0.09,
count: 10,
},
},
}),
};
}
if (action === 'getNumber') {
if (country === '52') {
if (maxPrice === '0.05') {
thailandAcquireIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:52low${thailandAcquireIndex}:66950000${thailandAcquireIndex}`,
};
}
if (maxPrice === '0.08') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:52high1:66958888888',
};
}
}
if (country === '16') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:160001:447955001122',
};
}
}
if (action === 'getStatus') {
if (id === '52high1') {
return { ok: true, text: async () => 'STATUS_OK:112233' };
}
if (id === '160001') {
return { ok: true, text: async () => 'STATUS_OK:223344' };
}
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action} @ country ${country || 'n/a'} @ maxPrice ${maxPrice || ''}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
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,
phoneVerificationPage: false,
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',
});
const getNumberEntries = requests
.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getNumber')
.map((requestUrl) => `${requestUrl.searchParams.get('country')}:${requestUrl.searchParams.get('maxPrice') || ''}`);
assert.deepStrictEqual(getNumberEntries, ['52:0.05', '52:0.08']);
});
test('phone verification helper parses currency-formatted HeroSMS tiers and retries higher tier in same country', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [{ id: 16, label: 'United Kingdom' }],
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
heroSmsMaxPrice: '0.12',
heroSmsAcquirePriority: 'price',
};
let thailandAcquireIndex = 0;
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
const country = parsedUrl.searchParams.get('country');
const maxPrice = parsedUrl.searchParams.get('maxPrice');
if (action === 'getPrices' || action === 'getPricesExtended') {
if (country === '52') {
return {
ok: true,
text: async () => JSON.stringify({
52: {
dr: {
'$0.1183': { cost: '$0.1183', count: 925 },
'$0.1036': { cost: '$0.1036', count: 35 },
'$0.0942': { cost: '$0.0942', count: 25 },
'$0.0500': { cost: '$0.0500', count: 10 },
},
},
}),
};
}
return {
ok: true,
text: async () => JSON.stringify({
[country]: {
dr: {
cost: 0.09,
count: 10,
},
},
}),
};
}
if (action === 'getNumber') {
if (country === '52') {
if (maxPrice === '0.05') {
thailandAcquireIndex += 1;
return {
ok: true,
text: async () => `ACCESS_NUMBER:52low${thailandAcquireIndex}:66950000${thailandAcquireIndex}`,
};
}
if (maxPrice === '0.0942') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:52mid1:66954443322',
};
}
}
if (country === '16') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:160001:447955001122',
};
}
}
if (action === 'getStatus') {
if (id === '52mid1') {
return { ok: true, text: async () => 'STATUS_OK:998877' };
}
if (id === '160001') {
return { ok: true, text: async () => 'STATUS_OK:223344' };
}
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action} @ country ${country || 'n/a'} @ maxPrice ${maxPrice || ''}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
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,
phoneVerificationPage: false,
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',
});
const getNumberEntries = requests
.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getNumber')
.map((requestUrl) => `${requestUrl.searchParams.get('country')}:${requestUrl.searchParams.get('maxPrice') || ''}`);
assert.deepStrictEqual(getNumberEntries, ['52:0.05', '52:0.0942']);
});
test('phone verification helper prefers manually selected activation before automatic reuse/new acquisition', async () => {
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 1,
phoneCodeWaitSeconds: 15,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
phonePreferredActivation: {
provider: 'hero-sms',
activationId: 'preferred-activation',
phoneNumber: '66951112233',
countryId: 52,
countryLabel: 'Thailand',
successfulUses: 0,
maxUses: 3,
},
reusablePhoneActivation: {
provider: 'hero-sms',
activationId: 'reusable-activation',
phoneNumber: '66959998888',
countryId: 52,
countryLabel: 'Thailand',
successfulUses: 0,
maxUses: 3,
},
currentPhoneActivation: null,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
if (action === 'reactivate' && id === 'preferred-activation') {
return { ok: true, text: async () => 'ACCESS_READY' };
}
if (action === 'getStatus' && id === 'preferred-activation') {
return { ok: true, text: async () => 'STATUS_OK:556677' };
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action} @ id ${id || 'n/a'}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
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',
});
const actionTrace = requests.map((requestUrl) => (
`${requestUrl.searchParams.get('action')}:${requestUrl.searchParams.get('id') || requestUrl.searchParams.get('country') || ''}`
));
assert.equal(actionTrace.includes('reactivate:preferred-activation'), true);
assert.equal(actionTrace.some((item) => item.startsWith('reactivate:reusable-activation')), false);
assert.equal(actionTrace.some((item) => item.startsWith('getNumber:')), false);
});
test('phone verification helper retries with a new number after preferred activation timeout and updates runtime countdown state', async () => {
const requests = [];
const stateUpdates = [];
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 15,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
phonePreferredActivation: {
provider: 'hero-sms',
activationId: 'preferred-activation',
phoneNumber: '66951112233',
countryId: 52,
countryLabel: 'Thailand',
successfulUses: 0,
maxUses: 3,
},
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');
const country = parsedUrl.searchParams.get('country');
if (action === 'reactivate' && id === 'preferred-activation') {
return { ok: true, text: async () => 'ACCESS_READY' };
}
if (action === 'getStatus') {
if (id === 'preferred-activation') {
return { ok: true, text: async () => 'STATUS_WAIT_CODE' };
}
if (id === 'new-activation') {
return { ok: true, text: async () => 'STATUS_OK:112233' };
}
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: country || '52', cost: 0.08, count: 100 }),
};
}
if (action === 'getNumber') {
return {
ok: true,
text: async () => 'ACCESS_NUMBER:new-activation:66950000123',
};
}
throw new Error(`Unexpected HeroSMS action: ${action} @ id ${id || 'n/a'}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
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,
phoneVerificationPage: false,
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) => {
stateUpdates.push(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',
});
const reactivatePreferredCalls = requests.filter((requestUrl) => (
requestUrl.searchParams.get('action') === 'reactivate'
&& requestUrl.searchParams.get('id') === 'preferred-activation'
));
assert.equal(reactivatePreferredCalls.length, 1);
const getNumberCalls = requests.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getNumber');
assert.equal(getNumberCalls.length, 1);
assert.equal(getNumberCalls[0].searchParams.get('country'), '52');
assert.equal(
stateUpdates.some((updates) => Number(updates.currentPhoneVerificationCountdownEndsAt) > 0),
true,
'should expose countdown window start in runtime state'
);
assert.equal(
stateUpdates.some((updates) => Number(updates.currentPhoneVerificationCountdownEndsAt) === 0),
true,
'should clear countdown window after step9 rotation/completion'
);
});
test('phone verification helper logs no-supply diagnostics with consecutive streak when all providers fail to acquire number', async () => {
const logs = [];
const requests = [];
let currentState = {
heroSmsApiKey: 'demo-key',
phoneSmsProvider: 'hero-sms',
phoneSmsProviderOrder: ['hero-sms'],
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
heroSmsCountryFallback: [],
heroSmsMaxPrice: '0.06',
currentPhoneActivation: null,
reusablePhoneActivation: null,
verificationResendCount: 0,
phoneVerificationReplacementLimit: 1,
};
const helpers = api.createPhoneVerificationHelpers({
addLog: async (message, level = 'info') => {
logs.push({ message, level });
},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
if (action === 'getPrices') {
return {
ok: true,
text: async () => buildHeroSmsPricesPayload({ country: '52', cost: 0.05, count: 20 }),
};
}
if (action === 'getNumber' || action === 'getNumberV2') {
return {
ok: true,
text: async () => 'NO_NUMBERS',
};
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});
const runOnce = async () => assert.rejects(
helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
}),
/all provider candidates failed to acquire number/i
);
await runOnce();
await runOnce();
const diagnosticsLogs = logs
.filter((entry) => String(entry.message || '').includes('Step 9 diagnostics: 无号连续失败'))
.map((entry) => String(entry.message || ''));
assert.equal(diagnosticsLogs.length >= 2, true);
assert.equal(diagnosticsLogs.some((message) => message.includes('无号连续失败 1 次')), true);
assert.equal(diagnosticsLogs.some((message) => message.includes('无号连续失败 2 次')), true);
assert.equal(
diagnosticsLogs.some((message) => message.includes('maxPrice=0.06')),
true
);
assert.equal(
diagnosticsLogs.some((message) => message.includes('国家数 HeroSMS=1, 5sim=0, NexSMS=0')),
true
);
assert.equal(currentState.phoneNoSupplyFailureStreak, 2);
assert.equal(requests.some((entry) => entry.searchParams.get('action') === 'getNumber'), true);
});
test('phone verification helper routes 5sim buy, check, and finish by current activation provider', async () => {
const requests = [];
let currentState = {
phoneSmsProvider: '5sim',
fiveSimApiKey: 'demo-key',
fiveSimCountryId: 'england',
fiveSimCountryLabel: 'England',
fiveSimMaxPrice: '12',
fiveSimOperator: 'any',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
createFiveSimProvider: fiveSimModule.createProvider,
fetchImpl: async (url, options = {}) => {
const parsedUrl = new URL(url);
requests.push({ url: parsedUrl, options });
if (parsedUrl.pathname === '/v1/guest/products/england/any') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ openai: { Category: 'activation', Qty: 3, Price: 9.5 } }),
};
}
if (parsedUrl.pathname === '/v1/guest/prices') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ england: { any: { openai: { cost: 9.5, count: 3 } } } }),
};
}
if (parsedUrl.pathname === '/v1/user/buy/activation/england/any/openai') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ id: 5001, phone: '+447911223344', country: 'england', operator: 'any', status: 'PENDING' }),
};
}
if (parsedUrl.pathname === '/v1/user/check/5001') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ id: 5001, phone: '+447911223344', status: 'RECEIVED', sms: [{ text: 'OpenAI code 123456' }] }),
};
}
if (parsedUrl.pathname === '/v1/user/finish/5001') {
return {
ok: true,
status: 200,
text: async () => JSON.stringify({ status: 'FINISHED' }),
};
}
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
}
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(currentState.currentPhoneActivation, null);
assert.equal(currentState.reusablePhoneActivation.provider, '5sim');
assert.equal(currentState.reusablePhoneActivation.activationId, '5001');
assert.equal(currentState.reusablePhoneActivation.successfulUses, 1);
const buy = requests.find((entry) => entry.url.pathname.includes('/buy/activation'));
assert.equal(buy.url.searchParams.get('maxPrice'), '12');
assert.equal(buy.url.searchParams.get('reuse'), '1');
assert.equal(buy.options.headers.Authorization, 'Bearer demo-key');
assert.deepStrictEqual(
requests.map((entry) => entry.url.pathname),
[
'/v1/guest/products/england/any',
'/v1/guest/prices',
'/v1/user/buy/activation/england/any/openai',
'/v1/user/check/5001',
'/v1/user/finish/5001',
]
);
});
test('phone verification helper routes 5sim reusable activation through reuse endpoint', async () => {
const requests = [];
let currentState = {
phoneSmsProvider: '5sim',
fiveSimApiKey: 'demo-key',
fiveSimCountryId: 'england',
fiveSimCountryLabel: 'England',
fiveSimOperator: 'any',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 60,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 1,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: {
activationId: '4001',
phoneNumber: '+447911223344',
provider: '5sim',
serviceCode: 'openai',
countryId: 'england',
countryLabel: 'England',
successfulUses: 1,
maxUses: 3,
},
};
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
createFiveSimProvider: fiveSimModule.createProvider,
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
if (parsedUrl.pathname === '/v1/user/reuse/openai/447911223344') {
return { ok: true, status: 200, text: async () => JSON.stringify({ id: 4002, phone: '+447911223344', country: 'england', status: 'PENDING' }) };
}
if (parsedUrl.pathname === '/v1/user/check/4002') {
return { ok: true, status: 200, text: async () => JSON.stringify({ id: 4002, phone: '+447911223344', status: 'RECEIVED', sms: [{ code: '654321' }] }) };
}
if (parsedUrl.pathname === '/v1/user/finish/4002') {
return { ok: true, status: 200, text: async () => JSON.stringify({ status: 'FINISHED' }) };
}
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
sendToContentScriptResilient: async (_source, message) => {
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
}
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(currentState.reusablePhoneActivation.activationId, '4002');
assert.equal(currentState.reusablePhoneActivation.successfulUses, 1);
assert.deepStrictEqual(
requests.map((url) => url.pathname),
[
'/v1/user/reuse/openai/447911223344',
'/v1/user/check/4002',
'/v1/user/finish/4002',
]
);
});