Skip to content

Commit

Permalink
feat(DTFS2-7796): post check your answers page (#4217)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarRobSoftwire authored Feb 12, 2025
1 parent b983df9 commit 43a23f2
Show file tree
Hide file tree
Showing 17 changed files with 415 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
const amendmentId = new ObjectId().toString();

const { status } = await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus: 'a new status' })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus: 'a new status' })
.to(generateUrl(facilityId, amendmentId));

expect(status).toEqual(HttpStatusCode.NotFound);
Expand Down Expand Up @@ -104,7 +104,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
const anInvalidFacilityId = 'InvalidId';

const { body, status } = (await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus: 'a new status' })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus: 'a new status' })
.to(generateUrl(anInvalidFacilityId, amendmentId))) as ErrorResponse;

expect(status).toEqual(HttpStatusCode.BadRequest);
Expand All @@ -119,7 +119,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
const anInvalidAmendmentId = 'InvalidId';

const { body, status } = (await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus: 'a new status' })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus: 'a new status' })
.to(generateUrl(facilityId, anInvalidAmendmentId))) as ErrorResponse;

expect(status).toEqual(HttpStatusCode.BadRequest);
Expand All @@ -130,31 +130,11 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
});
});

it(`should return ${HttpStatusCode.BadRequest} when the deal id is invalid`, async () => {
const anInvalidDealId = 'dealId';

const { body, status } = (await testApi
.patch({
dealId: anInvalidDealId,
auditDetails: generatePortalAuditDetails(portalUserId),
newStatus: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL,
})
.to(generateUrl(facilityId, amendmentId))) as ErrorResponse;

expect(status).toEqual(HttpStatusCode.BadRequest);

expect(body).toEqual({
status: HttpStatusCode.BadRequest,
message: ['dealId: _id must be a valid mongo object id (custom)'],
code: API_ERROR_CODE.INVALID_PAYLOAD,
});
});

it(`should return ${HttpStatusCode.BadRequest} when the new status is invalid`, async () => {
const anInvalidStatus = 'a new status';

const { body, status } = (await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus: anInvalidStatus })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus: anInvalidStatus })
.to(generateUrl(facilityId, amendmentId))) as ErrorResponse;

expect(status).toEqual(HttpStatusCode.BadRequest);
Expand All @@ -175,7 +155,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
const aValidButNonExistentFacilityId = new ObjectId().toString();

const { body, status } = (await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.to(generateUrl(aValidButNonExistentFacilityId, amendmentId))) as ErrorResponse;

expect(status).toEqual(HttpStatusCode.NotFound);
Expand All @@ -189,7 +169,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
const aValidButNonExistentAmendmentId = new ObjectId().toString();

const { body, status } = (await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.to(generateUrl(facilityId, aValidButNonExistentAmendmentId))) as ErrorResponse;

expect(status).toEqual(HttpStatusCode.NotFound);
Expand All @@ -210,7 +190,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status

// Act
const { body, status } = (await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.to(generateUrl(facilityId, incompleteAmendmentId))) as ErrorResponse;

// Assert
Expand All @@ -222,9 +202,7 @@ describe('PATCH /v1/portal/facilities/:facilityId/amendments/:amendmentId/status
});

