UNPKG

@p-j/eapi-middleware-cache

Version:

A cache middleware to work within an EAPI app. Check worker-eapi-template for context.

111 lines (87 loc) 5.09 kB
import { withCache } from './withCache' describe('withCache', () => { let successFn: jest.Mock<Response>, errorFn: jest.Mock<Response>, waitUntil: jest.Mock<boolean>, matchSpy: jest.SpyInstance, putSpy: jest.SpyInstance, cacheControl: string, varyHeaders: string[] beforeEach(async () => { successFn = jest.fn(() => new Response('ok', { status: 200 })) errorFn = jest.fn(() => new Response('ko', { status: 500 })) waitUntil = jest.fn(() => true) caches.delete('default') caches.default = await caches.open('default') matchSpy = jest.spyOn(caches.default, 'match') putSpy = jest.spyOn(caches.default, 'put') cacheControl = 'public, max-age=100' varyHeaders = ['Accept', 'Origin'] }) it('caches a successful response', async () => { const requestHandler = withCache({ cacheControl, varyHeaders })(successFn) const request = new Request('https://example.com') const event = Object.assign(new FetchEvent('fetch', { request }), { waitUntil }) const response = await requestHandler({ event, request, params: {} }) expect(successFn.mock.calls.length).toBe(1) // call the request handler expect(waitUntil.mock.calls.length).toBe(1) // make sure we've waited to put stuff in cache expect(response.headers.get('Cache-Control')).toBe(cacheControl) // the cache header is added expect(response.headers.get('Vary')).toBe(varyHeaders.join(',')) // the vary header is added expect(matchSpy).toHaveBeenCalled() // we looked in the cache to find some response expect(putSpy).toHaveBeenCalled() // we added the response to the cache as it was missing }) it('hits the cache when called twice', async () => { const request = new Request('https://example.com') const event = Object.assign(new FetchEvent('fetch', { request }), { waitUntil }) const requestHandler = await withCache({ cacheControl })(successFn) await requestHandler({ event, request, params: {} }) await requestHandler({ event, request, params: {} }) expect(successFn.mock.calls.length).toBe(1) // functions are called only for the 1st request expect(waitUntil.mock.calls.length).toBe(1) // functions are called only for the 1st request expect(matchSpy).toHaveBeenCalledTimes(2) // match is called for every request expect(putSpy).toHaveBeenCalledTimes(1) // cache was updated only for the 1st request }) it('does not hit the cache when no cache header or cdnTtl is given', async () => { const request = new Request('https://example.com') const event = Object.assign(new FetchEvent('fetch', { request }), { waitUntil }) const requestHandler = withCache()(successFn) const response1 = await requestHandler({ event, request, params: {} }) const response2 = await requestHandler({ event, request, params: {} }) expect(successFn.mock.calls.length).toBe(2) // handler function is called all the time expect(waitUntil.mock.calls.length).toBe(0) // waintUntil is never called expect(matchSpy).toHaveBeenCalledTimes(2) // match is called for every request expect(putSpy).toHaveBeenCalledTimes(0) // cache is never updated expect(response1.headers.get('Cache-Control')).toBe(null) // No Cache-Control is set on the responses expect(response2.headers.get('Cache-Control')).toBe(null) // No Cache-Control is set on the responses }) it('does not cache errors by default', async () => { const request = new Request('https://example.com') const event = Object.assign(new FetchEvent('fetch', { request }), { waitUntil }) const requestHandler = withCache({ cacheControl })(errorFn) const response = await requestHandler({ request, event, params: {} }) expect(errorFn.mock.calls.length).toBe(1) expect(waitUntil.mock.calls.length).toBe(0) expect(response.headers.get('Cache-Control')).toBe('no-store') expect(matchSpy).toHaveBeenCalled() expect(putSpy).not.toHaveBeenCalled() }) it('caches an error if asked to', async () => { const request = new Request('https://example.com') const event = Object.assign(new FetchEvent('fetch', { request }), { waitUntil }) const requestHandler = withCache({ cacheControl, cacheError: true })(errorFn) const response = await requestHandler({ request, event, params: {} }) expect(errorFn.mock.calls.length).toBe(1) expect(waitUntil.mock.calls.length).toBe(1) expect(response.headers.get('Cache-Control')).toBe(cacheControl) expect(matchSpy).toHaveBeenCalled() expect(putSpy).toHaveBeenCalled() }) it('combines Vary headers correctly', async () => { const handler = () => new Response('ok', { headers: { Vary: 'X-Custom-Header' } }) const requestHandler = withCache({ cacheControl, varyHeaders })(handler) const request = new Request('https://example.com') const event = Object.assign(new FetchEvent('fetch', { request }), { waitUntil }) const response = await requestHandler({ event, request, params: {} }) expect(response.headers.get('Vary')).toBe('X-Custom-Header,' + varyHeaders.join(',')) }) })