From 593ddd8347b252c8739e8485886f4ebebf60bc57 Mon Sep 17 00:00:00 2001 From: antoinechalifour Date: Wed, 14 Aug 2019 13:40:27 +0200 Subject: [PATCH] fix: apply delay to cached responses --- src/domain/usecase/RespondToRequest.test.ts | 15 ++++++++++- src/domain/usecase/RespondToRequest.ts | 26 ++++++++++++++----- .../service/NetworkServiceAxios.ts | 7 +---- src/util/__mocks__/timers.ts | 1 + src/util/timers.ts | 2 ++ 5 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 src/util/__mocks__/timers.ts create mode 100644 src/util/timers.ts diff --git a/src/domain/usecase/RespondToRequest.test.ts b/src/domain/usecase/RespondToRequest.test.ts index 2d01eaf..615e989 100644 --- a/src/domain/usecase/RespondToRequest.test.ts +++ b/src/domain/usecase/RespondToRequest.test.ts @@ -1,13 +1,17 @@ import { ResponseRepository } from '../repository'; import { NetworkService } from '../service'; -import { RespondToRequest } from './RespondToRequest'; import { Response, Request } from '../entity'; +import { RespondToRequest } from './RespondToRequest'; +import { wait } from '../../util/timers'; + +jest.mock('../../util/timers'); let responseRepository: ResponseRepository; let networkService: NetworkService; beforeEach(() => { + (wait as jest.Mock).mockReset(); responseRepository = { getResponseForRequest: jest.fn().mockResolvedValue(null), persistResponseForRequest: jest.fn().mockResolvedValue(null), @@ -26,9 +30,11 @@ describe('when the response is in the cache', () => { it('should return the response from the cache, without using the network', async () => { // Given + const delay = 1000; const useCase = new RespondToRequest({ responseRepository, networkService, + delay, }); const method = 'GET'; const url = '/beers/1'; @@ -47,6 +53,8 @@ describe('when the response is in the cache', () => { expect(responseRepository.getResponseForRequest).toHaveBeenCalledWith( new Request(method, url, headers, body) ); + expect(wait).toHaveBeenCalledTimes(1); + expect(wait).toHaveBeenCalledWith(1000); expect(networkService.executeRequest).not.toHaveBeenCalled(); }); @@ -63,9 +71,11 @@ describe('when no response is in the cache', () => { }); it('should fetch the reponse from the network and store it in the cache', async () => { + const delay = 1000; const useCase = new RespondToRequest({ responseRepository, networkService, + delay, }); const method = 'GET'; const url = '/beers/1'; @@ -92,5 +102,8 @@ describe('when no response is in the cache', () => { new Request(method, url, headers, body), new Response(200, { 'cache-control': 'something' }, 'some body') ); + + expect(wait).toHaveBeenCalledTimes(1); + expect(wait).toHaveBeenCalledWith(1000); }); }); diff --git a/src/domain/usecase/RespondToRequest.ts b/src/domain/usecase/RespondToRequest.ts index f50795f..fb4493a 100644 --- a/src/domain/usecase/RespondToRequest.ts +++ b/src/domain/usecase/RespondToRequest.ts @@ -1,4 +1,5 @@ import { logger } from '../../util/logger'; +import { wait } from '../../util/timers'; import { Method, Response, Request } from '../entity'; import { ResponseRepository } from '../repository'; @@ -7,6 +8,7 @@ import { NetworkService } from '../service'; interface Dependencies { responseRepository: ResponseRepository; networkService: NetworkService; + delay: number; } export interface Headers { @@ -16,10 +18,16 @@ export interface Headers { export class RespondToRequest { private responseRepository: ResponseRepository; private networkService: NetworkService; + private delay: number; - public constructor({ responseRepository, networkService }: Dependencies) { + public constructor({ + responseRepository, + networkService, + delay, + }: Dependencies) { this.responseRepository = responseRepository; this.networkService = networkService; + this.delay = delay; } public async execute( @@ -33,16 +41,22 @@ export class RespondToRequest { const cachedResponse = await this.responseRepository.getResponseForRequest( request ); + let response: Response; if (cachedResponse) { + response = cachedResponse; logger.debug('Using response from the cache'); - return cachedResponse; + } else { + logger.debug('Fetching response from the network'); + response = await this.networkService.executeRequest(request); + + await this.responseRepository.persistResponseForRequest( + request, + response + ); } - logger.debug('Fetching response from the network'); - const response = await this.networkService.executeRequest(request); - - await this.responseRepository.persistResponseForRequest(request, response); + await wait(this.delay); return response; } diff --git a/src/infrastructure/service/NetworkServiceAxios.ts b/src/infrastructure/service/NetworkServiceAxios.ts index 4d4252f..82649f1 100644 --- a/src/infrastructure/service/NetworkServiceAxios.ts +++ b/src/infrastructure/service/NetworkServiceAxios.ts @@ -6,16 +6,13 @@ import { Request, Response } from '../../domain/entity'; interface Dependencies { targetUrl: string; - delay: number; } export class NetworkServiceAxios implements NetworkService { private targetUrl: string; - private delay: number; - public constructor({ targetUrl, delay }: Dependencies) { + public constructor({ targetUrl }: Dependencies) { this.targetUrl = targetUrl; - this.delay = delay; } public async executeRequest(request: Request): Promise { @@ -27,8 +24,6 @@ export class NetworkServiceAxios implements NetworkService { // For now we do not support gzip delete headers['accept-encoding']; - await new Promise(resolve => setTimeout(resolve, this.delay)); - const axiosResponse = await axios({ data: request.body, url: `${this.targetUrl}${request.url}`, diff --git a/src/util/__mocks__/timers.ts b/src/util/__mocks__/timers.ts new file mode 100644 index 0000000..895ac86 --- /dev/null +++ b/src/util/__mocks__/timers.ts @@ -0,0 +1 @@ +export const wait = jest.fn(); diff --git a/src/util/timers.ts b/src/util/timers.ts new file mode 100644 index 0000000..4d5c97d --- /dev/null +++ b/src/util/timers.ts @@ -0,0 +1,2 @@ +export const wait = (time: number) => + new Promise(resolve => setTimeout(resolve, time));