Skip to content

Commit

Permalink
Merge pull request #5648 from flexion/10553-bug
Browse files Browse the repository at this point in the history
10553: Process to update contact info fails if DAWSON is also updating other contact info at the same time
  • Loading branch information
jimlerza authored Dec 14, 2024
2 parents 9243ea2 + e4f7a08 commit 545dd03
Show file tree
Hide file tree
Showing 30 changed files with 884 additions and 34 deletions.
36 changes: 36 additions & 0 deletions shared/src/business/entities/factories/UserFactory.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Practitioner } from '@shared/business/entities/Practitioner';
import { ROLES } from '@shared/business/entities/EntityConstants';
import { User } from '@shared/business/entities/User';
import { UserFactory } from '@shared/business/entities/factories/UserFactory';

describe('UserFactory', () => {
describe('getClass', () => {
it('should return "Practitioner" class type if role is "privatePractitioner"', () => {
const TEST_USER = { role: ROLES.privatePractitioner };
const userFactory = new UserFactory(TEST_USER);
const classInstance = userFactory.getClass();
expect(classInstance).toEqual(Practitioner);
});

it('should return "Practitioner" class type if role is "irsPractitioner"', () => {
const TEST_USER = { role: ROLES.irsPractitioner };
const userFactory = new UserFactory(TEST_USER);
const classInstance = userFactory.getClass();
expect(classInstance).toEqual(Practitioner);
});

it('should return "Practitioner" class type if role is "inactivePractitioner"', () => {
const TEST_USER = { role: ROLES.inactivePractitioner };
const userFactory = new UserFactory(TEST_USER);
const classInstance = userFactory.getClass();
expect(classInstance).toEqual(Practitioner);
});

it('should return "User" class type if role is "admin"', () => {
const TEST_USER = { role: ROLES.admin };
const userFactory = new UserFactory(TEST_USER);
const classInstance = userFactory.getClass();
expect(classInstance).toEqual(User);
});
});
});
27 changes: 27 additions & 0 deletions shared/src/business/entities/factories/UserFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Practitioner } from '@shared/business/entities/Practitioner';
import { ROLES, Role } from '@shared/business/entities/EntityConstants';
import { User } from '@shared/business/entities/User';

type MinimalFactoryInfo = {
role: Role;
};

