Tests for TechDocsCache class
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
committed by
Eric Peterson
parent
693db1da54
commit
a16dce0433
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CacheClient, getVoidLogger } from '@backstage/backend-common';
|
||||
import { CacheInvalidationError, TechDocsCache } from './TechDocsCache';
|
||||
|
||||
const cached = (str: string): string => {
|
||||
return Buffer.from(str).toString('base64');
|
||||
};
|
||||
|
||||
describe('TechDocsCache', () => {
|
||||
let CacheUnderTest: TechDocsCache;
|
||||
let MockClient: jest.Mocked<CacheClient>;
|
||||
|
||||
beforeEach(() => {
|
||||
MockClient = {
|
||||
get: jest.fn(),
|
||||
set: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
CacheUnderTest = new TechDocsCache({
|
||||
cache: MockClient,
|
||||
logger: getVoidLogger(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
it('returns undefined if no response', async () => {
|
||||
const expectedPath = 'some/index.html';
|
||||
MockClient.get.mockResolvedValueOnce(undefined);
|
||||
|
||||
const actual = await CacheUnderTest.get(expectedPath);
|
||||
expect(MockClient.get).toHaveBeenCalledWith(expectedPath);
|
||||
expect(actual).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns undefined if cache get throws', async () => {
|
||||
const expectedPath = 'some/index.html';
|
||||
MockClient.get.mockRejectedValueOnce(new Error());
|
||||
|
||||
const actual = await CacheUnderTest.get(expectedPath);
|
||||
expect(actual).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns undefined if no response after 1s', async () => {
|
||||
const expectedPath = 'some/index.html';
|
||||
MockClient.get.mockImplementationOnce(() => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(cached('value')), 1500);
|
||||
});
|
||||
});
|
||||
const actual = await CacheUnderTest.get(expectedPath);
|
||||
expect(actual).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns data if cache get returns it', async () => {
|
||||
const expectedPath = 'some/index.html';
|
||||
MockClient.get.mockResolvedValueOnce(cached('expected value'));
|
||||
|
||||
const actual = await CacheUnderTest.get(expectedPath);
|
||||
expect(actual?.toString()).toBe('expected value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('set', () => {
|
||||
it('sets a base64-encoded string', async () => {
|
||||
const expectedPath = 'some/index.html';
|
||||
MockClient.set.mockResolvedValueOnce(undefined);
|
||||
|
||||
await CacheUnderTest.set(expectedPath, Buffer.from('some data'));
|
||||
expect(MockClient.set).toHaveBeenCalledWith(
|
||||
expectedPath,
|
||||
cached('some data'),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not throw if client throws', () => {
|
||||
MockClient.set.mockRejectedValueOnce(new Error());
|
||||
expect(() => CacheUnderTest.set('i.html', Buffer.from(''))).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalidate', () => {
|
||||
it('calls delete on client', async () => {
|
||||
const expectedPath = 'some/index.html';
|
||||
MockClient.delete.mockResolvedValueOnce(undefined);
|
||||
|
||||
await CacheUnderTest.invalidate(expectedPath);
|
||||
expect(MockClient.delete).toHaveBeenCalledWith(expectedPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalidateMultiple', () => {
|
||||
it('calls delete once per given path', async () => {
|
||||
const expectedPaths = ['one/index.html', 'two/index.html'];
|
||||
MockClient.delete.mockResolvedValue(undefined);
|
||||
|
||||
await CacheUnderTest.invalidateMultiple(expectedPaths);
|
||||
expect(MockClient.delete).toHaveBeenNthCalledWith(1, expectedPaths[0]);
|
||||
expect(MockClient.delete).toHaveBeenNthCalledWith(2, expectedPaths[1]);
|
||||
});
|
||||
|
||||
it('returns an array of as many paths provided', async () => {
|
||||
const expectedPaths = ['one/index.html', 'two/index.html'];
|
||||
MockClient.delete.mockResolvedValue(undefined);
|
||||
|
||||
const actual = await CacheUnderTest.invalidateMultiple(expectedPaths);
|
||||
expect(actual.length).toBe(2);
|
||||
});
|
||||
|
||||
it('calls delete on all paths even if the first rejects', async () => {
|
||||
const expectedPaths = ['one/index.html', 'two/index.html'];
|
||||
MockClient.delete.mockRejectedValueOnce(new Error());
|
||||
MockClient.delete.mockResolvedValueOnce(undefined);
|
||||
|
||||
await expect(
|
||||
CacheUnderTest.invalidateMultiple(expectedPaths),
|
||||
).rejects.toThrowError(CacheInvalidationError);
|
||||
expect(MockClient.delete).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('rejects with invalidations error response', async () => {
|
||||
const expectedPaths = ['one/index.html', 'two/index.html'];
|
||||
MockClient.delete.mockResolvedValueOnce(undefined);
|
||||
MockClient.delete.mockRejectedValueOnce(new Error());
|
||||
|
||||
await expect(
|
||||
CacheUnderTest.invalidateMultiple.bind(CacheUnderTest, expectedPaths),
|
||||
).rejects.toThrow(
|
||||
expect.objectContaining({ rejections: expect.arrayContaining([]) }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user