-
Notifications
You must be signed in to change notification settings - Fork 518
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
5 changed files
with
196 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
'use strict'; | ||
|
||
jest.setTimeout(15000); | ||
|
||
const twilio = require('./lib/index.js'); | ||
|
||
const fromNumber = process.env.TWILIO_FROM_NUMBER; | ||
const toNumber = process.env.TWILIO_TO_NUMBER; | ||
const apiKey = process.env.TWILIO_API_KEY; | ||
const apiSecret = process.env.TWILIO_API_SECRET; | ||
const accountSid = process.env.TWILIO_ACCOUNT_SID; | ||
const testClient = twilio(apiKey, apiSecret, {accountSid}); | ||
|
||
test("Should send a text", () => { | ||
return testClient.messages.create({ | ||
body: "hello world", | ||
to: toNumber, | ||
from: fromNumber, | ||
}).then(msg => { | ||
expect(msg.sid).not.toBeUndefined(); | ||
expect(msg.to).toBe(toNumber); | ||
expect(msg.from).toBe(fromNumber); | ||
expect(msg.body).toBe("hello world") | ||
}) | ||
}) | ||
|
||
test("Should list incoming numbers", () => { | ||
return testClient.incomingPhoneNumbers | ||
.list() | ||
.then(incomingPhoneNumbers => { | ||
expect(incomingPhoneNumbers).not.toBeNull(); | ||
expect(incomingPhoneNumbers.length).toBeGreaterThanOrEqual(2); | ||
}) | ||
}) | ||
|
||
test("Should list a incoming number", () => { | ||
return testClient.incomingPhoneNumbers | ||
.list({limit: 1}) | ||
.then(incomingPhoneNumbers => { | ||
expect(incomingPhoneNumbers).not.toBeUndefined(); | ||
expect(incomingPhoneNumbers.length).toEqual(1); | ||
}) | ||
}) | ||
|
||
test("Should allow special characters for friendly and identity name", async () => { | ||
const friendlyName = 'service|friendly&name' | ||
const identityName = 'user|identity&string' | ||
const conversation = await testClient.conversations.v1.conversations | ||
.create({friendlyName: friendlyName}) | ||
const participant = await testClient.conversations.v1.conversations(conversation.sid) | ||
.participants | ||
.create({identity: identityName}) | ||
|
||
expect(conversation).not.toBeNull(); | ||
expect(participant).not.toBeNull(); | ||
expect(conversation.friendlyName).toBe(friendlyName) | ||
expect(participant.identity).toBe(identityName) | ||
|
||
const removeConversation = await testClient.conversations.v1.conversations(conversation.sid) | ||
.remove(); | ||
|
||
expect(removeConversation).toBeTruthy(); | ||
}) | ||
|
||
test("Should list available numbers", () => { | ||
return testClient.availablePhoneNumbers('US') | ||
.tollFree | ||
.list({limit: 2}) | ||
.then(tollFree => { | ||
expect(tollFree).not.toBeNull(); | ||
expect(tollFree.length).toEqual(2); | ||
}); | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
'use strict'; | ||
|
||
import {FlowInstance} from "./lib/rest/studio/v2/flow"; | ||
jest.setTimeout(15000); | ||
|
||
const twilio = require('./lib/index.js'); | ||
const localtunnel = require('localtunnel'); | ||
const http = require('http'); | ||
|
||
const authToken = process.env.TWILIO_AUTH_TOKEN; | ||
const apiKey = process.env.TWILIO_API_KEY; | ||
const apiSecret = process.env.TWILIO_API_SECRET; | ||
const accountSid = process.env.TWILIO_ACCOUNT_SID; | ||
const testClient = twilio(apiKey, apiSecret, {accountSid}); | ||
|
||
describe('Validating Incoming Twilio Request', () => { | ||
let tunnel; | ||
let flowSid; | ||
let validationServer; | ||
let portNumber = 7777; | ||
let validationCount = 0; //workaround to ensure validation server receives requests (due to localtunnel connections issue) | ||
|
||
beforeAll(async () => { | ||
validationServer = await http.createServer((req, res) => { | ||
validationCount++; | ||
let url = req.headers["x-forwarded-proto"] + "://" + req.headers["host"] + req.url | ||
let signatureHeader = req.headers["x-twilio-signature"] | ||
let body = ""; | ||
req.on("data", (chunk: string) => { | ||
body += chunk; | ||
}); | ||
|
||
req.on("end", () => { | ||
let params = new URLSearchParams(body); | ||
let paramObject = Object.fromEntries(params.entries()); | ||
let requestIsValid = twilio.validateRequest(authToken, signatureHeader, url, paramObject) | ||
expect(requestIsValid).toBeTruthy(); | ||
}); | ||
}); | ||
validationServer.listen(portNumber); | ||
tunnel = await localtunnel({port: portNumber}); | ||
}); | ||
|
||
afterAll(() => { | ||
tunnel.close(); | ||
validationServer.close(); | ||
}); | ||
|
||
afterEach(() => { | ||
testClient.studio.v2.flows(flowSid).remove(); | ||
}) | ||
|
||
function CreateStudioFlow(url: string, method: string): Promise<FlowInstance> { | ||
return testClient.studio.v2.flows.create({ | ||
friendlyName: 'Node Cluster Test Flow', | ||
status: 'published', | ||
definition: { | ||
description: 'Studio Flow', | ||
states: [ | ||
{ | ||
name: 'Trigger', | ||
type: 'trigger', | ||
transitions: [{ | ||
"next": "httpRequest", | ||
"event": "incomingRequest" | ||
}], | ||
properties: {} | ||
}, { | ||
name: "httpRequest", | ||
type: "make-http-request", | ||
transitions: [], | ||
properties: { | ||
method: method, | ||
content_type: "application/x-www-form-urlencoded;charset=utf-8", | ||
url: url | ||
} | ||
} | ||
], | ||
initial_state: 'Trigger', | ||
flags: { | ||
allow_concurrent_calls: true | ||
} | ||
} | ||
}) | ||
} | ||
|
||
async function validateRequest(method: string) { | ||
let flow = await CreateStudioFlow(tunnel.url, method); | ||
flowSid = flow.sid; | ||
await testClient.studio.v2.flows(flowSid) | ||
.executions | ||
.create({to: 'to', from: 'from'}); | ||
await new Promise(resolve => setTimeout(resolve, 5000)) | ||
} | ||
|
||
test("Should validate GET request", async () => { | ||
await validateRequest('GET'); | ||
expect(validationCount).toBe(1); | ||
}) | ||
|
||
test("Should validate POST request", async () => { | ||
await validateRequest('POST'); | ||
expect(validationCount).toBe(2); | ||
}) | ||
}); |