Skip to content

Commit

Permalink
test: Include cluster test (#804)
Browse files Browse the repository at this point in the history
* [DI-1412] Add Cluster Test
  • Loading branch information
claudiachua authored Oct 25, 2022
1 parent 8bd7e7b commit cc83893
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 3 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/test-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ jobs:
- name: Run Unit Tests
run: npm run ci

- name: Run Cluster Tests
if: (!github.event.pull_request.head.repo.fork)
env:
TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }}
TWILIO_API_KEY: ${{ secrets.TWILIO_CLUSTER_TEST_API_KEY}}
TWILIO_API_SECRET: ${{ secrets.TWILIO_CLUSTER_TEST_API_KEY_SECRET }}
TWILIO_FROM_NUMBER: ${{ secrets.TWILIO_FROM_NUMBER }}
TWILIO_TO_NUMBER: ${{ secrets.TWILIO_TO_NUMBER }}
run: npm run cluster

- name: SonarCloud Scan
if: ${{ (github.event_name == 'pull_request' || github.ref_type == 'branch') && matrix.node == 'lts/*' && !github.event.pull_request.head.repo.fork }}
uses: SonarSource/sonarcloud-github-action@master
Expand Down
2 changes: 2 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Please enter each Issue number you are resolving in your PR after one of the fol
e.g.
Fixes #1
Closes #2
Note: If you made changes to the Request Validation logic, please run `npm run webhook-test` locally and ensure all test cases pass
-->

# Fixes #
Expand Down
73 changes: 73 additions & 0 deletions cluster_test.spec.ts
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);
});
})
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,21 @@
"nock": "^13.2.9",
"node-mocks-http": "^1.8.1",
"ts-jest": "^28.0.3",
"typescript": "^4.7.2"
"typescript": "^4.7.2",
"localtunnel": "2.0.2"
},
"scripts": {
"test": "npm run test:javascript",
"test": "npm run test:javascript -- spec/*",
"test:javascript": "jest --coverage --detectOpenHandles",
"test:typescript": "tsc -t es5 examples/typescript/example.ts --noEmit --strict",
"jshint": "jshint lib/rest/** lib/base/** lib/http/**",
"jscs": "eslint lib/base/**/**.js lib/http/**/**.js --fix",
"check": "npm run jshint && npm run jscs",
"ci": "npm run test && npm run nsp",
"jsdoc": "jsdoc -r lib -d docs",
"nsp": "npm audit --production"
"nsp": "npm audit --production",
"cluster": "jest ./cluster_test.spec.ts --coverage",
"webhook-test": "jest ./webhook_cluster_test.spec.ts --coverage"
},
"files": [
"lib",
Expand Down
105 changes: 105 additions & 0 deletions webhook_cluster_test.spec.ts
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);
})
});

0 comments on commit cc83893

Please sign in to comment.