export class UserFactory {
private rawUser: MinimalFactoryInfo;

constructor(rawUser: MinimalFactoryInfo) {
this.rawUser = rawUser;
}

public getClass(): typeof User | typeof Practitioner {
if (
this.rawUser.role === ROLES.privatePractitioner ||
this.rawUser.role === ROLES.irsPractitioner ||
this.rawUser.role === ROLES.inactivePractitioner
) {
return Practitioner;
}

return User;
}
}
4 changes: 2 additions & 2 deletions shared/src/proxies/users/verifyUserPendingEmailProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { put } from '../requests';
*/
export const verifyUserPendingEmailInteractor = (
applicationContext,
{ token },
) => {
{ token }: { token: string },
): Promise<void> => {
return put({
applicationContext,
body: {
Expand Down
9 changes: 9 additions & 0 deletions web-api/elasticsearch/efcms-case-mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ export const efcmsCaseMappings = {
'indexedTimestamp.N': {
type: 'text',
},
'irsPractitioners.L.M.email.S': {
type: 'keyword',
},
'irsPractitioners.L.M.userId.S': {
type: 'keyword',
},
Expand All @@ -89,6 +92,9 @@ export const efcmsCaseMappings = {
'petitioners.L.M.countryType.S': {
type: 'keyword',
},
'petitioners.L.M.email.S': {
type: 'keyword',
},
'petitioners.L.M.name.S': {
type: 'text',
},
Expand All @@ -104,6 +110,9 @@ export const efcmsCaseMappings = {
'preferredTrialCity.S': {
type: 'keyword',
},
'privatePractitioners.L.M.email.S': {
type: 'keyword',
},
'privatePractitioners.L.M.userId.S': {
type: 'keyword',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import {
UserStatusType,
} from '@aws-sdk/client-cognito-identity-provider';
import { MESSAGE_TYPES } from '@web-api/gateways/worker/workerRouter';
import { MOCK_PRACTITIONER } from '@shared/test/mockUsers';
import { MOCK_PRACTITIONER, petitionerUser } from '@shared/test/mockUsers';
import {
ROLES,
Role,
SERVICE_INDICATOR_TYPES,
} from '../../../../../shared/src/business/entities/EntityConstants';
import { UserRecord } from '@web-api/persistence/dynamo/dynamoTypes';
import { applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext';
import { changePasswordInteractor } from './changePasswordInteractor';
import {
changePasswordInteractor,
updateUserPendingEmailRecord,
} from './changePasswordInteractor';
import jwt from 'jsonwebtoken';

describe('changePasswordInteractor', () => {
Expand Down Expand Up @@ -326,3 +329,45 @@ describe('changePasswordInteractor', () => {
});
});
});

describe('updateUserPendingEmailRecord', () => {
beforeEach(() => {
applicationContext
.getPersistenceGateway()
.updateUser.mockResolvedValue(null);
});

it('should set isUpdatingInformation to true if flag is enabled', async () => {
await updateUserPendingEmailRecord(applicationContext, {
setIsUpdatingInformation: true,
user: {
...petitionerUser,
isUpdatingInformation: false,
},
});

const updateUserCalls =
applicationContext.getPersistenceGateway().updateUser.mock.calls;
expect(updateUserCalls.length).toEqual(1);
expect(updateUserCalls[0][0].user).toMatchObject({
isUpdatingInformation: true,
});
});

it('should not update isUpdatingInformation property when flag is disabled', async () => {
await updateUserPendingEmailRecord(applicationContext, {
setIsUpdatingInformation: false,
user: {
...petitionerUser,
isUpdatingInformation: false,
},
});

const updateUserCalls =
applicationContext.getPersistenceGateway().updateUser.mock.calls;
expect(updateUserCalls.length).toEqual(1);
expect(updateUserCalls[0][0].user).toMatchObject({
isUpdatingInformation: false,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ export const changePasswordInteractor = async (

export const updateUserPendingEmailRecord = async (
applicationContext: ServerApplicationContext,
{ user }: { user: RawUser },
{
setIsUpdatingInformation = false,
user,
}: { user: RawUser; setIsUpdatingInformation?: boolean },
): Promise<{ updatedUser: RawPractitioner | RawUser }> => {
let userEntity;

Expand All @@ -150,6 +153,8 @@ export const updateUserPendingEmailRecord = async (
});
}

if (setIsUpdatingInformation) userEntity.isUpdatingInformation = true;

const rawUser = userEntity.validate().toRawObject();
await applicationContext.getPersistenceGateway().updateUser({
applicationContext,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import {
MAX_ITERATIONS,
queueEmailUpdateAssociatedCasesWorker,
} from '@web-api/business/useCases/user/queueEmailUpdateAssociatedCasesWorker';
import { RawUser } from '@shared/business/entities/User';
import { applicationContext } from '@shared/business/test/createTestApplicationContext';
import { mockPetitionerUser } from '@shared/test/mockAuthUsers';
import { petitionerUser } from '@shared/test/mockUsers';
import { sleep } from '@shared/tools/helpers';

describe('queueEmailUpdateAssociatedCasesWorker', () => {
let TEST_USER: RawUser;
let RESOLVER: Function;

beforeEach(() => {
TEST_USER = {
...petitionerUser,
isUpdatingInformation: true,
};

applicationContext.getPersistenceGateway().updateUser.mockReturnValue(null);

applicationContext
.getUseCases()
.queueUpdateAssociatedCasesWorker.mockReturnValue(null);

applicationContext
.getPersistenceGateway()
.getCasesByEmailTotal.mockImplementation(
() =>
new Promise(resolve => {
RESOLVER = resolve;
}),
);

applicationContext.getUtilities().sleep.mockImplementation(() => {});
});

it('should disable user flag and short circuit if there is no associated cases to user', async () => {
applicationContext
.getPersistenceGateway()
.getDocketNumbersByUser.mockReturnValue([]);

await queueEmailUpdateAssociatedCasesWorker(
applicationContext,
{ user: TEST_USER },
mockPetitionerUser,
);

const updateUserCalls =
applicationContext.getPersistenceGateway().updateUser.mock.calls;
expect(updateUserCalls.length).toEqual(1);
expect(updateUserCalls[0][0].user).toMatchObject({
isUpdatingInformation: false,
});

const queueUpdateAssociatedCasesWorkerCalls =
applicationContext.getUseCases().queueUpdateAssociatedCasesWorker.mock
.calls;
expect(queueUpdateAssociatedCasesWorkerCalls.length).toEqual(0);
});

function assertFunctionCalls(expectedCount: number) {
expect(
applicationContext.getPersistenceGateway().getDocketNumbersByUser.mock
.calls.length,
).toEqual(expectedCount + 1);

expect(
applicationContext.getPersistenceGateway().getCasesByEmailTotal.mock.calls
.length,
).toEqual(expectedCount);
}

it('should call "queueUpdateAssociatedCasesWorker" with user information and wait until all expected cases to update', async () => {
const TEST_DOCKER_NUMBERS = ['TEST_1', 'TEST_2', 'TEST_3', 'TEST_4'];
let COMPLETE_FLAG = false;
applicationContext
.getPersistenceGateway()
.getDocketNumbersByUser.mockReturnValue(TEST_DOCKER_NUMBERS);

void queueEmailUpdateAssociatedCasesWorker(
applicationContext,
{ user: TEST_USER },
mockPetitionerUser,
).then(() => {
const queueUpdateAssociatedCasesWorkerCalls =
applicationContext.getUseCases().queueUpdateAssociatedCasesWorker.mock
.calls;

expect(queueUpdateAssociatedCasesWorkerCalls.length).toEqual(1);
expect(queueUpdateAssociatedCasesWorkerCalls[0][1]).toEqual({
user: TEST_USER,
});
expect(queueUpdateAssociatedCasesWorkerCalls[0][2]).toEqual(
mockPetitionerUser,
);

const updateUserCalls =
applicationContext.getPersistenceGateway().updateUser.mock.calls;
expect(updateUserCalls.length).toEqual(1);
expect(updateUserCalls[0][0].user).toMatchObject({
isUpdatingInformation: false,
});

COMPLETE_FLAG = true;
});

await sleep(100);
assertFunctionCalls(1);
RESOLVER(0);

await sleep(50);
assertFunctionCalls(2);
RESOLVER(2);

await sleep(50);
assertFunctionCalls(3);
RESOLVER(TEST_DOCKER_NUMBERS.length);

await sleep(50);
expect(COMPLETE_FLAG).toEqual(true);
});

it('should call resolve the interactor when the max number of iterations is met', async () => {
applicationContext
.getPersistenceGateway()
.getCasesByEmailTotal.mockImplementation(() => {});

const TEST_DOCKER_NUMBERS = ['TEST_1', 'TEST_2', 'TEST_3', 'TEST_4'];
let COMPLETE_FLAG = false;
applicationContext
.getPersistenceGateway()
.getDocketNumbersByUser.mockReturnValue(TEST_DOCKER_NUMBERS);

void queueEmailUpdateAssociatedCasesWorker(
applicationContext,
{ user: TEST_USER },
mockPetitionerUser,
).then(() => {
COMPLETE_FLAG = true;
});

await sleep(100);
expect(COMPLETE_FLAG).toEqual(true);

const getCasesByEmailTotalCalls =
applicationContext.getPersistenceGateway().getCasesByEmailTotal.mock
.calls;

expect(getCasesByEmailTotalCalls.length).toEqual(MAX_ITERATIONS + 1);
});

it('should resolve the interactor when the there is an error thrown in the check method', async () => {
applicationContext
.getPersistenceGateway()
.getCasesByEmailTotal.mockImplementation(() => {
throw Error('TEST ERROR');
});

const TEST_DOCKER_NUMBERS = ['TEST_1', 'TEST_2', 'TEST_3', 'TEST_4'];
applicationContext
.getPersistenceGateway()
.getDocketNumbersByUser.mockReturnValue(TEST_DOCKER_NUMBERS);

await queueEmailUpdateAssociatedCasesWorker(
applicationContext,
{ user: TEST_USER },
mockPetitionerUser,
);

const updateUserCalls =
applicationContext.getPersistenceGateway().updateUser.mock.calls;
expect(updateUserCalls.length).toEqual(1);
expect(updateUserCalls[0][0].user).toMatchObject({
isUpdatingInformation: false,
});
});
});
Loading

0 comments on commit 545dd03

Please sign in to comment.