it(`should return ${HttpStatusCode.Ok} when the payload is valid & the amendment exists`, async () => {
const { status } = await testApi
.patch({ dealId, auditDetails: generatePortalAuditDetails(portalUserId), newStatus })
.to(generateUrl(facilityId, amendmentId));
const { status } = await testApi.patch({ auditDetails: generatePortalAuditDetails(portalUserId), newStatus }).to(generateUrl(facilityId, amendmentId));

expect(status).toEqual(HttpStatusCode.Ok);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,16 @@ export class PortalFacilityAmendmentService {
* @param params
* @param params.amendmentId - The amendment id
* @param params.facilityId - The facility id
* @param params.dealId - The deal id
* @param params.auditDetails - The audit details for the update operation.
* @returns {Promise<(import('@ukef/dtfs2-common').FacilityAmendmentWithUkefId)>} A promise that resolves when the update operation is complete.
*/
public static async submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
}: {
amendmentId: string;
facilityId: string;
dealId: string;
auditDetails: PortalAuditDetails;
}): Promise<FacilityAmendmentWithUkefId> {
await this.validateAmendmentIsComplete({ amendmentId, facilityId });
Expand All @@ -205,7 +202,13 @@ export class PortalFacilityAmendmentService {
status: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL,
};

await this.validateNoOtherAmendmentsUnderWayOnDeal({ dealId, amendmentId });
const existingAmendment = await TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId(facilityId, amendmentId);

if (!existingAmendment || existingAmendment.type === AMENDMENT_TYPES.TFM) {
throw new AmendmentNotFoundError(amendmentId, facilityId);
}

await this.validateNoOtherAmendmentsUnderWayOnDeal({ dealId: existingAmendment.dealId.toString(), amendmentId });

const facilityMongoId = new ObjectId(facilityId);
const amendmentMongoId = new ObjectId(amendmentId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AMENDMENT_TYPES, PORTAL_AMENDMENT_STATUS } from '@ukef/dtfs2-common';
import { AMENDMENT_TYPES, AmendmentNotFoundError, PORTAL_AMENDMENT_STATUS } from '@ukef/dtfs2-common';
import { ObjectId } from 'mongodb';
import { generatePortalAuditDetails } from '@ukef/dtfs2-common/change-stream';
import { aPortalFacilityAmendment } from '@ukef/dtfs2-common/mock-data-backend';
Expand All @@ -13,9 +13,8 @@ const mockValidateAmendmentIsComplete = jest.fn();

const amendmentId = new ObjectId().toString();
const facilityId = new ObjectId().toString();
const dealId = new ObjectId().toString();

const updatedAmendment = aPortalFacilityAmendment();
const updatedAmendment = aPortalFacilityAmendment({ status: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL });
const auditDetails = generatePortalAuditDetails(aPortalUser()._id);

describe('PortalFacilityAmendmentService', () => {
Expand Down Expand Up @@ -46,7 +45,6 @@ describe('PortalFacilityAmendmentService', () => {
await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

Expand All @@ -58,104 +56,102 @@ describe('PortalFacilityAmendmentService', () => {
});
});

it('should call PortalFacilityAmendmentService.validateNoOtherAmendmentsUnderWayOnDeal', async () => {
it('should call PortalFacilityAmendmentService.findOneAmendmentByFacilityIdAndAmendmentId', async () => {
// Act
await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

// Assert
expect(mockValidateNoOtherAmendmentsUnderWayOnDeal).toHaveBeenCalledTimes(1);
expect(mockValidateNoOtherAmendmentsUnderWayOnDeal).toHaveBeenCalledWith({
dealId,
amendmentId,
});
expect(mockFindOneAmendmentByFacilityIdAndAmendmentId).toHaveBeenCalledTimes(2);
expect(mockFindOneAmendmentByFacilityIdAndAmendmentId).toHaveBeenCalledWith(facilityId, amendmentId);
});

it('should call TfmFacilitiesRepo.updatePortalFacilityAmendmentByAmendmentId with the correct params', async () => {
it('should throw an AmendmentNotFoundError if no amendment is found when calling TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId', async () => {
// Arrange
mockFindOneAmendmentByFacilityIdAndAmendmentId.mockResolvedValueOnce(null);

// Act
await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
const returned = PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

// Assert
const expectedUpdate = {
status: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL,
};

expect(mockUpdatePortalFacilityAmendmentByAmendmentId).toHaveBeenCalledTimes(1);
expect(mockUpdatePortalFacilityAmendmentByAmendmentId).toHaveBeenCalledWith({
update: expectedUpdate,
facilityId: new ObjectId(facilityId),
amendmentId: new ObjectId(amendmentId),
auditDetails,
allowedStatuses: [PORTAL_AMENDMENT_STATUS.DRAFT],
});
await expect(returned).rejects.toThrow(new AmendmentNotFoundError(amendmentId, facilityId));
});

it('should call TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId with the correct params', async () => {
it(`should throw an AmendmentNotFoundError if an amendment without a ${AMENDMENT_TYPES.PORTAL} amendment type is returned from TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId`, async () => {
// Arrange
mockFindOneAmendmentByFacilityIdAndAmendmentId.mockResolvedValueOnce({ ...updatedAmendment, type: AMENDMENT_TYPES.TFM });

// Act
await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
const returned = PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

// Assert
expect(mockFindOneAmendmentByFacilityIdAndAmendmentId).toHaveBeenCalledTimes(1);
expect(mockFindOneAmendmentByFacilityIdAndAmendmentId).toHaveBeenCalledWith(new ObjectId(facilityId), new ObjectId(amendmentId));
await expect(returned).rejects.toThrow(new AmendmentNotFoundError(amendmentId, facilityId));
});

it('should return the result of TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId', async () => {
it('should call PortalFacilityAmendmentService.validateNoOtherAmendmentsUnderWayOnDeal', async () => {
// Arrange
const existingAmendment = aPortalFacilityAmendment();
mockFindOneAmendmentByFacilityIdAndAmendmentId.mockResolvedValueOnce(existingAmendment);

// Act
const expected = await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

// Assert
expect(expected).toEqual(updatedAmendment);
expect(mockValidateNoOtherAmendmentsUnderWayOnDeal).toHaveBeenCalledTimes(1);
expect(mockValidateNoOtherAmendmentsUnderWayOnDeal).toHaveBeenCalledWith({
dealId: existingAmendment.dealId.toString(),
amendmentId,
});
});

it('should throw an error if no amendment is found when calling TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId', async () => {
// Arrange
mockFindOneAmendmentByFacilityIdAndAmendmentId.mockResolvedValueOnce(null);

it('should call TfmFacilitiesRepo.updatePortalFacilityAmendmentByAmendmentId with the correct params', async () => {
// Act
const returned = PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

// Assert
await expect(returned).rejects.toThrow(new Error('Could not find amendment to return'));
});
const expectedUpdate = {
status: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL,
};

it(`should throw an error if an amendment without a ${AMENDMENT_TYPES.PORTAL} amendment type is returned from TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId`, async () => {
// Arrange
mockFindOneAmendmentByFacilityIdAndAmendmentId.mockResolvedValueOnce({ ...updatedAmendment, type: AMENDMENT_TYPES.TFM });
expect(mockUpdatePortalFacilityAmendmentByAmendmentId).toHaveBeenCalledTimes(1);
expect(mockUpdatePortalFacilityAmendmentByAmendmentId).toHaveBeenCalledWith({
update: expectedUpdate,
facilityId: new ObjectId(facilityId),
amendmentId: new ObjectId(amendmentId),
auditDetails,
allowedStatuses: [PORTAL_AMENDMENT_STATUS.DRAFT],
});
});

it('should return the result of TfmFacilitiesRepo.findOneAmendmentByFacilityIdAndAmendmentId', async () => {
// Act
const returned = PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
const expected = await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({
amendmentId,
facilityId,
dealId,
auditDetails,
});

// Assert
await expect(returned).rejects.toThrow(new Error('Could not find amendment to return'));
expect(expected).toEqual(updatedAmendment);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ import { patchAmendmentStatus, PatchSubmitAmendmentToCheckerRequest } from './pa

const amendmentId = 'amendmentId';
const facilityId = 'facilityId';
const dealId = 'dealId';

const mockUpdatedAmendment = { facilityId, dealId, type: AMENDMENT_TYPES.PORTAL, status: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL };
const mockUpdatedAmendment = { facilityId, type: AMENDMENT_TYPES.PORTAL, status: PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL };

const mockSubmitPortalFacilityAmendmentToChecker = jest.fn();

const generateHttpMocks = ({ auditDetails, newStatus }: { auditDetails: unknown; newStatus: string }) =>
createMocks<PatchSubmitAmendmentToCheckerRequest>({
params: { facilityId, amendmentId },
body: { auditDetails, dealId, newStatus },
body: { auditDetails, newStatus },
});

describe('patchAmendmentStatus', () => {
Expand Down Expand Up @@ -76,7 +75,7 @@ describe('patchAmendmentStatus', () => {
// Assert

expect(mockSubmitPortalFacilityAmendmentToChecker).toHaveBeenCalledTimes(1);
expect(mockSubmitPortalFacilityAmendmentToChecker).toHaveBeenCalledWith({ facilityId, amendmentId, dealId, auditDetails });
expect(mockSubmitPortalFacilityAmendmentToChecker).toHaveBeenCalledWith({ facilityId, amendmentId, auditDetails });
});

it(`should return ${HttpStatusCode.Ok} and the updated amendment`, async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export type PatchSubmitAmendmentToCheckerRequest = CustomExpressRequest<{
*/
export const patchAmendmentStatus = async (req: PatchSubmitAmendmentToCheckerRequest, res: Response) => {
const { facilityId, amendmentId } = req.params;
const { auditDetails, dealId, newStatus } = req.body;
const { auditDetails, newStatus } = req.body;

try {
validateAuditDetailsAndUserType(auditDetails, AUDIT_USER_TYPES.PORTAL);

switch (newStatus) {
case PORTAL_AMENDMENT_STATUS.READY_FOR_CHECKERS_APPROVAL: {
const updatedAmendment = await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({ facilityId, amendmentId, dealId, auditDetails });
const updatedAmendment = await PortalFacilityAmendmentService.submitPortalFacilityAmendmentToChecker({ facilityId, amendmentId, auditDetails });
return res.status(HttpStatusCode.Ok).send(updatedAmendment);
}
default: {
Expand Down
Loading

0 comments on commit 43a23f2

Please sign in to comment.