add kubernetes custom hook tests

Signed-off-by: mclarke <mclarke@spotify.com>
This commit is contained in:
mclarke
2021-03-11 14:44:10 +00:00
parent 9804a5273e
commit b16a480275
6 changed files with 291 additions and 39 deletions
+1
View File
@@ -55,6 +55,7 @@
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/react-hooks": "^5.1.0",
"@testing-library/user-event": "^12.0.7",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32",
+1 -1
View File
@@ -57,7 +57,7 @@ type DeploymentTablesProps = {
};
export const PodsTable = ({ pods }: DeploymentTablesProps) => {
const tableStyle: React.CSSProperties = {
const tableStyle = {
minWidth: '0',
width: '100%',
};
@@ -0,0 +1,182 @@
/*
* Copyright 2021 Spotify AB
*
* 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 { useKubernetesObjects } from './useKubernetesObjects';
import { Entity } from '@backstage/catalog-model';
import { useApi } from '@backstage/core';
import { renderHook } from '@testing-library/react-hooks';
jest.mock('@backstage/core');
const entity = {
metadata: {
name: 'some-entity',
},
} as Entity;
const entityWithAuthToken = {
auth: {
google: 'some-token',
},
entity,
};
const mockResponse = {
items: [
{
cluster: { name: 'some-cluster' },
resources: [
{
type: 'pods',
resources: [
{
metadata: {
name: 'some-pod',
},
},
],
},
],
errors: [],
},
],
};
const getClustersResponse = [
{
name: 'cluster-a',
authProvider: 'google',
},
{
name: 'cluster-b',
authProvider: 'authprovider2',
},
];
describe('useKubernetesObjects', () => {
const mockGetClusters = jest.fn();
const mockGetObjectsByEntity = jest.fn();
const mockDecorateRequestBodyForAuth = jest.fn();
const expectMocksCalledCorrectly = () => {
expect(mockGetClusters).toBeCalledTimes(1);
expect(mockGetClusters).toHaveBeenLastCalledWith();
expect(mockDecorateRequestBodyForAuth).toBeCalledTimes(2);
expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith('google', {
entity,
});
expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith(
'authprovider2',
entityWithAuthToken,
);
expect(mockGetObjectsByEntity).toBeCalledTimes(1);
expect(mockGetObjectsByEntity).toHaveBeenLastCalledWith(
entityWithAuthToken,
);
};
afterEach(() => {
jest.resetAllMocks();
});
it('should return objects', async () => {
(useApi as any).mockReturnValue({
getClusters: mockGetClusters.mockResolvedValue(getClustersResponse),
getObjectsByEntity: mockGetObjectsByEntity.mockResolvedValue(
mockResponse,
),
decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth.mockResolvedValue(
entityWithAuthToken,
),
});
const { result, waitForNextUpdate } = renderHook(() =>
useKubernetesObjects(entity),
);
await waitForNextUpdate();
expect(result.current.error).toBeUndefined();
expect(result.current.kubernetesObjects).toStrictEqual(mockResponse);
expectMocksCalledCorrectly();
});
it('should return error when getObjectsByEntity throws', async () => {
(useApi as any).mockReturnValue({
getClusters: mockGetClusters.mockResolvedValue(getClustersResponse),
getObjectsByEntity: mockGetObjectsByEntity.mockRejectedValue({
message: 'some error',
}),
decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth.mockResolvedValue(
entityWithAuthToken,
),
});
const { result, waitForNextUpdate } = renderHook(() =>
useKubernetesObjects(entity),
);
await waitForNextUpdate();
expect(result.current.error).toBe('some error');
expect(result.current.kubernetesObjects).toBeUndefined();
expectMocksCalledCorrectly();
});
it('should return error when getClusters throws', async () => {
(useApi as any).mockReturnValue({
getClusters: mockGetClusters.mockRejectedValue({ message: 'some-error' }),
getObjectsByEntity: mockGetObjectsByEntity,
decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth,
});
const { result, waitForNextUpdate } = renderHook(() =>
useKubernetesObjects(entity),
);
await waitForNextUpdate();
expect(result.current.error).toBe('some-error');
expect(result.current.kubernetesObjects).toBeUndefined();
expect(mockGetClusters).toBeCalledTimes(1);
expect(mockGetClusters).toHaveBeenLastCalledWith();
expect(mockDecorateRequestBodyForAuth).toBeCalledTimes(0);
expect(mockGetObjectsByEntity).toBeCalledTimes(0);
});
it('should return error when decorateRequestBodyForAuth throws', async () => {
(useApi as any).mockReturnValue({
getClusters: mockGetClusters.mockResolvedValue(getClustersResponse),
decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth.mockRejectedValue(
{ message: 'some-error' },
),
getObjectsByEntity: mockGetObjectsByEntity,
});
const { result, waitForNextUpdate } = renderHook(() =>
useKubernetesObjects(entity),
);
await waitForNextUpdate();
expect(result.current.error).toBe('some-error');
expect(result.current.kubernetesObjects).toBeUndefined();
expect(mockGetClusters).toBeCalledTimes(1);
expect(mockGetClusters).toHaveBeenLastCalledWith();
expect(mockDecorateRequestBodyForAuth).toBeCalledTimes(1);
expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith('google', {
entity,
});
expect(mockGetObjectsByEntity).toBeCalledTimes(0);
});
});
@@ -40,7 +40,15 @@ export const useKubernetesObjects = (entity: Entity): KubernetesObjects => {
useEffect(() => {
(async () => {
const clusters = await kubernetesApi.getClusters();
let clusters = [];
try {
clusters = await kubernetesApi.getClusters();
} catch (e) {
setError(e.message);
return;
}
const authProviders: string[] = [
...new Set(clusters.map(c => c.authProvider)),
];
@@ -50,21 +58,25 @@ export const useKubernetesObjects = (entity: Entity): KubernetesObjects => {
};
for (const authProviderStr of authProviders) {
// Multiple asyncs done sequentially instead of all at once to prevent same requestBody from being modified simultaneously
requestBody = await kubernetesAuthProvidersApi.decorateRequestBodyForAuth(
authProviderStr,
requestBody,
);
try {
requestBody = await kubernetesAuthProvidersApi.decorateRequestBodyForAuth(
authProviderStr,
requestBody,
);
} catch (e) {
setError(e.message);
return;
}
}
// TODO: Add validation on contents/format of requestBody
kubernetesApi
.getObjectsByEntity(requestBody)
.then(result => {
setKubernetesObjects(result);
})
.catch(e => {
setError(e.message);
});
try {
setKubernetesObjects(
await kubernetesApi.getObjectsByEntity(requestBody),
);
} catch (e) {
setError(e.message);
return;
}
})();
/* eslint-disable react-hooks/exhaustive-deps */
}, [entity.metadata.name, kubernetesApi, kubernetesAuthProvidersApi]);