196 lines
7.5 KiB
JavaScript
196 lines
7.5 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
const source = fs.readFileSync('phone-sms/providers/sms-bower.js', 'utf8');
|
|
const api = new Function('self', `${source}; return self.PhoneSmsBowerProvider;`)({});
|
|
|
|
function createTextResponse(payload, ok = true, status = ok ? 200 : 400, statusText = ok ? 'OK' : 'Bad Request') {
|
|
return {
|
|
ok,
|
|
status,
|
|
statusText,
|
|
text: async () => (typeof payload === 'string' ? payload : JSON.stringify(payload)),
|
|
};
|
|
}
|
|
|
|
test('SMS Bower provider defaults to Indonesia and documented OpenAI service code', async () => {
|
|
const requests = [];
|
|
const provider = api.createProvider({
|
|
fetchImpl: async (url, options = {}) => {
|
|
const parsed = new URL(url);
|
|
requests.push({ url: parsed, options });
|
|
if (parsed.searchParams.get('action') === 'getNumberV2') {
|
|
return createTextResponse('ACCESS_NUMBER:67890:6281234567890');
|
|
}
|
|
throw new Error(`unexpected ${parsed.toString()}`);
|
|
},
|
|
});
|
|
|
|
const activation = await provider.requestActivation({ smsBowerApiKey: 'demo-key' });
|
|
|
|
assert.equal(requests[0].url.searchParams.get('country'), '6');
|
|
assert.equal(requests[0].url.searchParams.get('service'), 'dr');
|
|
assert.equal(activation.countryId, 6);
|
|
assert.equal(activation.countryLabel, 'Indonesia');
|
|
});
|
|
|
|
test('SMS Bower provider uses documented handler API methods for balance and price catalog', async () => {
|
|
const requests = [];
|
|
const provider = api.createProvider({
|
|
fetchImpl: async (url, options = {}) => {
|
|
const parsed = new URL(url);
|
|
requests.push({ url: parsed, options });
|
|
if (parsed.searchParams.get('action') === 'getBalance') {
|
|
return createTextResponse('ACCESS_BALANCE:12.34');
|
|
}
|
|
if (parsed.searchParams.get('action') === 'getPricesV3') {
|
|
return createTextResponse({ 52: { dr: { 3170: { price: 0.21, count: 7 } } } });
|
|
}
|
|
throw new Error(`unexpected ${parsed.toString()}`);
|
|
},
|
|
});
|
|
|
|
const balance = await provider.fetchBalance({ smsBowerApiKey: 'demo-key' });
|
|
const prices = await provider.fetchPrices({ smsBowerApiKey: 'demo-key', smsBowerCountryId: 52 });
|
|
const entries = provider.collectPriceEntries(prices, []);
|
|
|
|
assert.equal(requests[0].url.origin + requests[0].url.pathname, 'https://smsbower.page/stubs/handler_api.php');
|
|
assert.equal(requests[0].url.searchParams.get('api_key'), 'demo-key');
|
|
assert.equal(requests[0].url.searchParams.get('action'), 'getBalance');
|
|
assert.equal(balance.balance, 12.34);
|
|
assert.equal(requests[1].url.searchParams.get('action'), 'getPricesV3');
|
|
assert.equal(requests[1].url.searchParams.get('service'), 'dr');
|
|
assert.equal(requests[1].url.searchParams.get('country'), '52');
|
|
assert.deepStrictEqual(entries, [{ cost: 0.21, count: 7, inStock: true }]);
|
|
});
|
|
|
|
test('SMS Bower provider treats JSON status 0 responses as API errors even when HTTP is ok', async () => {
|
|
const provider = api.createProvider({
|
|
fetchImpl: async () => createTextResponse({ status: 0, message: 'No access', data: [] }, true, 200),
|
|
});
|
|
|
|
await assert.rejects(
|
|
() => provider.fetchBalance({ smsBowerApiKey: 'bad-key' }),
|
|
/SMS Bower 查询余额失败:No access/
|
|
);
|
|
});
|
|
|
|
test('SMS Bower provider acquires number with getNumberV2, polls code, and updates activation statuses', async () => {
|
|
const requests = [];
|
|
const provider = api.createProvider({
|
|
fetchImpl: async (url, options = {}) => {
|
|
const parsed = new URL(url);
|
|
const action = parsed.searchParams.get('action');
|
|
requests.push({ url: parsed, options });
|
|
if (action === 'getNumberV2') {
|
|
return createTextResponse({ activationId: '12345', phoneNumber: '447700900123', activationCost: 0.3, countryCode: 16 });
|
|
}
|
|
if (action === 'getStatus') {
|
|
return createTextResponse('STATUS_OK:Your OpenAI code is 654321');
|
|
}
|
|
if (action === 'setStatus') {
|
|
return createTextResponse(parsed.searchParams.get('status') === '6' ? 'ACCESS_ACTIVATION' : 'ACCESS_CANCEL');
|
|
}
|
|
throw new Error(`unexpected ${parsed.toString()}`);
|
|
},
|
|
sleepWithStop: async () => {},
|
|
throwIfStopped: () => {},
|
|
});
|
|
|
|
const state = {
|
|
smsBowerApiKey: 'demo-key',
|
|
smsBowerServiceCode: 'dr',
|
|
smsBowerCountryId: 16,
|
|
smsBowerCountryLabel: 'United Kingdom',
|
|
smsBowerMinPrice: '0.1',
|
|
smsBowerMaxPrice: '0.3',
|
|
};
|
|
|
|
const activation = await provider.requestActivation(state);
|
|
const code = await provider.pollActivationCode(state, activation, { timeoutMs: 1000, intervalMs: 1, maxRounds: 1 });
|
|
await provider.finishActivation(state, activation);
|
|
await provider.cancelActivation(state, activation);
|
|
await provider.banActivation(state, activation);
|
|
const reused = await provider.reuseActivation(state, activation);
|
|
|
|
assert.equal(activation.provider, 'sms-bower');
|
|
assert.equal(activation.activationId, '12345');
|
|
assert.equal(activation.phoneNumber, '447700900123');
|
|
assert.equal(activation.countryId, 16);
|
|
assert.equal(code, '654321');
|
|
assert.equal(reused.activationId, '12345');
|
|
|
|
const acquireUrl = requests[0].url;
|
|
assert.equal(acquireUrl.searchParams.get('action'), 'getNumberV2');
|
|
assert.equal(acquireUrl.searchParams.get('service'), 'dr');
|
|
assert.equal(acquireUrl.searchParams.get('country'), '16');
|
|
assert.equal(acquireUrl.searchParams.get('maxPrice'), '0.3');
|
|
assert.equal(acquireUrl.searchParams.get('minPrice'), '0.1');
|
|
assert.equal(acquireUrl.searchParams.get('providerIds'), null);
|
|
assert.equal(acquireUrl.searchParams.get('exceptProviderIds'), null);
|
|
assert.equal(acquireUrl.searchParams.get('phoneException'), null);
|
|
assert.deepStrictEqual(
|
|
requests.map((entry) => [entry.url.searchParams.get('action'), entry.url.searchParams.get('status')]),
|
|
[
|
|
['getNumberV2', null],
|
|
['getStatus', null],
|
|
['setStatus', '6'],
|
|
['setStatus', '8'],
|
|
['setStatus', '8'],
|
|
['setStatus', '3'],
|
|
]
|
|
);
|
|
});
|
|
|
|
|
|
test('SMS Bower rotate cancels rejected number then immediately acquires the next number', async () => {
|
|
const requests = [];
|
|
const provider = api.createProvider({
|
|
fetchImpl: async (url, options = {}) => {
|
|
const parsed = new URL(url);
|
|
const action = parsed.searchParams.get('action');
|
|
requests.push({ url: parsed, options });
|
|
if (action === 'setStatus') {
|
|
return createTextResponse('ACCESS_CANCEL');
|
|
}
|
|
if (action === 'getNumberV2') {
|
|
return createTextResponse({ activationId: 'next-activation', phoneNumber: '447700900456', activationCost: 0.22, countryCode: 16 });
|
|
}
|
|
throw new Error(`unexpected ${parsed.toString()}`);
|
|
},
|
|
sleepWithStop: async () => {},
|
|
throwIfStopped: () => {},
|
|
});
|
|
|
|
const state = {
|
|
smsBowerApiKey: 'demo-key',
|
|
smsBowerServiceCode: 'dr',
|
|
smsBowerCountryId: 16,
|
|
smsBowerCountryLabel: 'United Kingdom',
|
|
smsBowerMinPrice: '0.1',
|
|
smsBowerMaxPrice: '0.3',
|
|
};
|
|
|
|
const rotated = await provider.rotateActivation(
|
|
state,
|
|
{ activationId: 'old-activation', phoneNumber: '447700900123', provider: 'sms-bower', countryId: 16 },
|
|
{ releaseAction: 'cancel', blockedCountryIds: [] }
|
|
);
|
|
|
|
assert.equal(rotated.currentTicketId, 'old-activation');
|
|
assert.equal(rotated.nextActivation.activationId, 'next-activation');
|
|
assert.equal(rotated.nextActivation.phoneNumber, '447700900456');
|
|
assert.deepStrictEqual(
|
|
requests.map((entry) => [
|
|
entry.url.searchParams.get('action'),
|
|
entry.url.searchParams.get('id'),
|
|
entry.url.searchParams.get('status'),
|
|
]),
|
|
[
|
|
['setStatus', 'old-activation', '8'],
|
|
['getNumberV2', null, null],
|
|
]
|
|
);
|
|
});
|