const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const source = fs.readFileSync('background.js', 'utf8'); function load(extra = {}) { const sandbox = { chrome: { action: { onClicked: { addListener() {} } }, tabs: { async sendMessage() {} }, scripting: { async executeScript() {} }, runtime: { onMessage: { addListener() {} } }, storage: { local: { async get() { return {}; }, async set() {} } } }, fetch: async () => { throw new Error('not mocked'); }, URL, console, Date, Number, String, Object, Array, RegExp, Math, JSON, Set, ...extra, }; const fn = new Function('globalThis', 'self', `with (globalThis) { ${source}; return { collectPriceEntries, filterPriceEntries, normalizeActivation, extractVerificationCode, isPhoneNumberUsedError, isPhoneNumberDeliveryRefusedError, getPhoneReplacementReleaseAction, fetchNextPhone, normalizeSettings, buildUrl, fetchActiveCode }; }`); return fn(sandbox, sandbox); } test('collectPriceEntries parses keyed price inventory', () => { const api = load(); const entries = api.collectPriceEntries({ 6: { dr: { '0.12': { count: 3 }, '0.20': { count: 0 } } } }); assert.deepEqual(entries.filter((x) => x.inStock).map((x) => x.cost), [0.12]); }); test('filterPriceEntries respects min and max', () => { const api = load(); const entries = [{ cost: 0.05, inStock: true }, { cost: 0.15, inStock: true }, { cost: 0.3, inStock: true }]; assert.deepEqual(api.filterPriceEntries(entries, { minPrice: '0.1', maxPrice: '0.2' }).map((x) => x.cost), [0.15]); }); test('normalizeActivation parses ACCESS_NUMBER response', () => { const api = load(); const activation = api.normalizeActivation('ACCESS_NUMBER:12345:628123456789', { countryId: 6, countryLabel: 'Indonesia' }); assert.equal(activation.activationId, '12345'); assert.equal(activation.phoneNumber, '628123456789'); assert.equal(activation.countryLabel, 'Indonesia'); }); test('extractVerificationCode parses STATUS_OK response', () => { const api = load(); assert.equal(api.extractVerificationCode('STATUS_OK: Your code is 654321'), '654321'); assert.equal(api.extractVerificationCode('STATUS_OK:987654'), '987654'); }); test('FlowPilot-style phone failure classifiers choose release action', () => { const api = load(); assert.equal(api.isPhoneNumberUsedError('该电话已被使用,请换一个号码'), true); assert.equal(api.isPhoneNumberUsedError('手机号已绑定,请换一个号码'), true); assert.equal(api.isPhoneNumberDeliveryRefusedError('无法向此电话号码发送验证码'), true); assert.equal(api.getPhoneReplacementReleaseAction('phone_number_used'), 'ban'); assert.equal(api.getPhoneReplacementReleaseAction('phone_delivery_refused'), 'cancel'); }); test('buildUrl keeps api_key and replacement queries explicit', () => { const api = load(); const url = new URL(api.buildUrl( { baseUrl: 'https://smsbower.app/stubs/handler_api.php', apiKey: 'demo-key' }, { action: 'getNumberV2', service: 'dr', country: 6, maxPrice: 0.2 } )); assert.equal(url.searchParams.get('api_key'), 'demo-key'); assert.equal(url.searchParams.get('action'), 'getNumberV2'); assert.equal(url.searchParams.get('service'), 'dr'); assert.equal(url.searchParams.get('country'), '6'); assert.equal(url.searchParams.get('maxPrice'), '0.2'); }); test('fetchNextPhone releases previous activation then acquires replacement', async () => { const requests = []; const stored = { apiKey: 'demo-key', baseUrl: 'https://smsbower.app/stubs/handler_api.php', serviceCode: 'dr', minPrice: '0.1', maxPrice: '0.3', countries: [{ id: 6, label: 'Indonesia' }], activeActivation: { activationId: 'old-activation', phoneNumber: '628111', countryId: 6 }, }; const api = load({ chrome: { action: { onClicked: { addListener() {} } }, tabs: { async sendMessage() {} }, scripting: { async executeScript() {} }, runtime: { onMessage: { addListener() {} } }, storage: { local: { async get() { return stored; }, async set(update) { Object.assign(stored, update); } } } }, fetch: async (url) => { const parsed = new URL(url); const action = parsed.searchParams.get('action'); requests.push([action, parsed.searchParams.get('id'), parsed.searchParams.get('status'), parsed.searchParams.get('country')]); if (action === 'setStatus') return new Response('ACCESS_CANCEL', { status: 200 }); if (action === 'getPricesV3') return new Response(JSON.stringify({ 6: { dr: { '0.2': { count: 2 } } } }), { status: 200 }); if (action === 'getNumberV2') return new Response(JSON.stringify({ activationId: 'new-activation', phoneNumber: '628222', countryCode: 6 }), { status: 200 }); throw new Error(`unexpected ${url}`); }, Response, }); const result = await api.fetchNextPhone({ reason: 'phone_number_used', releaseAction: 'ban' }); assert.equal(result.activation.activationId, 'new-activation'); assert.equal(result.releasePrevious.releaseAction, 'ban'); assert.equal(stored.activeActivation.previousActivationId, 'old-activation'); assert.deepEqual(requests, [ ['setStatus', 'old-activation', '8', null], ['getPricesV3', null, null, '6'], ['getNumberV2', null, null, '6'], ]); }); test('buildUrl uses Luban apikey parameter and base endpoint', () => { const api = load(); const url = new URL(api.buildUrl( { provider: 'luban-sms', lubanBaseUrl: 'https://lubansms.com/v2/api/getNumber', apiKey: 'luban-key' }, { service_id: '121949' } )); assert.equal(url.searchParams.get('apikey'), 'luban-key'); assert.equal(url.searchParams.get('api_key'), null); assert.equal(url.searchParams.get('service_id'), '121949'); }); test('normalizeActivation parses Luban getNumber response', () => { const api = load(); const activation = api.normalizeActivation( { code: 0, msg: '', number: '79781901206', request_id: 230698 }, { provider: 'luban-sms', serviceCode: '121949' } ); assert.equal(activation.provider, 'luban-sms'); assert.equal(activation.activationId, '230698'); assert.equal(activation.phoneNumber, '79781901206'); assert.equal(activation.serviceCode, '121949'); }); test('fetchNextPhone acquires Luban number and rejects previous request', async () => { const requests = []; const stored = { provider: 'luban-sms', apiKey: 'luban-key', lubanBaseUrl: 'https://lubansms.com/v2/api/getNumber', lubanServiceId: '121949', activeActivation: { activationId: 'old-request', phoneNumber: '79780000000', provider: 'luban-sms' }, }; const api = load({ chrome: { action: { onClicked: { addListener() {} } }, tabs: { async sendMessage() {} }, scripting: { async executeScript() {} }, runtime: { onMessage: { addListener() {} } }, storage: { local: { async get() { return stored; }, async set(update) { Object.assign(stored, update); } } } }, fetch: async (url) => { const parsed = new URL(url); requests.push([parsed.pathname, parsed.searchParams.get('apikey'), parsed.searchParams.get('request_id'), parsed.searchParams.get('status'), parsed.searchParams.get('service_id')]); if (parsed.pathname.endsWith('/setStatus')) return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200 }); if (parsed.pathname.endsWith('/List')) return new Response(JSON.stringify({ code: 0, msg: [] }), { status: 200 }); if (parsed.pathname.endsWith('/getNumber')) return new Response(JSON.stringify({ code: 0, msg: '', number: '79781901206', request_id: 230698 }), { status: 200 }); throw new Error(`unexpected ${url}`); }, Response, }); const result = await api.fetchNextPhone({ reason: 'manual_replace', releaseAction: 'cancel' }); assert.equal(result.activation.provider, 'luban-sms'); assert.equal(result.activation.activationId, '230698'); assert.equal(stored.activeActivation.previousActivationId, 'old-request'); assert.deepEqual(requests, [ ['/v2/api/setStatus', 'luban-key', 'old-request', 'reject', null], ['/v2/api/List', 'luban-key', null, null, null], ['/v2/api/getNumber', 'luban-key', null, null, '121949'], ]); }); test('fetchActiveCode parses Luban success SMS code', async () => { const stored = { provider: 'luban-sms', apiKey: 'luban-key', lubanBaseUrl: 'https://lubansms.com/v2/api/getSms', activeActivation: { activationId: '230698', phoneNumber: '79781901206', provider: 'luban-sms' }, }; const api = load({ chrome: { action: { onClicked: { addListener() {} } }, tabs: { async sendMessage() {} }, scripting: { async executeScript() {} }, runtime: { onMessage: { addListener() {} } }, storage: { local: { async get() { return stored; }, async set(update) { Object.assign(stored, update); } } } }, fetch: async (url) => { const parsed = new URL(url); assert.equal(parsed.pathname, '/v2/api/getSms'); assert.equal(parsed.searchParams.get('request_id'), '230698'); return new Response(JSON.stringify({ code: 0, msg: 'success', sms_code: '380682' }), { status: 200 }); }, Response, }); const result = await api.fetchActiveCode(); assert.equal(result.code, '380682'); assert.equal(stored.activeActivation.lastCode, '380682'); });