collabSupport/tests/callTest.signature.test.js
Joseph McQueen 7aa8c37d1d Add Twilio /calltest for store and direct-dial voice path testing.
Enables outbound PSTN probes via TwiML webhooks with Webex result cards, status polling, and optional store CDR enrichment.
2026-07-23 17:59:10 -04:00

97 lines
3.8 KiB
JavaScript

// tests/callTest.signature.test.js
import test from 'node:test';
import assert from 'node:assert/strict';
import crypto from 'node:crypto';
import twilio from 'twilio';
import { validateTwilioSignature } from '../integrations/twilio/client.js';
import { resolveCallTestWebhookUrl } from '../services/callTest/config.js';
test('validateTwilioSignature matches Twilio HMAC algorithm', () => {
const authToken = 'test-auth-token';
const url = 'https://example.com/twilio/calltest/status?testId=abc';
const params = { CallStatus: 'completed', CallSid: 'CA123' };
let data = url;
for (const key of Object.keys(params).sort()) {
data += key + params[key];
}
const signature = crypto.createHmac('sha1', authToken).update(data, 'utf-8').digest('base64');
const prevSid = process.env.TWILIO_ACCOUNT_SID;
const prevToken = process.env.TWILIO_AUTH_TOKEN;
const prevFrom = process.env.TWILIO_FROM_NUMBER;
const prevBase = process.env.TWILIO_WEBHOOK_BASE_URL;
process.env.TWILIO_ACCOUNT_SID = 'ACtest';
process.env.TWILIO_AUTH_TOKEN = authToken;
process.env.TWILIO_FROM_NUMBER = '+15550001111';
process.env.TWILIO_WEBHOOK_BASE_URL = 'https://example.com';
try {
assert.equal(validateTwilioSignature(signature, url, params), true);
assert.equal(validateTwilioSignature('bad', url, params), false);
} finally {
if (prevSid == null) delete process.env.TWILIO_ACCOUNT_SID;
else process.env.TWILIO_ACCOUNT_SID = prevSid;
if (prevToken == null) delete process.env.TWILIO_AUTH_TOKEN;
else process.env.TWILIO_AUTH_TOKEN = prevToken;
if (prevFrom == null) delete process.env.TWILIO_FROM_NUMBER;
else process.env.TWILIO_FROM_NUMBER = prevFrom;
if (prevBase == null) delete process.env.TWILIO_WEBHOOK_BASE_URL;
else process.env.TWILIO_WEBHOOK_BASE_URL = prevBase;
}
});
test('resolveCallTestWebhookUrl rebuilds the registered Twilio voice URL', () => {
const prevBase = process.env.TWILIO_WEBHOOK_BASE_URL;
process.env.TWILIO_WEBHOOK_BASE_URL = 'https://proxy.example.com/CollabSupport';
try {
const req = {
path: '/twilio/calltest/voice',
originalUrl: '/twilio/calltest/voice?testId=abc&mode=dial&step=intro',
query: { testId: 'abc', mode: 'dial', step: 'intro' },
};
assert.equal(
resolveCallTestWebhookUrl(req),
'https://proxy.example.com/CollabSupport/twilio/calltest/voice?testId=abc&mode=dial&step=intro',
);
} finally {
if (prevBase == null) delete process.env.TWILIO_WEBHOOK_BASE_URL;
else process.env.TWILIO_WEBHOOK_BASE_URL = prevBase;
}
});
test('validateTwilioSignature accepts array POST params like Twilio SDK', () => {
const authToken = 'test-auth-token';
const url = 'https://example.com/twilio/calltest/status?testId=abc';
const params = { Tags: ['a', 'b'], CallStatus: 'completed' };
const signature = twilio.getExpectedTwilioSignature(authToken, url, params);
const prevSid = process.env.TWILIO_ACCOUNT_SID;
const prevToken = process.env.TWILIO_AUTH_TOKEN;
const prevFrom = process.env.TWILIO_FROM_NUMBER;
const prevBase = process.env.TWILIO_WEBHOOK_BASE_URL;
process.env.TWILIO_ACCOUNT_SID = 'ACtest';
process.env.TWILIO_AUTH_TOKEN = authToken;
process.env.TWILIO_FROM_NUMBER = '+15550001111';
process.env.TWILIO_WEBHOOK_BASE_URL = 'https://example.com';
try {
assert.equal(validateTwilioSignature(signature, url, params), true);
} finally {
if (prevSid == null) delete process.env.TWILIO_ACCOUNT_SID;
else process.env.TWILIO_ACCOUNT_SID = prevSid;
if (prevToken == null) delete process.env.TWILIO_AUTH_TOKEN;
else process.env.TWILIO_AUTH_TOKEN = prevToken;
if (prevFrom == null) delete process.env.TWILIO_FROM_NUMBER;
else process.env.TWILIO_FROM_NUMBER = prevFrom;
if (prevBase == null) delete process.env.TWILIO_WEBHOOK_BASE_URL;
else process.env.TWILIO_WEBHOOK_BASE_URL = prevBase;
}
});