From 8ac13e819a75a5e0f64b7a6fa085a0d1f9d1225a Mon Sep 17 00:00:00 2001 From: Felipe Alvarado Date: Mon, 12 Feb 2024 16:27:35 +0100 Subject: [PATCH] Add test for Axios errors (#152) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add test for Axios errors * Fix test description message Co-authored-by: Uxío --------- Co-authored-by: Uxío --- src/routes/webhook/webhook.service.spec.ts | 142 ++++++++++++++++++++- 1 file changed, 137 insertions(+), 5 deletions(-) diff --git a/src/routes/webhook/webhook.service.spec.ts b/src/routes/webhook/webhook.service.spec.ts index 3ed4987..66ba716 100644 --- a/src/routes/webhook/webhook.service.spec.ts +++ b/src/routes/webhook/webhook.service.spec.ts @@ -1,3 +1,4 @@ +import { Logger } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { WebhookService } from './webhook.service'; import { Webhook } from './entities/webhook.entity'; @@ -7,7 +8,8 @@ import { ConfigModule } from '@nestjs/config'; import { TxServiceEventType } from '../events/event.dto'; import { HttpService } from '@nestjs/axios'; import { Observable } from 'rxjs'; -import { AxiosResponse } from 'axios'; +import { AxiosError, AxiosHeaders, AxiosResponse } from 'axios'; +import { throwError } from 'rxjs'; describe('Webhook service', () => { let httpService: HttpService; @@ -64,8 +66,8 @@ describe('Webhook service', () => { }; const results = await webhookService.postEveryWebhook(msg); expect(results).toEqual([]); - expect(findAllActiveSpy).toBeCalledTimes(1); - expect(postWebhookSpy).toBeCalledTimes(0); + expect(findAllActiveSpy).toHaveBeenCalledTimes(1); + expect(postWebhookSpy).toHaveBeenCalledTimes(0); }); it('should post if webhooks are defined', async () => { const webhooks: Webhook[] = [new Webhook(), new Webhook(), new Webhook()]; @@ -98,9 +100,9 @@ describe('Webhook service', () => { }; const results = await webhookService.postEveryWebhook(msg); expect(results).toEqual([postWebhookResponse, postWebhookResponse]); - expect(findAllActiveSpy).toBeCalledTimes(1); + expect(findAllActiveSpy).toHaveBeenCalledTimes(1); // Only 2 webhooks will be called, as one of them has `sendSafeCreations=false` - expect(postWebhookSpy).toBeCalledTimes(2); + expect(postWebhookSpy).toHaveBeenCalledTimes(2); expect(postWebhookSpy).toHaveBeenNthCalledWith( 1, msg, @@ -171,5 +173,135 @@ describe('Webhook service', () => { headers: { Authorization: authorization }, }); }); + + it('should log an error message if response is received with non-2xx status code', async () => { + const url = 'http://localhost:4815'; + const msg = { + chainId: '1', + type: 'SAFE_CREATED' as TxServiceEventType, + text: 'hello', + address: '0x0275FC2adfF11270F3EcC4D2F7Aa0a9784601Ca6', + }; + + const axiosConfigMocked = { + headers: new AxiosHeaders(), + }; + const axiosResponseMocked = { + status: 503, + statusText: 'Service Unavailable', + data: 'No data', + }; + + const httpServicePostSpy = jest + .spyOn(httpService, 'post') + .mockReturnValue( + throwError( + () => + new AxiosError( + 'Service Unavailable', + '503', + axiosConfigMocked, + {}, + axiosResponseMocked, + ), + ), + ); + const loggerErrorSpy = jest + .spyOn(Logger.prototype, 'error') + .mockImplementation(); + + await webhookService.postWebhook(msg, url, ''); + + expect(httpServicePostSpy).toHaveBeenCalledTimes(1); + expect(httpServicePostSpy).toHaveBeenCalledWith(url, msg, { + headers: {}, + }); + expect(loggerErrorSpy).toHaveBeenCalledWith( + expect.stringContaining( + `Error sending event ${JSON.stringify(msg)} to ${url}: ${ + axiosResponseMocked.status + } ${axiosResponseMocked.statusText} - ${axiosResponseMocked.data}`, + ), + ); + }); + + it('should log an error message if response is not received', async () => { + const url = 'http://localhost:4815'; + const msg = { + chainId: '1', + type: 'SAFE_CREATED' as TxServiceEventType, + text: 'hello', + address: '0x0275FC2adfF11270F3EcC4D2F7Aa0a9784601Ca6', + }; + + const axiosConfigMocked = { + headers: new AxiosHeaders(), + }; + + const httpServicePostSpy = jest + .spyOn(httpService, 'post') + .mockReturnValue( + throwError( + () => + new AxiosError( + 'Service Unavailable', + '503', + axiosConfigMocked, + {}, + undefined, + ), + ), + ); + const loggerErrorSpy = jest + .spyOn(Logger.prototype, 'error') + .mockImplementation(); + + await webhookService.postWebhook(msg, url, ''); + + expect(httpServicePostSpy).toHaveBeenCalledTimes(1); + expect(httpServicePostSpy).toHaveBeenCalledWith(url, msg, { + headers: {}, + }); + expect(loggerErrorSpy).toHaveBeenCalledWith( + expect.stringContaining( + `Error sending event ${JSON.stringify( + msg, + )} to ${url}: Response not received`, + ), + ); + }); + + it('should log an error message if request cannot be made', async () => { + const url = 'http://localhost:4815'; + const msg = { + chainId: '1', + type: 'SAFE_CREATED' as TxServiceEventType, + text: 'hello', + address: '0x0275FC2adfF11270F3EcC4D2F7Aa0a9784601Ca6', + }; + + const errorMessage = 'Internal Server Error'; + + const httpServicePostSpy = jest + .spyOn(httpService, 'post') + .mockReturnValue(throwError(() => new Error(errorMessage))); + const loggerErrorSpy = jest + .spyOn(Logger.prototype, 'error') + .mockImplementation(); + + await webhookService.postWebhook(msg, url, ''); + + expect(httpServicePostSpy).toHaveBeenCalledTimes(1); + expect(httpServicePostSpy).toHaveBeenCalledWith(url, msg, { + headers: {}, + }); + expect(loggerErrorSpy).toHaveBeenCalledWith( + expect.stringContaining( + `Error sending event ${JSON.stringify( + msg, + )} to ${url}: ${errorMessage}`, + ), + ); + }); }); });