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 acquiring phone number')).length >= 2, true ); assert.equal( logs.some((entry) => String(entry.message || '').includes('HeroSMS has no available numbers (round 1/2); retrying')), true ); }); 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.deepStrictEqual(stateUpdates.slice(1), [ { currentPhoneActivation: { activationId: '654321', phoneNumber: '447911123456', provider: 'hero-sms', serviceCode: 'dr', countryId: 16, successfulUses: 0, maxUses: 3, statusAction: 'getStatusV2', }, currentPhoneVerificationCode: '', }, { currentPhoneVerificationCode: '112233', }, { reusablePhoneActivation: { activationId: '654321', phoneNumber: '447911123456', provider: 'hero-sms', serviceCode: 'dr', countryId: 16, successfulUses: 1, maxUses: 3, statusAction: 'getStatusV2', }, }, { currentPhoneActivation: null, currentPhoneVerificationCode: '', }, ]); 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 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.deepStrictEqual(stateUpdates.slice(1), [ { currentPhoneActivation: { activationId: '123456', phoneNumber: '66959916439', provider: 'hero-sms', serviceCode: 'dr', countryId: 52, successfulUses: 0, maxUses: 3, }, currentPhoneVerificationCode: '', }, { currentPhoneVerificationCode: '654321', }, { reusablePhoneActivation: { activationId: '123456', phoneNumber: '66959916439', provider: 'hero-sms', serviceCode: 'dr', countryId: 52, successfulUses: 1, maxUses: 3, }, }, { currentPhoneActivation: null, currentPhoneVerificationCode: '', }, ]); 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', }), /did not succeed after 3 number replacements/i ); 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', }), /did not succeed after 1 number replacements/i ); 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', }), /did not succeed after 1 number replacements/i ); 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', 'SUBMIT_PHONE_NUMBER', 'SUBMIT_PHONE_VERIFICATION_CODE', ]); }); 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 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', '52', '16']); });