Skip to content

Commit

Permalink
Merge pull request #523 from twilio-labs/add-tests
Browse files Browse the repository at this point in the history
Add tests for verify prefill
  • Loading branch information
cmsunu28 authored Jun 12, 2024
2 parents 1c67683 + 10f72d2 commit 3d370f1
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[Twilio Functions](https://www.twilio.com/functions) are a serverless environment to build and run Twilio applications so you can get to production faster. You provide the Node.js code to perform the task you need and Twilio runs it. You can read [more about Twilio Functions and how to use them in the introductory blog post](https://www.twilio.com/blog/2017/05/introducing-twilio-functions.html).

###Attention
### Attention

With the release of Node v18, the Node.js ecosystem is migrating over from the old CommonJS (CJS) standard to the newer, ES Modules (ESM) standard. Using ESM modules in CJS code is not possible. You can read about the differences in far more detail in this [blog Post.](https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1). The following snippets may causes errors.

Expand Down
12 changes: 1 addition & 11 deletions verify-prefill/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,4 @@
# format: sid
# link: https://www.twilio.com/console/verify/services
# required: true
VERIFY_SERVICE_SID=

# description: Your Lookup API Key. Can be created in the Console.
# required: true
# format: sid
LOOKUP_API_KEY=

# description: Your Lookup API Secret. Can be created in the Console.
# required: true
# format: secret
LOOKUP_API_SECRET=
VERIFY_SERVICE_SID=
5 changes: 4 additions & 1 deletion verify-prefill/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Demo application showing Twilio Verify and Lookup Identity-Prefill APIs

In today's digital age, user experience is paramount. Every additional step or friction point can deter potential customers, reduce conversion rates, and degrade the overall experience. To combat this, Twilio introduces an innovative feature within its Lookup API: **Identity Pre-Fill** in Private Beta. This powerful tool can revolutionize how businesses handle customer interactions, from onboarding to checkout, by pre-populating user data with verified information. Below, we discuss how this feature works, its potential benefits, and ways you can leverage it through a sample demo app.
In today's digital age, user experience is paramount. Every additional step or friction point can deter potential customers, reduce conversion rates, and degrade the overall experience. To combat this, Twilio introduces an innovative feature within its Lookup API: **Identity Pre-Fill** in Pilot. This powerful tool can revolutionize how businesses handle customer interactions, from onboarding to checkout, by pre-populating user data with verified information. Below, we discuss how this feature works, its potential benefits, and ways you can leverage it through a sample demo app.

This project demonstrates a phone number verification and prefilling user information using Twilio's Verify and Lookups APIs. Users can enter their phone number, receive an OTP, and verify their phone number. Upon successful verification, additional user details such as name and address will be fetched and displayed after a 90 seconds delay.

Expand All @@ -20,6 +20,9 @@ This project demonstrates a phone number verification and prefilling user inform

## Pre-requisites

Access is behind a flag during the pilot phase.
[Request Access Here](https://docs.google.com/forms/d/e/1FAIpQLSfXowQ9dUGgDNc_onA0yj2_Mo3tXxFWK67SpDfOZjONothBYQ/viewform?usp=send_form).

### Technical Considerations

A few technical aspects to keep in mind:
Expand Down
3 changes: 0 additions & 3 deletions verify-prefill/functions/blank.js

This file was deleted.

13 changes: 5 additions & 8 deletions verify-prefill/functions/fetch-user-data.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
const fetch = require('node-fetch');

exports.handler = async function (context, event, callback) {
const { phoneNumber } = event;
const { verificationSid } = event;
const lookupApiKey = context.LOOKUP_API_KEY;
const lookupApiSecret = context.LOOKUP_API_SECRET;
const { phoneNumber, verificationSid } = event;
const lookupApiKey = context.ACCOUNT_SID;
const lookupApiSecret = context.AUTH_TOKEN;

try {
// Use dynamic import to load the node-fetch module
const fetch = (await import('node-fetch')).default;

const lookupUrl = `https://lookups.twilio.com/v2/PhoneNumbers/${phoneNumber}?Fields=pre_fill&VerificationSid=${verificationSid}`;
const lookupResponse = await fetch(lookupUrl, {
headers: {
Expand All @@ -19,7 +17,6 @@ exports.handler = async function (context, event, callback) {
});

const lookupData = await lookupResponse.json();

return callback(null, { success: true, prefillData: lookupData.pre_fill });
} catch (error) {
console.error('Error fetching user data:', error);
Expand Down
21 changes: 10 additions & 11 deletions verify-prefill/functions/send-otp.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@ exports.handler = async function (context, event, callback) {
const { phoneNumber } = event;
const { VERIFY_SERVICE_SID: serviceSid } = context;

// Ensure the Twilio client is initialized
const client = context.getTwilioClient();
try {
// Ensure the Twilio client is initialized
const client = context.getTwilioClient();

if (!serviceSid) {
console.error('Missing VERIFY_SERVICE_SID');
return callback('Missing VERIFY_SERVICE_SID');
}
if (!serviceSid) {
throw new Error('Missing VERIFY_SERVICE_SID');
}

try {
// Validate phone number using Twilio Lookup API with Node.js library
const lookupResponse = await client.lookups.v2
.phoneNumbers(phoneNumber)
.fetch();

if (!lookupResponse.valid) {
const message =
'Invalid phone number. Please enter a valid number in E.164 format.';
console.error(message, lookupResponse);
return callback(null, { success: false, message });
console.error(lookupResponse);
throw new Error(
'Invalid phone number. Please enter a valid number in E.164 format.'
);
}

// Start verification if the phone number is valid
Expand Down
11 changes: 5 additions & 6 deletions verify-prefill/functions/verify-otp.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ exports.handler = async function (context, event, callback) {
const { phoneNumber, code } = event;
const { VERIFY_SERVICE_SID: serviceSid } = context;

const client = context.getTwilioClient();
try {
const client = context.getTwilioClient();

if (!serviceSid) {
console.error('Missing VERIFY_SERVICE_SID');
return callback('Missing VERIFY_SERVICE_SID');
}
if (!serviceSid) {
throw new Error('Missing VERIFY_SERVICE_SID');
}

try {
// Verify the OTP using Twilio Verify API V2
const verificationCheck = await client.verify.v2
.services(serviceSid)
Expand Down
2 changes: 1 addition & 1 deletion verify-prefill/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"twilio": "^5.0.4",
"node-fetch": "^3.3.2"
"node-fetch": "^2.7.0"
}
}
9 changes: 0 additions & 9 deletions verify-prefill/tests/blank.test.js

This file was deleted.

42 changes: 42 additions & 0 deletions verify-prefill/tests/fetch-user-data.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const fetch = require('node-fetch');
const fetchUserDataFunction = require('../functions/fetch-user-data').handler;
const helpers = require('../../test/test-helper');

const testContext = {
LOOKUP_API_KEY: 'foo',
LOOKUP_API_SECRET: 'bar',
};

jest.mock('node-fetch', () => jest.fn());

describe('verify-prefill/fetch-user-data', () => {
beforeAll(() => {
helpers.setup({});
});
afterAll(() => {
helpers.teardown();
});

test('returns success with valid request', (done) => {
const callback = (_err, result) => {
expect(result).toBeDefined();
expect(result.success).toEqual(true);
expect(result.prefillData).toBeDefined();
done();
};

const lookupResponse = Promise.resolve({
json: () =>
Promise.resolve({
pre_fill: 'mydata', // eslint-disable-line camelcase
}),
});
fetch.mockImplementation(() => lookupResponse);

const event = {
phoneNumber: '+17341234567',
verificationSid: 'VExxkasjdf',
};
fetchUserDataFunction(testContext, event, callback);
});
});
80 changes: 80 additions & 0 deletions verify-prefill/tests/send-otp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const sendOtpFunction = require('../functions/send-otp').handler;
const helpers = require('../../test/test-helper');

const mockLookup = {
fetch: jest.fn(() => Promise.resolve({ valid: true })),
};
const mockClient = {
verify: {
v2: {
services: jest.fn(() => ({
verifications: {
create: jest.fn(),
},
})),
},
},
lookups: {
v2: {
phoneNumbers: jest.fn(() => mockLookup),
},
},
};

const testContext = {
VERIFY_SERVICE_SID: 'default',
getTwilioClient: () => mockClient,
};

describe('verify/send-otp', () => {
beforeAll(() => {
helpers.setup({});
});
afterAll(() => {
helpers.teardown();
});

test('returns an error with VERIFY_SERVICE_SID is missing', (done) => {
const callback = (_err, result) => {
expect(result).toBeDefined();
expect(result.success).toEqual(false);
expect(result.message).toEqual('Missing VERIFY_SERVICE_SID');
done();
};

const event = { phoneNumber: '+17341234567' };
const contextWithoutServiceSid = { getTwilioClient: () => mockClient };
sendOtpFunction(contextWithoutServiceSid, event, callback);
});

test('returns an error with invalid phone number', (done) => {
const callback = (_err, result) => {
expect(result.success).toEqual(false);
expect(result.message).toEqual(
'Invalid phone number. Please enter a valid number in E.164 format.'
);
done();
};

mockLookup.fetch.mockImplementationOnce(() =>
Promise.resolve({
valid: false,
})
);

const event = { phoneNumber: 'invalid' };
sendOtpFunction(testContext, event, callback);
});

test('sends an otp with valid phone number', (done) => {
const callback = (_err, result) => {
expect(result).toBeDefined();
expect(result.success).toEqual(true);
expect(result.message).toEqual('Verification sent to +17341234567');
done();
};

const event = { phoneNumber: '+17341234567' };
sendOtpFunction(testContext, event, callback);
});
});
71 changes: 71 additions & 0 deletions verify-prefill/tests/verify-otp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const verifyOtpFunction = require('../functions/verify-otp').handler;
const helpers = require('../../test/test-helper');

const mockCheck = {
create: jest.fn(() => Promise.resolve({ sid: 'VAxxx', status: 'approved' })),
};
const mockClient = {
verify: {
v2: {
services: jest.fn(() => ({
verificationChecks: mockCheck,
})),
},
},
};

const testContext = {
VERIFY_SERVICE_SID: 'default',
getTwilioClient: () => mockClient,
};

describe('verify/send-otp', () => {
beforeAll(() => {
helpers.setup({});
});
afterAll(() => {
helpers.teardown();
});

test('returns an error with VERIFY_SERVICE_SID is missing', (done) => {
const callback = (_err, result) => {
expect(result).toBeDefined();
expect(result.success).toEqual(false);
expect(result.message).toEqual('Missing VERIFY_SERVICE_SID');
done();
};

const event = { phoneNumber: '+17341234567', code: '123456' };
const contextWithoutServiceSid = { getTwilioClient: () => mockClient };
verifyOtpFunction(contextWithoutServiceSid, event, callback);
});

test('returns an error with invalid code', (done) => {
const callback = (_err, result) => {
expect(result.success).toEqual(false);
expect(result.message).toEqual('Verification failed. Status: pending');
done();
};

mockCheck.create.mockImplementationOnce(() =>
Promise.resolve({
status: 'pending',
})
);

const event = { phoneNumber: '+17341234567', code: '777777' };
verifyOtpFunction(testContext, event, callback);
});

test('returns success with approved otp', (done) => {
const callback = (_err, result) => {
expect(result).toBeDefined();
expect(result.success).toEqual(true);
expect(result.verificationSid).toEqual('VAxxx');
done();
};

const event = { phoneNumber: '+17341234567', code: '123456' };
verifyOtpFunction(testContext, event, callback);
});
});

0 comments on commit 3d370f1

Please sign in to comment.