Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ bc79df4f82052918ae6bf69d36279e5dd391d61e:tests/test-team/auth/user.json:jwt:25
306d9ec55d3498b86d5506da9a90ac486fc66563:frontend/src/components/molecules/MessagePlanFallbackConditions/MessagePlanFallbackConditions.tsx:ipv4:92
a408293ecbc3a95684246fd16b111b6e09d6e194:lambdas/backend-client/src/__tests__/schemas/contact-details/email.test.ts:ipv4:80
a408293ecbc3a95684246fd16b111b6e09d6e194:lambdas/backend-client/src/__tests__/schemas/contact-details/email.test.ts:ipv4:81
9ff9d02e5f132ff57aaee7e28b4002c17c3041a3:.tool-versions:ipv4:27
184 changes: 172 additions & 12 deletions frontend/src/__tests__/utils/contact-details.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,179 @@
import { contactDetailApiClient } from 'nhs-notify-backend-client';
import { createContactDetail } from '@utils/contact-details';
/**
* @jest-environment node
*/
import { getSessionServer } from '@utils/amplify-utils';
import {
createContactDetail,
getVerifiedContactDetails,
} from '@utils/contact-details';
import { contactDetailApiClient } from 'nhs-notify-backend-client';
import { logger } from 'nhs-notify-web-template-management-utils/logger';
import type { ContactDetailDto } from 'nhs-notify-web-template-management-types';

jest.mock('nhs-notify-backend-client');
jest.mock('nhs-notify-web-template-management-utils/logger');
jest.mock('@utils/amplify-utils');
jest.mock('nhs-notify-backend-client');
jest.mock('nhs-notify-web-template-management-utils/logger', () => ({
logger: {
error: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
},
}));

const getSessionServerMock = jest.mocked(getSessionServer);
const contactDetailApiClientMock = jest.mocked(contactDetailApiClient);
const loggerMock = jest.mocked(logger);

describe('getVerifiedContactDetails', () => {
beforeEach(() => {
jest.resetAllMocks();
});

it('should throw when access token is missing', async () => {
getSessionServerMock.mockResolvedValueOnce({
accessToken: undefined,
clientId: undefined,
});

await expect(getVerifiedContactDetails('EMAIL')).rejects.toThrow(
'Failed to get access token'
);

expect(contactDetailApiClientMock.list).not.toHaveBeenCalled();
});

it('should return id and rawValue for verified contact details', async () => {
getSessionServerMock.mockResolvedValueOnce({
accessToken: 'token',
clientId: 'client-1',
});

const contactDetails = [
{
id: 'contact-1',
type: 'EMAIL',
status: 'VERIFIED',
value: 'one@example.com',
rawValue: 'oNe@example.com',
} as ContactDetailDto,
{
id: 'contact-2',
type: 'EMAIL',
status: 'VERIFIED',
value: 'two@example.com',
rawValue: 'Two@example.com',
} as ContactDetailDto,
];

beforeEach(() => {
jest.resetAllMocks();
contactDetailApiClientMock.list.mockResolvedValueOnce({
data: contactDetails,
});

const result = await getVerifiedContactDetails('EMAIL');

expect(contactDetailApiClientMock.list).toHaveBeenCalledWith('token', {
type: 'EMAIL',
status: 'VERIFIED',
});

expect(result).toEqual([
{
id: 'contact-1',
rawValue: 'oNe@example.com',
},
{
id: 'contact-2',
rawValue: 'Two@example.com',
},
]);
});

it('should log and filter out invalid contact details from API response', async () => {
getSessionServerMock.mockResolvedValueOnce({
accessToken: 'token',
clientId: 'client-1',
});

jest
.mocked(getSessionServer)
.mockResolvedValue({ accessToken: 'access-token' });
const contactDetails = [
{
id: 'contact-1',
type: 'EMAIL',
status: 'VERIFIED',
value: 'one@example.com',
rawValue: 'One@example.com',
},
{
id: 'contact-2',
type: 'EMAIL',
status: 'VERIFIED',
rawValue: 'Two@example.com',
},
] as ContactDetailDto[];

contactDetailApiClientMock.list.mockResolvedValueOnce({
data: contactDetails,
});

const result = await getVerifiedContactDetails('EMAIL');

expect(result).toEqual([
{
id: 'contact-1',
rawValue: 'One@example.com',
},
]);

expect(loggerMock.error).toHaveBeenCalledWith(
'Listed invalid contact detail',
expect.objectContaining({
clientId: 'client-1',
type: 'EMAIL',
})
);
});

it('should return empty list and log when API call fails', async () => {
getSessionServerMock.mockResolvedValueOnce({
accessToken: 'token',
clientId: 'client-1',
});

const error = {
errorMeta: {
code: 500,
description: 'Unexpected error',
},
};

contactDetailApiClientMock.list.mockResolvedValueOnce({
error,
});

const result = await getVerifiedContactDetails('EMAIL');

expect(result).toEqual([]);
expect(loggerMock.error).toHaveBeenCalledWith(
'Failed to get verified contact details',
{
clientId: 'client-1',
type: 'EMAIL',
error,
}
);
});
});

describe('createContactDetail', () => {
beforeEach(() => {
jest.mocked(contactDetailApiClient.create).mockImplementation((input) =>
jest.resetAllMocks();

getSessionServerMock.mockResolvedValue({
accessToken: 'access-token',
clientId: 'client-1',
});

contactDetailApiClientMock.create.mockImplementation((input) =>
Promise.resolve({
data: {
...input,
Expand Down Expand Up @@ -53,7 +210,10 @@ describe('createContactDetail', () => {
});

it('errors if unable to get an access token', async () => {
jest.mocked(getSessionServer).mockResolvedValueOnce({});
getSessionServerMock.mockResolvedValueOnce({
accessToken: undefined,
clientId: undefined,
});

await expect(() =>
createContactDetail({
Expand All @@ -66,7 +226,7 @@ describe('createContactDetail', () => {
});

it('errors if api client call returns an error', async () => {
jest.mocked(contactDetailApiClient.create).mockResolvedValueOnce({
contactDetailApiClientMock.create.mockResolvedValueOnce({
error: {
errorMeta: {
code: 500,
Expand Down
Loading
Loading