Files
FlowPilot/tests/madao-provider.test.js
T
2026-05-29 00:44:30 +08:00

213 lines
6.6 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/madao.js', 'utf8');
const api = new Function('self', `${source}; return self.PhoneSmsMaDaoProvider;`)({});
function createJsonResponse(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('MaDao direct acquire sends only direct fields and normalizes activation data', async () => {
const requests = [];
const provider = api.createProvider({
fetchImpl: async (url, options = {}) => {
requests.push({ url: new URL(url), options, body: JSON.parse(options.body) });
return createJsonResponse({
ticket_id: 'ticket-1',
phone_number: '+441111111111',
service: 'openai',
country: 'gb',
provider: 'Upstream A!',
price: 0.123456,
status: 'code_received',
});
},
});
const activation = await provider.acquireActivation({
madaoBaseUrl: 'http://127.0.0.1:7822/',
madaoHttpSecret: 'token-1',
madaoMode: 'direct',
madaoRoutingPlanId: 'route-plan-should-not-be-sent',
madaoProviderId: 'Upstream A!',
madaoCountry: 'gb',
madaoAutoPickCountry: 'false',
madaoReusePhone: '1',
madaoMinPrice: '0.01',
madaoMaxPrice: '0.20',
});
assert.equal(requests.length, 1);
assert.equal(requests[0].url.toString(), 'http://127.0.0.1:7822/api/acquire');
assert.equal(requests[0].options.method, 'POST');
assert.equal(requests[0].options.headers.Authorization, 'Bearer token-1');
assert.deepEqual(requests[0].body, {
provider: 'upstreama',
service: 'openai',
auto_pick_country: false,
reuse_phone: true,
country: 'GB',
min_price: 0.01,
max_price: 0.2,
});
assert.equal(activation.provider, 'madao');
assert.equal(activation.activationId, 'ticket-1');
assert.equal(activation.phoneNumber, '+441111111111');
assert.equal(activation.countryId, 'GB');
assert.equal(activation.madaoProviderId, 'upstreama');
assert.equal(activation.madaoPrice, 0.1235);
assert.equal(activation.madaoStatus, 'code_received');
});
test('MaDao routing acquire sends routing plan only', async () => {
const requests = [];
const provider = api.createProvider({
fetchImpl: async (url, options = {}) => {
requests.push({ url: new URL(url), body: JSON.parse(options.body) });
return createJsonResponse({
ticket_id: 'ticket-2',
phone_number: '+442222222222',
routing_plan_id: 'rp-openai',
routing_item_id: 'route-1',
});
},
});
const activation = await provider.acquireActivation({
madaoMode: 'routing_plan',
madaoRoutingPlanId: 'rp-openai',
madaoProviderId: 'direct-provider',
madaoCountry: 'TH',
madaoAutoPickCountry: false,
madaoReusePhone: false,
madaoMinPrice: '0.01',
madaoMaxPrice: '0.20',
});
assert.deepEqual(requests[0].body, {
provider: 'auto',
service: 'openai',
routing_plan_id: 'rp-openai',
});
assert.equal(activation.madaoRoutingPlanId, 'rp-openai');
assert.equal(activation.madaoRoutingItemId, 'route-1');
});
test('MaDao poll extracts codes from nested messages and reports pending status', async () => {
const statusEvents = [];
const provider = api.createProvider({
fetchImpl: async (_url, options = {}) => {
const body = JSON.parse(options.body);
if (body.ticket_id === 'pending-ticket') {
return createJsonResponse({ status: 'waiting_code', message: 'still waiting' });
}
return createJsonResponse({
messages: [
{ text: 'old text without code' },
{ body: 'OpenAI verification code: 765432' },
],
});
},
});
const pending = await provider.pollActivationCode({}, { activationId: 'pending-ticket' }, {
onStatus: async (event) => statusEvents.push(event),
});
const code = await provider.pollActivationCode({}, { activationId: 'ready-ticket' });
assert.equal(pending, '');
assert.equal(statusEvents.length, 1);
assert.equal(statusEvents[0].statusText, 'still waiting');
assert.equal(code, '765432');
});
test('MaDao direct rotate releases the current ticket without acquiring a new one', async () => {
const requests = [];
const provider = api.createProvider({
fetchImpl: async (url, options = {}) => {
requests.push({ url: new URL(url), body: JSON.parse(options.body) });
return createJsonResponse({ released: true });
},
});
const result = await provider.rotateActivation(
{ madaoMode: 'direct' },
{ activationId: 'ticket-old', phoneNumber: '+441111111111' },
{ releaseAction: 'ban' }
);
assert.deepEqual(
requests.map((request) => request.url.pathname),
['/api/release']
);
assert.deepEqual(requests[0].body, { ticket_id: 'ticket-old', action: 'ban' });
assert.equal(result.currentTicketId, 'ticket-old');
assert.equal(result.nextActivation, null);
});
test('MaDao routing rotate rejects invalid replacement payloads', async () => {
const provider = api.createProvider({
fetchImpl: async () => createJsonResponse({
current_ticket_id: 'ticket-old',
next_ticket: {
ticket_id: 'ticket-new',
},
}),
});
await assert.rejects(
() => provider.rotateActivation(
{ madaoMode: 'routing_plan' },
{
activationId: 'ticket-old',
phoneNumber: '+441111111111',
madaoRoutingPlanId: 'rp-openai',
madaoRoutingItemId: 'route-old',
},
{ releaseAction: 'ban', reason: 'phone_number_used' }
),
/MaDao/
);
});
test('MaDao provider surfaces HTTP errors and request timeouts', async () => {
const httpProvider = api.createProvider({
fetchImpl: async () => createJsonResponse({ error: 'upstream unavailable' }, false, 503, 'Service Unavailable'),
});
await assert.rejects(
() => httpProvider.acquireActivation({}),
(error) => {
assert.equal(error.status, 503);
assert.equal(error.payload.error, 'upstream unavailable');
assert.match(error.message, /upstream unavailable/);
return true;
}
);
const timeoutProvider = api.createProvider({
fetchImpl: async (_url, options = {}) => {
await new Promise((resolve, reject) => {
options.signal.addEventListener('abort', () => {
const error = new Error('aborted');
error.name = 'AbortError';
reject(error);
});
});
},
requestTimeoutMs: 1,
});
await assert.rejects(
() => timeoutProvider.acquireActivation({}),
/MaDao/
);
});