// 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; } });