167 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* eslint-disable camelcase */
 | |
| /* global describe it expect */
 | |
| /* global jest*/
 | |
| 
 | |
| import axios from 'axios';
 | |
| import keys from '../../../config/secrets';
 | |
| import {
 | |
|   getAsyncPaypalToken,
 | |
|   verifyWebHook,
 | |
|   updateUser,
 | |
|   capitalizeKeys,
 | |
|   createDonationObj
 | |
| } from './donation';
 | |
| import { mockActivationHook, mockCancellationHook } from './__mocks__/donation';
 | |
| import {
 | |
|   mockApp,
 | |
|   createDonationMockFn,
 | |
|   createUserMockFn,
 | |
|   updateDonationAttr,
 | |
|   updateUserAttr
 | |
| } from '../boot_tests/fixtures';
 | |
| 
 | |
| jest.mock('axios');
 | |
| 
 | |
| const verificationUrl = `https://api.sandbox.paypal.com/v1/notifications/verify-webhook-signature`;
 | |
| const tokenUrl = `https://api.sandbox.paypal.com/v1/oauth2/token`;
 | |
| const {
 | |
|   body: activationHookBody,
 | |
|   headers: activationHookHeaders
 | |
| } = mockActivationHook;
 | |
| 
 | |
| describe('donation', () => {
 | |
|   describe('getAsyncPaypalToken', () => {
 | |
|     it('call paypal api for token ', async () => {
 | |
|       const res = {
 | |
|         data: {
 | |
|           access_token: 'token'
 | |
|         }
 | |
|       };
 | |
| 
 | |
|       axios.post.mockImplementationOnce(() => Promise.resolve(res));
 | |
| 
 | |
|       await expect(getAsyncPaypalToken()).resolves.toEqual(
 | |
|         res.data.access_token
 | |
|       );
 | |
| 
 | |
|       expect(axios.post).toHaveBeenCalledTimes(1);
 | |
|       expect(axios.post).toHaveBeenCalledWith(tokenUrl, null, {
 | |
|         headers: {
 | |
|           'Content-Type': 'application/x-www-form-urlencoded'
 | |
|         },
 | |
|         auth: {
 | |
|           username: keys.paypal.client,
 | |
|           password: keys.paypal.secret
 | |
|         },
 | |
|         params: {
 | |
|           grant_type: 'client_credentials'
 | |
|         }
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('verifyWebHook', () => {
 | |
|     // normalize headers
 | |
|     capitalizeKeys(activationHookHeaders);
 | |
|     const mockWebhookId = 'qwdfq;3w12341dfa4';
 | |
|     const mockAccessToken = '241231223$!@$#1243';
 | |
|     const mockPayLoad = {
 | |
|       auth_algo: activationHookHeaders['PAYPAL-AUTH-ALGO'],
 | |
|       cert_url: activationHookHeaders['PAYPAL-CERT-URL'],
 | |
|       transmission_id: activationHookHeaders['PAYPAL-TRANSMISSION-ID'],
 | |
|       transmission_sig: activationHookHeaders['PAYPAL-TRANSMISSION-SIG'],
 | |
|       transmission_time: activationHookHeaders['PAYPAL-TRANSMISSION-TIME'],
 | |
|       webhook_id: mockWebhookId,
 | |
|       webhook_event: activationHookBody
 | |
|     };
 | |
|     const failedVerificationErr = {
 | |
|       message: `Failed token verification.`,
 | |
|       type: 'FailedPaypalTokenVerificationError'
 | |
|     };
 | |
|     const axiosOptions = {
 | |
|       headers: {
 | |
|         'Content-Type': 'application/json',
 | |
|         Authorization: `Bearer ${mockAccessToken}`
 | |
|       }
 | |
|     };
 | |
|     const successVerificationResponce = {
 | |
|       data: {
 | |
|         verification_status: 'SUCCESS'
 | |
|       }
 | |
|     };
 | |
|     const failedVerificationResponce = {
 | |
|       data: {
 | |
|         verification_status: 'FAILED'
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     it('calls paypal for Webhook verification', async () => {
 | |
|       axios.post.mockImplementationOnce(() =>
 | |
|         Promise.resolve(successVerificationResponce)
 | |
|       );
 | |
| 
 | |
|       await expect(
 | |
|         verifyWebHook(
 | |
|           activationHookHeaders,
 | |
|           activationHookBody,
 | |
|           mockAccessToken,
 | |
|           mockWebhookId
 | |
|         )
 | |
|       ).resolves.toEqual(activationHookBody);
 | |
| 
 | |
|       expect(axios.post).toHaveBeenCalledWith(
 | |
|         verificationUrl,
 | |
|         mockPayLoad,
 | |
|         axiosOptions
 | |
|       );
 | |
|     });
 | |
|     it('throws error if verification not successful', async () => {
 | |
|       axios.post.mockImplementationOnce(() =>
 | |
|         Promise.resolve(failedVerificationResponce)
 | |
|       );
 | |
| 
 | |
|       await expect(
 | |
|         verifyWebHook(
 | |
|           activationHookHeaders,
 | |
|           activationHookBody,
 | |
|           mockAccessToken,
 | |
|           mockWebhookId
 | |
|         )
 | |
|       ).rejects.toEqual(failedVerificationErr);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('updateUser', () => {
 | |
|     it('created a donation when a machting user found', () => {
 | |
|       updateUser(activationHookBody, mockApp);
 | |
|       expect(createDonationMockFn).toHaveBeenCalledTimes(1);
 | |
|       expect(createDonationMockFn).toHaveBeenCalledWith(
 | |
|         createDonationObj(activationHookBody)
 | |
|       );
 | |
|     });
 | |
|     it('create a user and donation when no machting user found', () => {
 | |
|       let newActivationHookBody = activationHookBody;
 | |
|       newActivationHookBody.resource.subscriber.email_address =
 | |
|         'new@freecodecamp.org';
 | |
|       updateUser(newActivationHookBody, mockApp);
 | |
|       expect(createUserMockFn).toHaveBeenCalledTimes(1);
 | |
|     });
 | |
| 
 | |
|     it('modify user and donation records on cancellation', () => {
 | |
|       const { body: cancellationHookBody } = mockCancellationHook;
 | |
|       const {
 | |
|         resource: { status_update_time = new Date(Date.now()).toISOString() }
 | |
|       } = cancellationHookBody;
 | |
| 
 | |
|       updateUser(cancellationHookBody, mockApp);
 | |
|       expect(updateDonationAttr).toHaveBeenCalledWith({
 | |
|         endDate: new Date(status_update_time).toISOString()
 | |
|       });
 | |
| 
 | |
|       expect(updateUserAttr).toHaveBeenCalledWith({
 | |
|         isDonating: false
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| });
 |