Skip to content

Commit 75d9fee

Browse files
committed
Additional service tests
1 parent 11ff8f5 commit 75d9fee

File tree

6 files changed

+125
-21
lines changed

6 files changed

+125
-21
lines changed

src/@types/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,11 @@ export interface JoinConferenceParams extends Request {
3636
params: {
3737
conferenceName: string
3838
}
39+
}
40+
41+
export interface ParticipantsToDial[] {
42+
messagingBinding: {
43+
address: string
44+
proxyAddress: string
45+
}
3946
}

src/services/inboundCall.service.ts

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import VoiceResponse from "twilio/lib/twiml/VoiceResponse";
33
import { getConversationByAddressPair } from "../utils/getConversationByAddressPair.util";
44

55
import client from '../twilioClient'
6+
import { participantsToDial } from "../utils/participantsToDial.util";
7+
import { listConversationParticipants } from "../utils";
68

79
export const generateTwiml = async (from: string, to: string) => {
810
let response = new VoiceResponse();
@@ -16,33 +18,18 @@ export const generateTwiml = async (from: string, to: string) => {
1618
language: process.env.CALL_ANNOUCEMENT_LANGUAGE as any,
1719
}, process.env.OUT_OF_SESSION_MESSAGE_FOR_CALL);
1820
} else {
19-
const participants = await client.conversations
20-
.conversations(conversation.conversationSid)
21-
.participants
22-
.list()
23-
24-
const participantsToDial = participants.reduce((result, p) => {
25-
if (p.messagingBinding.type === "sms" && p.messagingBinding.address != from) {
26-
console.log(`Adding ${p.messagingBinding.address} to list of numbers to dial.\n`)
27-
28-
result.push({
29-
address: p.messagingBinding.address,
30-
proxyAddress: p.messagingBinding.proxy_address
31-
})
32-
}
33-
34-
return result;
35-
}, [])
21+
const participants = await listConversationParticipants(conversation.conversationSid)
22+
const dialList = participantsToDial(participants, from)
3623

3724
response.say({
3825
voice: process.env.CALL_ANNOUCEMENT_VOICE as any,
3926
language: process.env.CALL_ANNOUCEMENT_LANGUAGE as any
4027
}, process.env.CONNECTING_CALL_ANNOUCEMENT);
4128

42-
if (participantsToDial.length > 1) {
29+
if (dialList.length > 1) {
4330
const conferenceName = `${from}_at_${Date.now()}`
4431

45-
const callPromises = participantsToDial.map(pa => {
32+
const callPromises = dialList.map(pa => {
4633
console.log(`Dialing ${pa.address} from ${pa.proxyAddress}...`);
4734

4835
return client.calls.create({
@@ -66,7 +53,7 @@ export const generateTwiml = async (from: string, to: string) => {
6653

6754

6855
} else {
69-
const callee = participantsToDial[0]
56+
const callee = dialList[0]
7057
const dial = response.dial({
7158
callerId: callee.proxyAddress
7259
});

src/utils/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ export * from "./addParticipant.util";
22
export * from "./createConversation.util";
33
export * from "./deleteConversation.util";
44
export * from "./listParticipantConversations.util"
5-
export * from "./retryAddParticipant.util"
5+
export * from "./retryAddParticipant.util"
6+
export * from "./listConversationParticipants.util"
7+
export * from "./participantsToDial.util"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import retry from 'async-retry'
2+
import { ParticipantInstance } from 'twilio/lib/rest/conversations/v1/conversation/participant'
3+
import client from '../twilioClient'
4+
5+
export const listConversationParticipants = async (conversation: string) : Promise<ParticipantInstance[]> => {
6+
return retry(async (quit) => {
7+
try {
8+
const participants = await client.conversations
9+
.conversations(conversation)
10+
.participants
11+
.list()
12+
13+
return participants
14+
} catch (err) {
15+
if (err.status !== 429) {
16+
quit(new Error(err));
17+
return;
18+
}
19+
20+
console.log('Re-trying on 429 error');
21+
throw new Error(err);
22+
}
23+
})
24+
25+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ParticipantInstance } from "twilio/lib/rest/conversations/v1/conversation/participant";
2+
3+
export const participantsToDial = (participants: Array<ParticipantInstance>, from: string) : ParticipantsToDial => {
4+
5+
let accumulator: {address: string, proxyAddress: string }[]
6+
return participants.reduce((result, p) => {
7+
if (p.messagingBinding.type === "sms" && p.messagingBinding.address != from) {
8+
console.log(`Adding ${p.messagingBinding.address} to list of numbers to dial.\n`)
9+
10+
result.push({
11+
address: p.messagingBinding.address,
12+
proxyAddress: p.messagingBinding.proxy_address
13+
})
14+
}
15+
16+
return result;
17+
}, accumulator)
18+
}

tests/services/inboundCall.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
import { generateTwiml } from '../../src/services/inboundCall.service'
3+
import { getConversationByAddressPair } from "../../src/utils/getConversationByAddressPair.util";
4+
import { listConversationParticipants } from '../../src/utils/listConversationParticipants.util';
5+
import { participantsToDial } from '../../src/utils/participantsToDial.util'
6+
7+
8+
jest.mock("../../src/utils/getConversationByAddressPair.util")
9+
10+
jest.mock("../../src/utils/listConversationParticipants.util")
11+
12+
jest.mock('../../src/utils/participantsToDial.util')
13+
14+
describe('inbound call service', () => {
15+
const env = process.env
16+
const mockGetConversationAddressPair = jest.mocked(getConversationByAddressPair, true)
17+
const mockListConversationParticipants = jest.mocked(listConversationParticipants, true)
18+
const mockParticipantsToDial = jest.mocked(participantsToDial, true)
19+
20+
21+
beforeEach(() => {
22+
jest.resetModules()
23+
process.env = { ...env }
24+
25+
})
26+
27+
afterEach(() => {
28+
process.env = env
29+
jest.resetAllMocks()
30+
})
31+
32+
it('returns out of session call announcement if no conversation if provided', async () => {
33+
mockGetConversationAddressPair.mockResolvedValue(undefined)
34+
35+
process.env.CALL_ANNOUCEMENT_VOICE = 'alice'
36+
process.env.CALL_ANNOUCEMENT_LANGUAGE = 'en'
37+
process.env.OUT_OF_SESSION_MESSAGE_FOR_CALL = 'You session has ended, please call our main line.'
38+
const result = await generateTwiml('+1112223333', '+2223334444')
39+
40+
expect(result.toString()).toBe("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><Say voice=\"alice\" language=\"en\">You session has ended, please call our main line.</Say></Response>")
41+
})
42+
43+
it('creates a conference if dialList is longer than 1', async () => {
44+
45+
mockGetConversationAddressPair.mockResolvedValue({ conversationSid: 'CH123'} as any)
46+
mockListConversationParticipants.mockResolvedValue([{
47+
messagingBinding: {
48+
address: '+1112223333',
49+
proxyAddress: '+2223334444'
50+
}
51+
}] as any)
52+
53+
mockParticipantsToDial.mockResolvedValue([{
54+
messagingBinding: {
55+
address: '+1112223333',
56+
proxyAddress: '+2223334444'
57+
}
58+
}])
59+
60+
const result = await generateTwiml('+1112223333', '+2223334444')
61+
62+
})
63+
64+
65+
})

0 commit comments

Comments
 (0)