Skip to content

Commit

Permalink
#1532 - fix unit tests & refactor DOI api error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
louise-davies committed Mar 20, 2024
1 parent 5cbd215 commit 1f0a8ed
Show file tree
Hide file tree
Showing 16 changed files with 458 additions and 505 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exports[`DialogTitle should render correctly 1`] = `
Title
</p>
<button
aria-label="downloadConfirmDialog.close_arialabel"
aria-label="close_aria_label"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeLarge css-1f5482o-MuiButtonBase-root-MuiIconButton-root"
tabindex="0"
type="button"
Expand Down
15 changes: 11 additions & 4 deletions packages/datagateway-common/src/api/dataPublications.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,11 @@ describe('data publications api functions', () => {
id: 1,
pid: 'doi 1',
title: 'Test 1',
modTime: '2000-01-01',
createTime: '2000-01-01',
},
{
id: 2,
pid: 'doi 2',
title: 'Test 2',
modTime: '2000-01-02',
createTime: '2000-01-02',
},
];
history = createMemoryHistory({
Expand Down Expand Up @@ -309,7 +305,11 @@ describe('data publications api functions', () => {
},
],
},
dataCollectionDatasets: 'dataset',
dataCollectionDatafiles: 'datafile',
},
relatedItems: 'publication',
users: 'user',
},
'users',
'facility',
Expand Down Expand Up @@ -361,7 +361,11 @@ describe('data publications api functions', () => {
},
],
},
dataCollectionDatasets: 'dataset',
dataCollectionDatafiles: 'datafile',
},
relatedItems: 'publication',
users: 'user',
},
'users',
'facility',
Expand Down Expand Up @@ -555,6 +559,7 @@ describe('data publications api functions', () => {
await waitFor(() => result.current.isSuccess);

params.append('order', JSON.stringify('name asc'));
params.append('order', JSON.stringify('title desc'));
params.append('order', JSON.stringify('id asc'));
params.append(
'where',
Expand Down Expand Up @@ -632,6 +637,7 @@ describe('data publications api functions', () => {
await waitFor(() => result.current.isSuccess);

params.append('order', JSON.stringify('name asc'));
params.append('order', JSON.stringify('title desc'));
params.append('order', JSON.stringify('id asc'));
params.append(
'where',
Expand Down Expand Up @@ -677,6 +683,7 @@ describe('data publications api functions', () => {
await waitFor(() => result.current.isSuccess);

params.append('order', JSON.stringify('name asc'));
params.append('order', JSON.stringify('title desc'));
params.append('order', JSON.stringify('id asc'));
params.append(
'where',
Expand Down
242 changes: 242 additions & 0 deletions packages/datagateway-common/src/api/dois.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { renderHook, act } from '@testing-library/react-hooks';
import axios, { AxiosError, AxiosHeaders } from 'axios';
import { handleDOIAPIError, useCheckUser } from '.';
import { createReactQueryWrapper } from '../setupTests';
import { InvalidateTokenType } from '../state/actions/actions.types';
import { setLogger } from 'react-query';
import log from 'loglevel';

// silence react-query errors
setLogger({
log: console.log,
warn: console.warn,
error: jest.fn(),
});

jest.mock('loglevel');

describe('handleDOIAPIError', () => {
const localStorageGetItemMock = jest.spyOn(
window.localStorage.__proto__,
'getItem'
);
let events: CustomEvent<{
detail: { type: string; payload?: unknown };
}>[] = [];
let error: AxiosError;

beforeEach(() => {
events = [];

document.dispatchEvent = (e: Event) => {
events.push(
e as CustomEvent<{ detail: { type: string; payload?: unknown } }>
);
return true;
};

const headers = {} as AxiosHeaders;
const config = {
url: 'https://example.com',
headers,
};
error = {
isAxiosError: true,
config,
response: {
data: { message: 'Test error message (response data)' },
status: 401,
statusText: 'Unauthorized',
headers: {},
config,
},
name: 'Test error name',
message: 'Test error message',
toJSON: jest.fn(),
};
});

afterEach(() => {
jest.clearAllMocks();
localStorageGetItemMock.mockReset();
});

it('should handle 401 by broadcasting an invalidate token message with autologin being true', async () => {
localStorageGetItemMock.mockImplementation((name) => {
return name === 'autoLogin' ? 'true' : null;
});

handleDOIAPIError(error);

expect(log.error).toHaveBeenCalledWith(error);
expect(events.length).toBe(1);
expect(events[0].detail).toEqual({
type: InvalidateTokenType,
payload: {
severity: 'error',
message: 'Your session has expired, please reload the page',
},
});
});

it('should handle 401 by broadcasting an invalidate token message with autologin being false & not log if false logging condition given', async () => {
localStorageGetItemMock.mockImplementation((name) => {
return name === 'autoLogin' ? 'false' : null;
});

handleDOIAPIError(error, undefined, undefined, false);

expect(log.error).not.toHaveBeenCalled();
expect(events.length).toBe(1);
expect(events[0].detail).toEqual({
type: InvalidateTokenType,
payload: {
severity: 'error',
message: 'Your session has expired, please login again',
},
});
});

it('should handle other errors by not broadcasting a message & log if true logging condition given', async () => {
localStorageGetItemMock.mockImplementation((name) => {
return name === 'autoLogin' ? 'false' : null;
});

if (error.response) error.response.status = 400;
handleDOIAPIError(error, undefined, undefined, true);

expect(log.error).toHaveBeenCalledWith(error);
expect(events.length).toBe(0);
});
});

describe('doi api functions', () => {
afterEach(() => {
jest.clearAllMocks();
});

describe('useCheckUser', () => {
it('should check whether a user exists in ICAT', async () => {
axios.get = jest
.fn()
.mockResolvedValue({ data: { id: 1, name: 'user 1' } });

const { result, waitFor } = renderHook(
() => useCheckUser('user 1', '/doi-minter'),
{
wrapper: createReactQueryWrapper(),
}
);
expect(result.current.isIdle).toBe(true);
act(() => {
result.current.refetch();
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));

expect(result.current.data).toEqual({ id: 1, name: 'user 1' });
expect(axios.get).toHaveBeenCalledWith('/doi-minter/user/user 1', {
headers: { Authorization: 'Bearer null' },
});
});

it('should not retry 401 errors', async () => {
const error = {
message: 'Test error message',
response: {
status: 401,
},
};
axios.get = jest.fn().mockRejectedValue(error);

const { result, waitFor } = renderHook(
() => useCheckUser('user 1', '/doi-minter'),
{
wrapper: createReactQueryWrapper(),
}
);
expect(result.current.isIdle).toBe(true);
act(() => {
result.current.refetch();
});
await waitFor(() => expect(result.current.isError).toBe(true));

expect(log.error).toHaveBeenCalledWith(error);
expect(axios.get).toHaveBeenCalledTimes(1);
});

it('should not retry 404 errors', async () => {
const error = {
message: 'Test error message',
response: {
status: 404,
},
};
axios.get = jest.fn().mockRejectedValue(error);

const { result, waitFor } = renderHook(
() => useCheckUser('user 1', '/doi-minter'),
{
wrapper: createReactQueryWrapper(),
}
);
expect(result.current.isIdle).toBe(true);
act(() => {
result.current.refetch();
});
await waitFor(() => expect(result.current.isError).toBe(true));

expect(log.error).toHaveBeenCalledWith(error);
expect(axios.get).toHaveBeenCalledTimes(1);
});

it('should not retry 422 errors', async () => {
const error = {
message: 'Test error message',
response: {
status: 422,
},
};
axios.get = jest.fn().mockRejectedValue(error);

const { result, waitFor } = renderHook(
() => useCheckUser('user 1', '/doi-minter'),
{
wrapper: createReactQueryWrapper(),
}
);
expect(result.current.isIdle).toBe(true);
act(() => {
result.current.refetch();
});
await waitFor(() => expect(result.current.isError).toBe(true));

expect(log.error).toHaveBeenCalledWith(error);
expect(axios.get).toHaveBeenCalledTimes(1);
});

it('should retry other errors', async () => {
const error = {
message: 'Test error message',
response: {
status: 400,
},
};
axios.get = jest.fn().mockRejectedValue(error);

const { result, waitFor } = renderHook(
() => useCheckUser('user 1', '/doi-minter'),
{
wrapper: createReactQueryWrapper(),
}
);
expect(result.current.isIdle).toBe(true);
act(() => {
result.current.refetch();
});
await waitFor(() => expect(result.current.isError).toBe(true));

expect(log.error).toHaveBeenCalledWith(error);
expect(axios.get).toHaveBeenCalledTimes(4);
});
});
});
Loading

0 comments on commit 1f0a8ed

Please sign in to comment.