From 15843f7c8d19b4f31db2d07dcacf9fbfa6e0eca1 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Thu, 16 Jan 2025 14:18:45 -0500 Subject: [PATCH 1/6] feat(test) test for withdraw-rai email (#1031) * feat(test) test for withdrwa rai * remove attachments.ts on coverage * add respondto rai and fix event in changelog * change WithdrawRAI to withdraw-rai * prettier issue * clean the mock data * add back in ignoring mock-data --- lib/lambda/processEmailsHandler.test.ts | 158 +++++++++++++++++-- lib/lambda/search.test.ts | 2 +- lib/lambda/update/updatePackage.test.ts | 10 +- lib/libs/email/content/withdrawRai/index.tsx | 2 +- lib/libs/email/index.ts | 2 +- mocks/data/items.ts | 71 ++++++++- 6 files changed, 224 insertions(+), 21 deletions(-) diff --git a/lib/lambda/processEmailsHandler.test.ts b/lib/lambda/processEmailsHandler.test.ts index 91017c964..dace4fd42 100644 --- a/lib/lambda/processEmailsHandler.test.ts +++ b/lib/lambda/processEmailsHandler.test.ts @@ -4,112 +4,206 @@ import { SESClient } from "@aws-sdk/client-ses"; import { handler } from "./processEmails"; import { KafkaRecord, KafkaEvent } from "shared-types"; import { Authority } from "shared-types"; - +import { SIMPLE_ID, WITHDRAW_RAI_ITEM_B, WITHDRAW_RAI_ITEM_C } from "mocks"; const nms = "new-medicaid-submission"; const ncs = "new-chip-submission"; const tempExtension = "temp-extension"; const withdrawPackage = "withdraw-package"; const contractingInitial = "contracting-initial"; const capitatedInitial = "capitated-initial"; +const withdrawRai = "withdraw-rai"; +const respondToRai = "respond-to-rai"; describe("process emails Handler", () => { it.each([ - [`should send an email for ${nms} with ${Authority.MED_SPA}`, Authority.MED_SPA, nms], - [`should send an email for ${nms} with ${Authority.CHIP_SPA}`, Authority.CHIP_SPA, nms], - [`should send an email for ${nms} with ${Authority["1915b"]}`, Authority["1915b"], nms], - [`should send an email for ${nms} with ${Authority["1915c"]}`, Authority["1915c"], nms], - [`should send an email for ${ncs} with ${Authority.MED_SPA}`, Authority.MED_SPA, ncs], - [`should send an email for ${ncs} with ${Authority.CHIP_SPA}`, Authority.CHIP_SPA, ncs], - [`should send an email for ${ncs} with ${Authority["1915b"]}`, Authority["1915b"], ncs], - [`should send an email for ${ncs} with ${Authority["1915c"]}`, Authority["1915c"], ncs], + [ + `should send an email for ${respondToRai} with ${Authority.MED_SPA}`, + Authority.MED_SPA, + respondToRai, + SIMPLE_ID, + ], + [ + `should send an email for ${respondToRai} with ${Authority.CHIP_SPA}`, + Authority.CHIP_SPA, + respondToRai, + SIMPLE_ID, + ], + [ + `should send an email for ${respondToRai} with ${Authority["1915b"]}`, + Authority["1915b"], + respondToRai, + SIMPLE_ID, + ], + [ + `should send an email for ${respondToRai} with ${Authority["1915c"]}`, + Authority["1915c"], + respondToRai, + SIMPLE_ID, + ], + [ + `should send an email for ${nms} with ${Authority.MED_SPA}`, + Authority.MED_SPA, + nms, + SIMPLE_ID, + ], + [ + `should send an email for ${nms} with ${Authority.CHIP_SPA}`, + Authority.CHIP_SPA, + nms, + SIMPLE_ID, + ], + [ + `should send an email for ${nms} with ${Authority["1915b"]}`, + Authority["1915b"], + nms, + SIMPLE_ID, + ], + [ + `should send an email for ${nms} with ${Authority["1915c"]}`, + Authority["1915c"], + nms, + SIMPLE_ID, + ], + [ + `should send an email for ${ncs} with ${Authority.MED_SPA}`, + Authority.MED_SPA, + ncs, + SIMPLE_ID, + ], + [ + `should send an email for ${ncs} with ${Authority.CHIP_SPA}`, + Authority.CHIP_SPA, + ncs, + SIMPLE_ID, + ], + [ + `should send an email for ${ncs} with ${Authority["1915b"]}`, + Authority["1915b"], + ncs, + SIMPLE_ID, + ], + [ + `should send an email for ${ncs} with ${Authority["1915c"]}`, + Authority["1915c"], + ncs, + SIMPLE_ID, + ], [ `should send an email for ${tempExtension} with ${Authority.MED_SPA}`, Authority.MED_SPA, tempExtension, + SIMPLE_ID, ], [ `should send an email for ${tempExtension} with ${Authority.CHIP_SPA}`, Authority.CHIP_SPA, tempExtension, + SIMPLE_ID, ], [ `should send an email for ${tempExtension} with ${Authority["1915b"]}`, Authority["1915b"], tempExtension, + SIMPLE_ID, ], [ `should send an email for ${tempExtension} with ${Authority["1915c"]}`, Authority["1915c"], tempExtension, + SIMPLE_ID, ], [ `should send an email for ${withdrawPackage} with ${Authority.MED_SPA}`, Authority.MED_SPA, withdrawPackage, + SIMPLE_ID, ], [ `should send an email for ${withdrawPackage} with ${Authority.CHIP_SPA}`, Authority.CHIP_SPA, withdrawPackage, + SIMPLE_ID, ], [ `should send an email for ${withdrawPackage} for ${ncs} with ${Authority["1915b"]}`, Authority["1915b"], withdrawPackage, + SIMPLE_ID, ], [ `should send an email for ${withdrawPackage} with ${Authority["1915c"]}`, Authority["1915c"], withdrawPackage, + SIMPLE_ID, ], [ `should send an email for ${contractingInitial} with ${Authority.MED_SPA}`, Authority.MED_SPA, contractingInitial, + SIMPLE_ID, ], [ `should send an email for ${contractingInitial} with ${Authority.CHIP_SPA}`, Authority.CHIP_SPA, contractingInitial, + SIMPLE_ID, ], [ `should send an email for ${contractingInitial} with ${Authority["1915b"]}`, Authority["1915b"], contractingInitial, + SIMPLE_ID, ], [ `should send an email for ${contractingInitial} with ${Authority["1915c"]}`, Authority["1915c"], contractingInitial, + SIMPLE_ID, ], [ `should send an email for ${capitatedInitial} with ${Authority.MED_SPA}`, Authority.MED_SPA, capitatedInitial, + SIMPLE_ID, ], [ `should send an email for ${capitatedInitial} with ${Authority.CHIP_SPA}`, Authority.CHIP_SPA, capitatedInitial, + SIMPLE_ID, ], [ `should send an email for ${capitatedInitial} with ${Authority["1915b"]}`, Authority["1915b"], capitatedInitial, + SIMPLE_ID, ], [ `should send an email for ${capitatedInitial} with ${Authority["1915c"]}`, Authority["1915c"], capitatedInitial, + SIMPLE_ID, ], - ])("%s", async (_, auth, eventType) => { + [ + `should send an email for ${withdrawRai} with ${Authority["1915b"]}`, + Authority["1915b"], + withdrawRai, + WITHDRAW_RAI_ITEM_B, + ], + [ + `should send an email for ${withdrawRai} with ${Authority["1915c"]}`, + Authority["1915c"], + withdrawRai, + WITHDRAW_RAI_ITEM_C, + ], + ])("%s", async (_, auth, eventType, id) => { const callback = vi.fn(); const secSPY = vi.spyOn(SESClient.prototype, "send"); const mockEvent: KafkaEvent = { records: { "mock-topic": [ { - key: Buffer.from("VA").toString("base64"), + key: Buffer.from(id).toString("base64"), value: Buffer.from( JSON.stringify({ origin: "mako", @@ -132,3 +226,45 @@ describe("process emails Handler", () => { expect(secSPY).toHaveBeenCalledTimes(2); }); }); +describe("process emails Handler failures", () => { + it.each([ + [ + `should send an email for ${withdrawRai} with ${Authority["1915b"]} and fail due to not finding it`, + Authority["1915b"], + withdrawRai, + SIMPLE_ID, + ], + [ + `should send an email for ${withdrawRai} with ${Authority["1915c"]} and fail due to not finding it`, + Authority["1915c"], + withdrawRai, + SIMPLE_ID, + ], + ])("%s", async (_, auth, eventType, id = SIMPLE_ID) => { + const callback = vi.fn(); + const mockEvent: KafkaEvent = { + records: { + "mock-topic": [ + { + key: Buffer.from(id).toString("base64"), + value: Buffer.from( + JSON.stringify({ + origin: "mako", + event: eventType, + authority: auth, + }), + ).toString("base64"), + headers: {}, + timestamp: 1732645041557, + offset: "0", + partition: 0, + topic: "mock-topic", + } as unknown as KafkaRecord, + ], + }, + eventSource: "", + bootstrapServers: "", + }; + await expect(() => handler(mockEvent, {} as Context, callback)).rejects.toThrow(); + }); +}); diff --git a/lib/lambda/search.test.ts b/lib/lambda/search.test.ts index 4b49522ca..02532a03f 100644 --- a/lib/lambda/search.test.ts +++ b/lib/lambda/search.test.ts @@ -29,7 +29,7 @@ describe("getSearchData Handler", () => { const body = JSON.parse(res.body); expect(body).toBeTruthy(); expect(body?.hits?.hits).toBeTruthy(); - expect(body?.hits?.hits?.length).toEqual(14); + expect(body?.hits?.hits?.length).toEqual(16); }); it("should handle errors during processing", async () => { diff --git a/lib/lambda/update/updatePackage.test.ts b/lib/lambda/update/updatePackage.test.ts index df208f9ae..c95f792f2 100644 --- a/lib/lambda/update/updatePackage.test.ts +++ b/lib/lambda/update/updatePackage.test.ts @@ -7,7 +7,7 @@ import { EXISTING_ITEM_PENDING_ID, CAPITATED_INITIAL_ITEM_ID, CAPITATED_INITIAL_NEW_ITEM_ID, - WEIRD_ID, + SIMPLE_ID, } from "mocks"; vi.mock("libs/handler-lib", () => ({ response: vi.fn((data) => data), @@ -204,7 +204,7 @@ describe("handler", () => { it("should fail to update a package with bad existing id format", async () => { const noActionevent = { body: JSON.stringify({ - packageId: WEIRD_ID, + packageId: SIMPLE_ID, action: "update-id", changeReason: "Nunya", updatedId: "SS-120", @@ -223,7 +223,7 @@ describe("handler", () => { process.env.topicName = ""; const noActionevent = { body: JSON.stringify({ - packageId: WEIRD_ID, + packageId: SIMPLE_ID, action: "update-values", changeReason: "Nunya", updatedFields: {}, @@ -240,7 +240,7 @@ describe("handler", () => { it("should fail to update a package - No valid fields ", async () => { const noActionevent = { body: JSON.stringify({ - packageId: WEIRD_ID, + packageId: SIMPLE_ID, action: "update-values", changeReason: "Nunya", updatedFields: { badfield: "nothing" }, @@ -257,7 +257,7 @@ describe("handler", () => { it("should fail to update a package - Id can not be updated ", async () => { const noActionevent = { body: JSON.stringify({ - packageId: WEIRD_ID, + packageId: SIMPLE_ID, action: "update-values", changeReason: "Nunya", updatedFields: { id: "cant update ID here" }, diff --git a/lib/libs/email/content/withdrawRai/index.tsx b/lib/libs/email/content/withdrawRai/index.tsx index 5dde78064..a8e34e6a4 100644 --- a/lib/libs/email/content/withdrawRai/index.tsx +++ b/lib/libs/email/content/withdrawRai/index.tsx @@ -5,7 +5,7 @@ import { render } from "@react-email/render"; import { EmailProcessingError } from "libs/email/errors"; const getWithdrawRaiEvent = async (id: string) => { - const event = await getLatestMatchingEvent(id, "WithdrawRai"); + const event = await getLatestMatchingEvent(id, "withdraw-rai"); if (!event) { return null; diff --git a/lib/libs/email/index.ts b/lib/libs/email/index.ts index da2eea2ff..ca21f9e4d 100644 --- a/lib/libs/email/index.ts +++ b/lib/libs/email/index.ts @@ -115,7 +115,7 @@ export async function getLatestMatchingEvent( } // Filter matching events - const events = item.hits.hits.filter((event) => event._source.actionType === actionType); + const events = item.hits.hits.filter((event) => event._source.event === actionType); // Check if any matching events were found if (!events.length) { diff --git a/mocks/data/items.ts b/mocks/data/items.ts index 50b9f1e05..458418a0a 100644 --- a/mocks/data/items.ts +++ b/mocks/data/items.ts @@ -17,7 +17,7 @@ export const HI_TEST_ITEM_ID = "HI-0000.R00.00"; export const CAPITATED_INITIAL_ITEM_ID = "SS-2234.R00.00"; export const CAPITATED_INITIAL_NEW_ITEM_ID = "SS-1235.R00.00"; export const CAPITATED_AMEND_ITEM_ID = "VA-2234.R11.01"; -export const WEIRD_ID = "VA"; +export const SIMPLE_ID = "VA"; export const CONTRACTING_INITIAL_ITEM_ID = "MD-007.R00.00"; export const CONTRACTING_AMEND_ITEM_ID = "MD-007.R00.01"; export const MISSING_CHANGELOG_ITEM_ID = "MD-008.R00.00"; @@ -26,6 +26,8 @@ export const INITIAL_RELEASE_APPK_ITEM_ID = "MD-010.R00.01"; export const EXISTING_ITEM_APPROVED_APPK_ITEM_ID = "MD-012.R00.01"; export const SUBMISSION_ERROR_ITEM_ID = "Throw Submission Error"; export const GET_ERROR_ITEM_ID = "Throw Get Item Error"; +export const WITHDRAW_RAI_ITEM_B = "VA-2234.R11.02"; +export const WITHDRAW_RAI_ITEM_C = "VA-2234.R11.03"; const items: Record = { [EXISTING_ITEM_ID]: { @@ -37,7 +39,7 @@ const items: Record = { actionType: "New", }, }, - [WEIRD_ID]: { + [SIMPLE_ID]: { _id: EXISTING_ITEM_ID, found: true, _source: { @@ -399,6 +401,71 @@ const items: Record = { ], }, }, + [WITHDRAW_RAI_ITEM_B]: { + _id: WITHDRAW_RAI_ITEM_B, + found: true, + _source: { + id: WITHDRAW_RAI_ITEM_B, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "respond-to-rai", + authority: "1915(b)", + state: "MD", + origin: "OneMAC", + changelog: [ + { + _id: `${WITHDRAW_RAI_ITEM_B}-001`, + _source: { + id: `${WITHDRAW_RAI_ITEM_B}-0001`, + event: "respond-to-rai", + packageId: WITHDRAW_RAI_ITEM_B, + }, + }, + ], + }, + }, + [WITHDRAW_RAI_ITEM_C]: { + _id: WITHDRAW_RAI_ITEM_C, + found: true, + _source: { + id: WITHDRAW_RAI_ITEM_C, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "respond-to-rai", + authority: "1915(c)", + state: "MD", + leadAnalystName: "lead test", + leadAnalystEmail: "Lead test email", + reviewTeam: [ + { + name: "Test", + email: "testemail", + }, + ], + origin: "OneMAC", + changelog: [ + { + _id: `${WITHDRAW_RAI_ITEM_C}-001`, + _source: { + id: `${WITHDRAW_RAI_ITEM_C}-0001`, + submitterName: "Testmctex", + submitterEmail: "fakeemail;", + event: "respond-to-rai", + packageId: WITHDRAW_RAI_ITEM_C, + }, + }, + + { + _id: `${WITHDRAW_RAI_ITEM_C}-002`, + _source: { + id: `${WITHDRAW_RAI_ITEM_C}-0002`, + submitterName: "Testmctex", + submitterEmail: "fakeemail;", + event: "withdraw-rai", + packageId: WITHDRAW_RAI_ITEM_C, + }, + }, + ], + }, + }, [EXISTING_ITEM_APPROVED_APPK_ITEM_ID]: { _id: EXISTING_ITEM_APPROVED_APPK_ITEM_ID, found: true, From 254e209d2294d9f9ad8335058db5e4aedb26c567 Mon Sep 17 00:00:00 2001 From: Tiffany Forkner Date: Fri, 17 Jan 2025 09:25:38 -0500 Subject: [PATCH 2/6] feat(test): adding tests for getting package actions (#1035) * added tests for getting package actions --- lib/lambda/getPackageActions.test.ts | 19 +- lib/libs/api/package/appk.test.ts | 2 + lib/libs/email/vitest.setup.ts | 2 +- .../getAvailableActions.test.ts | 102 +++ .../package-actions/getAvailableActions.ts | 2 +- .../package-actions/rules.test.ts | 697 ++++++++++++++++++ .../shared-utils/package-actions/rules.ts | 16 +- .../shared-utils/package-check.test.ts | 62 +- mocks/data/items.ts | 23 +- mocks/data/users/index.ts | 16 +- mocks/handlers/api/cpocs.ts | 30 + mocks/handlers/api/index.ts | 17 +- mocks/handlers/api/items.ts | 7 + mocks/handlers/api/packageActions.ts | 46 ++ mocks/handlers/api/submissions.ts | 24 +- mocks/index.d.ts | 13 + react-app/src/api/getAttachmentUrl.test.ts | 25 + react-app/src/api/itemExists.test.ts | 33 + react-app/src/api/itemExists.ts | 9 +- react-app/src/api/useGetCPOCs.test.ts | 21 + react-app/src/api/useGetCPOCs.ts | 4 +- .../src/api/useGetPackageActions.test.ts | 73 ++ react-app/src/api/useGetPackageActions.ts | 2 +- 23 files changed, 1178 insertions(+), 67 deletions(-) create mode 100644 lib/packages/shared-utils/package-actions/getAvailableActions.test.ts create mode 100644 lib/packages/shared-utils/package-actions/rules.test.ts create mode 100644 mocks/handlers/api/cpocs.ts create mode 100644 mocks/handlers/api/packageActions.ts create mode 100644 react-app/src/api/getAttachmentUrl.test.ts create mode 100644 react-app/src/api/itemExists.test.ts create mode 100644 react-app/src/api/useGetCPOCs.test.ts create mode 100644 react-app/src/api/useGetPackageActions.test.ts diff --git a/lib/lambda/getPackageActions.test.ts b/lib/lambda/getPackageActions.test.ts index 373d58b1b..25e67402a 100644 --- a/lib/lambda/getPackageActions.test.ts +++ b/lib/lambda/getPackageActions.test.ts @@ -1,10 +1,12 @@ import { APIGatewayEvent } from "aws-lambda"; +import { Action } from "shared-types"; import { getRequestContext } from "mocks"; import { GET_ERROR_ITEM_ID, HI_TEST_ITEM_ID, NOT_FOUND_ITEM_ID, WITHDRAWN_CHANGELOG_ITEM_ID, + INITIAL_RELEASE_APPK_ITEM_ID, } from "mocks/data/items"; import { describe, expect, it } from "vitest"; import { handler } from "./getPackageActions"; @@ -60,7 +62,7 @@ describe("getPackageActions Handler", () => { expect(res.body).toEqual(JSON.stringify({ message: "No record found for the given id" })); }); - it("should return 200 with available actions if authorized and package is found", async () => { + it("should return 200 with available actions if authorized and package is found and has no app-k", async () => { const event = { body: JSON.stringify({ id: WITHDRAWN_CHANGELOG_ITEM_ID }), requestContext: getRequestContext(), @@ -73,6 +75,21 @@ describe("getPackageActions Handler", () => { expect(res.body).toEqual(JSON.stringify({ actions: [] })); }); + it("should return 200 with available actions if authorized and package is found and has app-k", async () => { + const event = { + body: JSON.stringify({ id: INITIAL_RELEASE_APPK_ITEM_ID }), + requestContext: getRequestContext(), + } as APIGatewayEvent; + + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual( + JSON.stringify({ actions: [Action.WITHDRAW_PACKAGE, Action.UPLOAD_SUBSEQUENT_DOCUMENTS] }), + ); + }); + it("should handle errors during processing", async () => { const event = { body: JSON.stringify({ id: GET_ERROR_ITEM_ID }), diff --git a/lib/libs/api/package/appk.test.ts b/lib/libs/api/package/appk.test.ts index 64717451b..8bfe427eb 100644 --- a/lib/libs/api/package/appk.test.ts +++ b/lib/libs/api/package/appk.test.ts @@ -33,8 +33,10 @@ describe("getAppkChildren", () => { hits: [ { _source: { + authority: "1915(c)", changedDate: "2024-01-01T00:00:00Z", title: "Initial release", + seatoolStatus: "Pending", cmsStatus: "Pending", stateStatus: "Under Review", }, diff --git a/lib/libs/email/vitest.setup.ts b/lib/libs/email/vitest.setup.ts index bfc8e2a7e..a3a0e206d 100644 --- a/lib/libs/email/vitest.setup.ts +++ b/lib/libs/email/vitest.setup.ts @@ -1,6 +1,6 @@ import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest"; -import { mockedServiceServer as mockedServer } from "mocks/server"; import { REGION, setDefaultStateSubmitter } from "mocks"; +import { mockedServiceServer as mockedServer } from "mocks/server"; beforeAll(() => { setDefaultStateSubmitter(); diff --git a/lib/packages/shared-utils/package-actions/getAvailableActions.test.ts b/lib/packages/shared-utils/package-actions/getAvailableActions.test.ts new file mode 100644 index 000000000..663360437 --- /dev/null +++ b/lib/packages/shared-utils/package-actions/getAvailableActions.test.ts @@ -0,0 +1,102 @@ +import { describe, it, expect } from "vitest"; +import { getAvailableActions } from "./getAvailableActions"; +import { Action, SEATOOL_STATUS } from "shared-types"; +import { + TEST_1915B_ITEM, + TEST_CHIP_SPA_ITEM, + TEST_CMS_REVIEWER_USER, + TEST_MED_SPA_ITEM, + TEST_STATE_SUBMITTER_USER, +} from "mocks"; + +describe("getAvailableActions tests", () => { + it(`should return actions: [${Action.RESPOND_TO_RAI},${Action.WITHDRAW_PACKAGE}]`, () => { + const result = getAvailableActions(TEST_STATE_SUBMITTER_USER, { + ...TEST_MED_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + }); + expect(result).toEqual([Action.RESPOND_TO_RAI, Action.WITHDRAW_PACKAGE]); + }); + + it(`should return actions: [${Action.TEMP_EXTENSION}, ${Action.AMEND_WAIVER}]`, () => { + const result = getAvailableActions(TEST_STATE_SUBMITTER_USER, { + ...TEST_1915B_ITEM._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.APPROVED, + }); + expect(result).toEqual([Action.TEMP_EXTENSION, Action.AMEND_WAIVER]); + }); + + it(`should return actions: [${Action.ENABLE_RAI_WITHDRAW}] for CHIP SPA`, () => { + const result = getAvailableActions(TEST_CMS_REVIEWER_USER, { + ...TEST_CHIP_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(result).toEqual([Action.ENABLE_RAI_WITHDRAW]); + }); + + it(`should return actions: [${Action.ENABLE_RAI_WITHDRAW}] for Medicaid SPA`, () => { + const result = getAvailableActions(TEST_CMS_REVIEWER_USER, { + ...TEST_MED_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(result).toEqual([Action.ENABLE_RAI_WITHDRAW]); + }); + + it(`should return actions: [${Action.DISABLE_RAI_WITHDRAW}] for CHIP SPA`, () => { + const result = getAvailableActions(TEST_CMS_REVIEWER_USER, { + ...TEST_CHIP_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(result).toEqual([Action.DISABLE_RAI_WITHDRAW]); + }); + + it(`should return actions: [${Action.DISABLE_RAI_WITHDRAW}] for Medicaid SPA`, () => { + const result = getAvailableActions(TEST_CMS_REVIEWER_USER, { + ...TEST_MED_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(result).toEqual([Action.DISABLE_RAI_WITHDRAW]); + }); + + it(`should return actions: [${Action.WITHDRAW_RAI}, ${Action.WITHDRAW_PACKAGE}, ${Action.UPLOAD_SUBSEQUENT_DOCUMENTS}]`, () => { + const result = getAvailableActions(TEST_STATE_SUBMITTER_USER, { + ...TEST_MED_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(result).toEqual([ + Action.WITHDRAW_RAI, + Action.WITHDRAW_PACKAGE, + Action.UPLOAD_SUBSEQUENT_DOCUMENTS, + ]); + }); + + it(`should return actions: [${Action.WITHDRAW_PACKAGE}, ${Action.UPLOAD_SUBSEQUENT_DOCUMENTS}]`, () => { + const result = getAvailableActions(TEST_STATE_SUBMITTER_USER, { + ...TEST_MED_SPA_ITEM._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiWithdrawEnabled: true, + }); + expect(result).toEqual([Action.WITHDRAW_PACKAGE, Action.UPLOAD_SUBSEQUENT_DOCUMENTS]); + }); +}); diff --git a/lib/packages/shared-utils/package-actions/getAvailableActions.ts b/lib/packages/shared-utils/package-actions/getAvailableActions.ts index c6a21a755..346a4a11c 100644 --- a/lib/packages/shared-utils/package-actions/getAvailableActions.ts +++ b/lib/packages/shared-utils/package-actions/getAvailableActions.ts @@ -1,4 +1,4 @@ -import { Action, CognitoUserAttributes, opensearch } from "../../shared-types"; +import { Action, CognitoUserAttributes, opensearch } from "shared-types"; import { PackageCheck } from "../package-check"; import rules from "./rules"; diff --git a/lib/packages/shared-utils/package-actions/rules.test.ts b/lib/packages/shared-utils/package-actions/rules.test.ts new file mode 100644 index 000000000..7296ea6c5 --- /dev/null +++ b/lib/packages/shared-utils/package-actions/rules.test.ts @@ -0,0 +1,697 @@ +import { describe, expect, it } from "vitest"; +import { + arRespondToRai, + arTempExtension, + arAmend, + arEnableWithdrawRaiResponse, + arDisableWithdrawRaiResponse, + arWithdrawRaiResponse, + arWithdrawPackage, + arUploadSubsequentDocuments, +} from "./rules"; +import { PackageCheck } from "../package-check"; +import { SEATOOL_STATUS } from "shared-types"; +import { + TEST_MED_SPA_ITEM, + TEST_TEMP_EXT_ITEM, + TEST_CHIP_SPA_ITEM, + TEST_1915B_ITEM, + TEST_1915C_ITEM, + TEST_STATE_SUBMITTER_USER, + TEST_CMS_REVIEWER_USER, +} from "mocks"; + +describe("package actions rules tests", () => { + describe("arRespondToRai rule tests", () => { + it("should return true for a valid package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arRespondToRai.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + it("should return false for a temporary extension package", () => { + const check = PackageCheck({ + ...TEST_TEMP_EXT_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arRespondToRai.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a non Pending-RAI status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arRespondToRai.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package without a raiRequestedDate", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: null, + }); + expect(arRespondToRai.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with an rai response", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arRespondToRai.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a cms reviewer user", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arRespondToRai.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package that is locked", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + locked: true, + }); + expect(arRespondToRai.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + }); + + describe("arTempExtension rule tests", () => { + it("should return true for a valid 1915(b) package", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + }); + expect(arTempExtension.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + it("should return true for a valid 1915(c) package", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + }); + expect(arTempExtension.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + it("should return false for a package with a non-Approved status", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + }); + expect(arTempExtension.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a Medicaid SPA package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + }); + expect(arTempExtension.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a CHIP SPA package", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + }); + expect(arTempExtension.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with action type Extend", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Extend", + }); + expect(arTempExtension.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a cms reviewer user", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + }); + expect(arTempExtension.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + }); + + describe("arAmend rule tests", () => { + it("should return true for a valid 1915(b) package", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Renew", + }); + expect(arAmend.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + + it("should return true for a valid 1915(c) package", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Renew", + }); + expect(arAmend.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + + it("should return false for a Medicaid SPA package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Renew", + }); + expect(arAmend.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + + it("should return false for a CHIP SPA package", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Renew", + }); + expect(arAmend.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + + it("should return false for a package with a non-Approved status", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "Renew", + }); + expect(arAmend.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + + it("should return false for a package with an actionType of Extend", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Extend", + }); + expect(arAmend.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + + it("should return false for a package with a cms reviewer", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Renew", + }); + expect(arAmend.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + }); + + describe("arEnableWithdrawRaiResponse rule tests", () => { + it("should return true for a valid package CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(true); + }); + it("should return false for a temporary extension package CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "Extend", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Withdrawn status CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.WITHDRAWN, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package that has an rai response CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawnDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with an rai withdraw enabled CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a state submitter CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with an Approved status CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Pending-Approved status CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_APPROVAL, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Submitted status CHIP SPA", () => { + const check = PackageCheck({ + ...TEST_CHIP_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.SUBMITTED, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + + it("should return true for a valid package 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(true); + }); + it("should return false for a temporary extension package 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "Extend", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Withdrawn status 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.WITHDRAWN, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package that has an rai response 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawnDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with an rai withdraw enabled 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a state submitter 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with an Approved status 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Pending-Approved status 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_APPROVAL, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Terminated status 1915(b)", () => { + const check = PackageCheck({ + ...TEST_1915B_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.TERMINATED, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arEnableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + }); + + describe("arDisableWithdrawRaiResponse rule tests", () => { + it("should return true for a valid package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(true); + }); + it("should return false for a temporary extension package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "Extend", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Withdrawn status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.WITHDRAWN, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package that has an rai response", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawnDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with an rai withdraw enabled", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: false, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a state submitter", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with an Approved status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Pending-Approved status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_APPROVAL, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package with a Unsubmitted status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.UNSUBMITTED, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arDisableWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + }); + + describe("arWithdrawRaiResponse rule tests", () => { + it("should return true for a valid package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + it("should return false for a temporary extension package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "Extend", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a Withdrawn status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.WITHDRAWN, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package that has an rai response", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawnDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a status of Pending-Approval", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING_APPROVAL, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with raiWithdrawEnabled false", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: false, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a cms reviewer", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package that is locked", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + raiRequestedDate: "2024-01-01T00:00:00.000Z", + raiReceivedDate: "2024-01-01T00:00:00.000Z", + raiWithdrawEnabled: true, + locked: true, + }); + expect(arWithdrawRaiResponse.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + }); + + describe("arWithdrawPackage rule tests", () => { + it("should return true for a valid package", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING, + }); + expect(arWithdrawPackage.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + it("should return false for a temporary extension package", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + actionType: "Extend", + seatoolStatus: SEATOOL_STATUS.PENDING, + }); + expect(arWithdrawPackage.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with status Approved", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.APPROVED, + }); + expect(arWithdrawPackage.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with status Submitted", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.SUBMITTED, + }); + expect(arWithdrawPackage.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a cms reviewer", () => { + const check = PackageCheck({ + ...TEST_1915C_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING, + }); + expect(arWithdrawPackage.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + }); + + describe("arUploadSubsequentDocuments rule tests", () => { + it("should return true for a valid package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING, + }); + expect(arUploadSubsequentDocuments.check(check, TEST_STATE_SUBMITTER_USER)).toBe(true); + }); + it("should return false for a package with a cms reviewer", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING, + }); + expect(arUploadSubsequentDocuments.check(check, TEST_CMS_REVIEWER_USER)).toBe(false); + }); + it("should return false for a package that needs intake", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING, + initialIntakeNeeded: true, + }); + expect(arUploadSubsequentDocuments.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a temporary extension package", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "Extend", + seatoolStatus: SEATOOL_STATUS.PENDING, + }); + expect(arUploadSubsequentDocuments.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with status Pending-RAI", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, + }); + expect(arUploadSubsequentDocuments.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a requested withdraw", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.PENDING, + raiRequestedDate: "2024-01-01T00:00:00.000Z", + }); + expect(arUploadSubsequentDocuments.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + it("should return false for a package with a non-Pending status", () => { + const check = PackageCheck({ + ...TEST_MED_SPA_ITEM?._source, + actionType: "New", + seatoolStatus: SEATOOL_STATUS.SUBMITTED, + }); + expect(arUploadSubsequentDocuments.check(check, TEST_STATE_SUBMITTER_USER)).toBe(false); + }); + }); +}); diff --git a/lib/packages/shared-utils/package-actions/rules.ts b/lib/packages/shared-utils/package-actions/rules.ts index d2df782cf..08ae73d00 100644 --- a/lib/packages/shared-utils/package-actions/rules.ts +++ b/lib/packages/shared-utils/package-actions/rules.ts @@ -7,7 +7,7 @@ import { } from "shared-types"; import { isStateUser, isCmsWriteUser, isCmsSuperUser } from "../user-helper"; -const arRespondToRai: ActionRule = { +export const arRespondToRai: ActionRule = { action: Action.RESPOND_TO_RAI, check: (checker, user) => !checker.isTempExtension && @@ -19,7 +19,7 @@ const arRespondToRai: ActionRule = { !checker.isLocked, }; -const arTempExtension: ActionRule = { +export const arTempExtension: ActionRule = { action: Action.TEMP_EXTENSION, check: (checker, user) => checker.hasStatus(SEATOOL_STATUS.APPROVED) && @@ -28,7 +28,7 @@ const arTempExtension: ActionRule = { isStateUser(user), }; -const arAmend: ActionRule = { +export const arAmend: ActionRule = { action: Action.AMEND_WAIVER, check: (checker, user) => checker.hasStatus(SEATOOL_STATUS.APPROVED) && @@ -37,7 +37,7 @@ const arAmend: ActionRule = { isStateUser(user), }; -const arEnableWithdrawRaiResponse: ActionRule = { +export const arEnableWithdrawRaiResponse: ActionRule = { action: Action.ENABLE_RAI_WITHDRAW, check: (checker, user) => { if (checker.authorityIs([Authority["CHIP_SPA"]])) { @@ -66,7 +66,7 @@ const arEnableWithdrawRaiResponse: ActionRule = { }, }; -const arDisableWithdrawRaiResponse: ActionRule = { +export const arDisableWithdrawRaiResponse: ActionRule = { action: Action.DISABLE_RAI_WITHDRAW, check: (checker, user) => !checker.isTempExtension && @@ -78,7 +78,7 @@ const arDisableWithdrawRaiResponse: ActionRule = { !checker.hasStatus([SEATOOL_STATUS.PENDING_CONCURRENCE, SEATOOL_STATUS.PENDING_APPROVAL]), }; -const arWithdrawRaiResponse: ActionRule = { +export const arWithdrawRaiResponse: ActionRule = { action: Action.WITHDRAW_RAI, check: (checker, user) => !checker.isTempExtension && @@ -92,7 +92,7 @@ const arWithdrawRaiResponse: ActionRule = { !checker.isLocked, }; -const arWithdrawPackage: ActionRule = { +export const arWithdrawPackage: ActionRule = { action: Action.WITHDRAW_PACKAGE, check: (checker, user) => !checker.isTempExtension && @@ -112,7 +112,7 @@ const arRemoveAppkChild: ActionRule = { check: (checker, user) => isStateUser(user) && !!checker.isAppkChild && false, }; -const arUploadSubsequentDocuments: ActionRule = { +export const arUploadSubsequentDocuments: ActionRule = { action: Action.UPLOAD_SUBSEQUENT_DOCUMENTS, check: (checker, user) => { if (isStateUser(user) === false) { diff --git a/lib/packages/shared-utils/package-check.test.ts b/lib/packages/shared-utils/package-check.test.ts index 3bbeb4509..2f7d8d155 100644 --- a/lib/packages/shared-utils/package-check.test.ts +++ b/lib/packages/shared-utils/package-check.test.ts @@ -1,48 +1,37 @@ import { describe, it, expect } from "vitest"; -import { testItemResult } from "./testData"; import { PackageCheck } from "."; import { ActionType, Authority, SEATOOL_STATUS } from "shared-types"; - -// Build Mock Package data: -// - make it basic, like a new submission -// - then override properties as needed -// ex: { ...baseNewSubmissionObj._source, raiWithdrawEnabled: true } +import { TEST_MED_SPA_ITEM, TEST_CHIP_SPA_ITEM, TEST_1915B_ITEM } from "mocks/data/items"; describe("PackageCheck", () => { describe("Plan Checks", () => { it("checks if isSpa", () => { let packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority.MED_SPA, + ...TEST_MED_SPA_ITEM?._source, }); expect(packageCheck.isSpa).toBe(true); packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority.CHIP_SPA, + ...TEST_CHIP_SPA_ITEM?._source, }); expect(packageCheck.isSpa).toBe(true); packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority["1915b"], + ...TEST_1915B_ITEM?._source, }); expect(packageCheck.isSpa).toBe(false); }); it("checks if isWaiver", () => { let packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority["1915b"], + ...TEST_1915B_ITEM?._source, }); expect(packageCheck.isWaiver).toBe(true); packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority.CHIP_SPA, + ...TEST_CHIP_SPA_ITEM?._source, }); expect(packageCheck.isWaiver).toBe(false); }); it("checks against input", () => { const packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority["1915b"], + ...TEST_1915B_ITEM._source, }); expect(packageCheck.authorityIs([Authority["1915b"]])).toBe(true); }); @@ -51,28 +40,26 @@ describe("PackageCheck", () => { describe("Status Checks", () => { it("checks if isInActivePendingStatus", () => { let packageCheck = PackageCheck({ - ...testItemResult._source, + ...TEST_MED_SPA_ITEM._source, seatoolStatus: SEATOOL_STATUS.PENDING, }); expect(packageCheck.isInActivePendingStatus).toBe(true); packageCheck = PackageCheck({ - ...testItemResult._source, + ...TEST_CHIP_SPA_ITEM._source, seatoolStatus: SEATOOL_STATUS.APPROVED, }); expect(packageCheck.isInActivePendingStatus).toBe(false); }); it("checks if isInSecondClock", () => { let packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority.CHIP_SPA, // Chip Spas don't have 2nd clock + ...TEST_CHIP_SPA_ITEM._source, seatoolStatus: SEATOOL_STATUS.PENDING, raiRequestedDate: "exists", raiReceivedDate: "exists", }); expect(packageCheck.isInSecondClock).toBe(false); packageCheck = PackageCheck({ - ...testItemResult._source, - authority: Authority.MED_SPA, + ...TEST_MED_SPA_ITEM._source, seatoolStatus: SEATOOL_STATUS.PENDING, raiRequestedDate: "exists", raiReceivedDate: "exists", @@ -81,19 +68,19 @@ describe("PackageCheck", () => { }); it("checks if isNotWithdrawn", () => { let packageCheck = PackageCheck({ - ...testItemResult._source, + ...TEST_1915B_ITEM._source, seatoolStatus: SEATOOL_STATUS.WITHDRAWN, }); expect(packageCheck.isNotWithdrawn).toBe(false); packageCheck = PackageCheck({ - ...testItemResult._source, + ...TEST_MED_SPA_ITEM._source, seatoolStatus: SEATOOL_STATUS.APPROVED, }); expect(packageCheck.isNotWithdrawn).toBe(true); }); it("checks against input", () => { const packageCheck = PackageCheck({ - ...testItemResult._source, + ...TEST_CHIP_SPA_ITEM._source, seatoolStatus: SEATOOL_STATUS.WITHDRAWN, }); expect(packageCheck.hasStatus(SEATOOL_STATUS.PENDING_RAI)).toBe(false); @@ -104,23 +91,23 @@ describe("PackageCheck", () => { describe("RAI Checks", () => { it("checks if hasRequestedRai", () => {}); it("checks if hasLatestRai", () => { - let packageChecker = PackageCheck(testItemResult._source); + let packageChecker = PackageCheck(TEST_MED_SPA_ITEM._source); expect(packageChecker.hasLatestRai).toBe(false); packageChecker = PackageCheck({ - ...testItemResult._source, + ...TEST_MED_SPA_ITEM._source, raiRequestedDate: "yesterday, lol", }); expect(packageChecker.hasLatestRai).toBe(true); }); it("checks if hasRaiResponse", () => { let packageChecker = PackageCheck({ - ...testItemResult._source, + ...TEST_CHIP_SPA_ITEM._source, raiRequestedDate: "yesterday, lol", raiReceivedDate: "today, foo", }); expect(packageChecker.hasRaiResponse).toBe(true); packageChecker = PackageCheck({ - ...testItemResult._source, + ...TEST_CHIP_SPA_ITEM._source, raiRequestedDate: "yesterday, lol", raiReceivedDate: "today, foo", raiWithdrawnDate: "test", @@ -128,20 +115,23 @@ describe("PackageCheck", () => { expect(packageChecker.hasRaiResponse).toBe(false); }); it("checks if hasCompletedRai", () => { - let packageChecker = PackageCheck(testItemResult._source); + let packageChecker = PackageCheck(TEST_1915B_ITEM._source); expect(packageChecker.hasCompletedRai).toBe(false); packageChecker = PackageCheck({ - ...testItemResult._source, + ...TEST_1915B_ITEM._source, raiRequestedDate: "yesterday, lol", raiReceivedDate: "today, foo", }); expect(packageChecker.hasCompletedRai).toBe(true); }); it("checks if hasEnabledRaiWithdraw", () => { - let packageChecker = PackageCheck(testItemResult._source); + let packageChecker = PackageCheck({ + ...TEST_MED_SPA_ITEM._source, + raiWithdrawEnabled: false, + }); expect(packageChecker.hasEnabledRaiWithdraw).toBe(false); packageChecker = PackageCheck({ - ...testItemResult._source, + ...TEST_MED_SPA_ITEM._source, raiWithdrawEnabled: true, }); expect(packageChecker.hasEnabledRaiWithdraw).toBe(true); @@ -150,7 +140,7 @@ describe("PackageCheck", () => { describe("Action Type Checks", () => { it("checks against input", () => { const packageChecker = PackageCheck({ - ...testItemResult._source, + ...TEST_MED_SPA_ITEM._source, actionType: "Amend" as ActionType, }); diff --git a/mocks/data/items.ts b/mocks/data/items.ts index 458418a0a..78756ace8 100644 --- a/mocks/data/items.ts +++ b/mocks/data/items.ts @@ -1,4 +1,4 @@ -import { SEATOOL_STATUS } from "shared-types"; +import { SEATOOL_STATUS, opensearch } from "shared-types"; import type { TestItemResult } from "../index.d"; import { ATTACHMENT_BUCKET_NAME } from "../consts"; @@ -163,7 +163,7 @@ const items: Record = { _source: { id: EXISTING_ITEM_TEMPORARY_EXTENSION_ID, seatoolStatus: SEATOOL_STATUS.APPROVED, - actionType: "Amend", + actionType: "Extend", authority: "Medicaid SPA", changedDate: undefined, origin: "OneMAC", @@ -392,8 +392,10 @@ const items: Record = { appkChildren: [ { _source: { + authority: "1915(c)", changedDate: "2024-01-01T00:00:00Z", title: "Initial release", + seatoolStatus: SEATOOL_STATUS.PENDING, cmsStatus: "Pending", stateStatus: "Under Review", }, @@ -428,8 +430,9 @@ const items: Record = { found: true, _source: { id: WITHDRAW_RAI_ITEM_C, - seatoolStatus: SEATOOL_STATUS.PENDING, + seatoolStatus: SEATOOL_STATUS.PENDING_RAI, actionType: "respond-to-rai", + raiRequestedDate: "2024-01-01T00:00:00.000Z", authority: "1915(c)", state: "MD", leadAnalystName: "lead test", @@ -498,4 +501,18 @@ const items: Record = { }, }; +export const TEST_MED_SPA_ITEM = items[TEST_ITEM_ID] as opensearch.main.ItemResult; +export const TEST_CHIP_SPA_ITEM = items[WITHDRAWN_CHANGELOG_ITEM_ID] as opensearch.main.ItemResult; +export const TEST_1915B_ITEM = items[EXISTING_ITEM_APPROVED_NEW_ID] as opensearch.main.ItemResult; +export const TEST_1915C_ITEM = items[INITIAL_RELEASE_APPK_ITEM_ID] as opensearch.main.ItemResult; +export const TEST_ITEM_WITH_APPK = items[ + EXISTING_ITEM_APPROVED_APPK_ITEM_ID +] as opensearch.main.ItemResult; +export const TEST_ITEM_WITH_CHANGELOG = items[ + WITHDRAWN_CHANGELOG_ITEM_ID +] as opensearch.main.ItemResult; +export const TEST_TEMP_EXT_ITEM = items[ + EXISTING_ITEM_TEMPORARY_EXTENSION_ID +] as opensearch.main.ItemResult; + export default items; diff --git a/mocks/data/users/index.ts b/mocks/data/users/index.ts index 953555aca..005d82e65 100644 --- a/mocks/data/users/index.ts +++ b/mocks/data/users/index.ts @@ -1,8 +1,9 @@ import type { TestUserData } from "../../index.d"; -import { reviewers } from "./cmsReviewer"; -import { helpDeskUsers } from "./helpDeskUsers"; -import { readOnlyUsers } from "./readOnlyCMSUsers"; -import { stateSubmitters } from "./stateSubmitters"; +import { reviewers, makoReviewer, superReviewer } from "./cmsReviewer"; +import { helpDeskUsers, helpDeskUser } from "./helpDeskUsers"; +import { readOnlyUsers, readOnlyUser } from "./readOnlyCMSUsers"; +import { stateSubmitters, makoStateSubmitter, coStateSubmitter } from "./stateSubmitters"; +import { convertUserAttributes } from "mocks/handlers/authUtils"; export const noRoleUser: TestUserData = { UserAttributes: [ @@ -42,6 +43,13 @@ export const userResponses: TestUserData[] = [ // return an array of all usernames export default userResponses.map((response) => ({ username: response.Username })); +export const TEST_STATE_SUBMITTER_USER = convertUserAttributes(makoStateSubmitter); +export const TEST_CO_STATE_SUBMITTER_USER = convertUserAttributes(coStateSubmitter); +export const TEST_CMS_REVIEWER_USER = convertUserAttributes(makoReviewer); +export const TEST_HELP_DESK_USER = convertUserAttributes(helpDeskUser); +export const TEST_READ_ONLY_USER = convertUserAttributes(readOnlyUser); +export const TEST_SUPER_USER = convertUserAttributes(superReviewer); + export * from "./cmsReviewer"; export * from "./helpDeskUsers"; export * from "./mockStorage"; diff --git a/mocks/handlers/api/cpocs.ts b/mocks/handlers/api/cpocs.ts new file mode 100644 index 000000000..2d8dd70fd --- /dev/null +++ b/mocks/handlers/api/cpocs.ts @@ -0,0 +1,30 @@ +import { http, HttpResponse } from "msw"; +import { cpocsList } from "../../data/cpocs"; + +const defaultCpocHandler = http.post(/\/getCpocs$/, async () => + HttpResponse.json({ + took: 3, + timed_out: false, + _shards: { + total: 5, + successful: 5, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 654, + relation: "eq", + }, + max_score: 1, + hits: cpocsList, + }, + }), +); + +export const errorCpocHandler = http.post( + /\/getCpocs/, + () => new HttpResponse(null, { status: 500 }), +); + +export const cpocHandlers = [defaultCpocHandler]; diff --git a/mocks/handlers/api/index.ts b/mocks/handlers/api/index.ts index abce82fd4..61f072f71 100644 --- a/mocks/handlers/api/index.ts +++ b/mocks/handlers/api/index.ts @@ -1,9 +1,20 @@ +import { cpocHandlers } from "./cpocs"; import { itemHandlers } from "./items"; +import { packageActionHandlers } from "./packageActions"; import { submissionHandlers } from "./submissions"; import { typeHandlers } from "./types"; -export const apiHandlers = [...itemHandlers, ...submissionHandlers, ...typeHandlers]; - -export { mockCurrentAuthenticatedUser, mockUseGetUser, mockUserAttributes } from "./user"; +export const apiHandlers = [ + ...cpocHandlers, + ...itemHandlers, + ...packageActionHandlers, + ...submissionHandlers, + ...typeHandlers, +]; +export { errorCpocHandler } from "./cpocs"; +export { errorItemHandler, errorItemExistsHandler } from "./items"; +export { errorPackageActionsHandler } from "./packageActions"; +export { errorAttachmentUrlHandler } from "./submissions"; export { errorSubTypesHandler, errorTypeHandler } from "./types"; +export { mockCurrentAuthenticatedUser, mockUseGetUser, mockUserAttributes } from "./user"; diff --git a/mocks/handlers/api/items.ts b/mocks/handlers/api/items.ts index a7e83738d..e5fa57e55 100644 --- a/mocks/handlers/api/items.ts +++ b/mocks/handlers/api/items.ts @@ -14,6 +14,8 @@ const defaultItemHandler = http.post(/\/item$/, async return item ? HttpResponse.json(item) : new HttpResponse(null, { status: 404 }); }); +export const errorItemHandler = http.post(/\/item$/, () => new HttpResponse(null, { status: 500 })); + const defaultItemExistsHandler = http.post( /\/itemExists$/, async ({ request }) => { @@ -25,4 +27,9 @@ const defaultItemExistsHandler = http.post( }, ); +export const errorItemExistsHandler = http.post( + /\/itemExists$/, + () => new HttpResponse(null, { status: 500 }), +); + export const itemHandlers = [defaultItemHandler, defaultItemExistsHandler]; diff --git a/mocks/handlers/api/packageActions.ts b/mocks/handlers/api/packageActions.ts new file mode 100644 index 000000000..2cae91846 --- /dev/null +++ b/mocks/handlers/api/packageActions.ts @@ -0,0 +1,46 @@ +import { http, HttpResponse, PathParams } from "msw"; +import { PackageActionsRequestBody, mockUseGetUser } from "mocks"; +import items from "mocks/data/items"; +import { opensearch, UserRoles } from "shared-types"; +import { getAvailableActions } from "shared-utils"; + +const defaultPackageActionsHandler = http.post( + /\/getPackageActions$/, + async ({ request }) => { + const { id } = await request.json(); + + if (!id) { + return HttpResponse.json({ message: "Event body required" }, { status: 400 }); + } + + const item = items[id]; + if (!item?._source?.state) { + return HttpResponse.json({ message: "No record found for the given id" }, { status: 404 }); + } + + const currUser = mockUseGetUser()?.data?.user; + const userRoles = (currUser?.["custom:cms-roles"] as string) || ""; + const userStates = (currUser?.["custom:state"] as string) || ""; + if ( + !currUser || + !userRoles.includes(UserRoles.STATE_SUBMITTER) || + !userStates.includes(item._source.state) + ) { + return HttpResponse.json( + { message: "Not authorized to view resources from this state" }, + { status: 401 }, + ); + } + + return HttpResponse.json( + getAvailableActions(currUser, item._source as opensearch.main.Document) || [], + ); + }, +); + +export const errorPackageActionsHandler = http.post( + /\/getPackageActions$/, + () => new HttpResponse(null, { status: 500 }), +); + +export const packageActionHandlers = [defaultPackageActionsHandler]; diff --git a/mocks/handlers/api/submissions.ts b/mocks/handlers/api/submissions.ts index 10007c454..aa64f5989 100644 --- a/mocks/handlers/api/submissions.ts +++ b/mocks/handlers/api/submissions.ts @@ -1,7 +1,7 @@ -import { http, HttpResponse } from "msw"; +import { http, HttpResponse, PathParams } from "msw"; import { SUBMISSION_ERROR_ITEM_ID } from "../../data/items"; - -export type SubmitRequestBody = { id: string }; +import { SubmitRequestBody, AttachmentUrlRequestBody } from "../../index.d"; +import { REGION } from "../../consts"; const defaultUploadHandler = http.put( /\/upload/, @@ -19,7 +19,22 @@ const defaultUploadUrlHandler = http.post(/\/getUploadUrl/, () => ), ); -const defaultSubmitHandler = http.post( +const defaultAttachmentUrlHandler = http.post( + /\/getAttachmentUrl$/, + async ({ request }) => { + const { id, bucket, key, filename } = await request.json(); + return HttpResponse.json({ + url: `https://s3.${REGION}.amazonaws.com/${bucket}/${id}-${key}-${filename}`, + }); + }, +); + +export const errorAttachmentUrlHandler = http.post( + /\/getAttachmentUrl$/, + async () => new HttpResponse(null, { status: 500 }), +); + +const defaultSubmitHandler = http.post( /\/submit$/, async ({ request }) => { const { id } = await request.json(); @@ -35,5 +50,6 @@ const defaultSubmitHandler = http.post( export const submissionHandlers = [ defaultUploadHandler, defaultUploadUrlHandler, + defaultAttachmentUrlHandler, defaultSubmitHandler, ]; diff --git a/mocks/index.d.ts b/mocks/index.d.ts index 4b8d79e68..f4ab69605 100644 --- a/mocks/index.d.ts +++ b/mocks/index.d.ts @@ -159,3 +159,16 @@ export type TestStepFunctionRequestBody = { }; export type TestCounty = [string, string, string]; + +export type SubmitRequestBody = { id: string }; + +export type AttachmentUrlRequestBody = { + id: string; + bucket: string; + key: string; + filename: string; +}; + +export type PackageActionsRequestBody = { + id: string; +}; diff --git a/react-app/src/api/getAttachmentUrl.test.ts b/react-app/src/api/getAttachmentUrl.test.ts new file mode 100644 index 000000000..67de816de --- /dev/null +++ b/react-app/src/api/getAttachmentUrl.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from "vitest"; +import { getAttachmentUrl } from "./getAttachmentUrl"; +import { ATTACHMENT_BUCKET_NAME, ATTACHMENT_BUCKET_REGION, errorAttachmentUrlHandler } from "mocks"; +import { mockedApiServer as mockedServer } from "mocks/server"; + +describe("getAttachmentUrl tests", () => { + const id = "1234"; + const key = "test-key"; + const filename = "test-file.txt"; + + it("should return a url", async () => { + const url = await getAttachmentUrl(id, ATTACHMENT_BUCKET_NAME, key, filename); + expect(url).toEqual( + `https://s3.${ATTACHMENT_BUCKET_REGION}.amazonaws.com/${ATTACHMENT_BUCKET_NAME}/${id}-${key}-${filename}`, + ); + }); + + it("should throw an error if the response is 500", async () => { + mockedServer.use(errorAttachmentUrlHandler); + + await expect(() => + getAttachmentUrl(id, ATTACHMENT_BUCKET_NAME, key, filename), + ).rejects.toThrowError(); + }); +}); diff --git a/react-app/src/api/itemExists.test.ts b/react-app/src/api/itemExists.test.ts new file mode 100644 index 000000000..1dedd78fe --- /dev/null +++ b/react-app/src/api/itemExists.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import { itemExists } from "./itemExists"; +import { + TEST_ITEM_ID, + NOT_FOUND_ITEM_ID, + NOT_EXISTING_ITEM_ID, + errorItemExistsHandler, +} from "mocks"; +import { mockedApiServer as mockedServer } from "mocks/server"; + +describe("itemExists test", () => { + it("should return true if the item exists", async () => { + const found = await itemExists(TEST_ITEM_ID); + expect(found).toBeTruthy(); + }); + + it("should return false if the item does not exist", async () => { + const found = await itemExists(NOT_EXISTING_ITEM_ID); + expect(found).toBeFalsy(); + }); + + it("should return false if the item is not found", async () => { + const found = await itemExists(NOT_FOUND_ITEM_ID); + expect(found).toBeFalsy(); + }); + + it("should return false if there is an error getting the item", async () => { + mockedServer.use(errorItemExistsHandler); + + const found = await itemExists(TEST_ITEM_ID); + expect(found).toBeFalsy(); + }); +}); diff --git a/react-app/src/api/itemExists.ts b/react-app/src/api/itemExists.ts index b3ebb7202..64091236c 100644 --- a/react-app/src/api/itemExists.ts +++ b/react-app/src/api/itemExists.ts @@ -1,6 +1,11 @@ import { API } from "aws-amplify"; export const itemExists = async (id: string): Promise => { - const response = await API.post("os", "/itemExists", { body: { id } }); - return response.exists; + try { + const response = await API.post("os", "/itemExists", { body: { id } }); + return response.exists; + } catch (error) { + console.error("Error checking if item exists:", error); + return false; + } }; diff --git a/react-app/src/api/useGetCPOCs.test.ts b/react-app/src/api/useGetCPOCs.test.ts new file mode 100644 index 000000000..c576b5f55 --- /dev/null +++ b/react-app/src/api/useGetCPOCs.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vitest"; +import { fetchCpocData } from "./useGetCPOCs"; +import { errorCpocHandler } from "mocks"; +import { mockedApiServer as mockedServer } from "mocks/server"; +import { cpocsList } from "mocks/data/cpocs"; + +describe("useGetCPOCs test", () => { + describe("fetchCpocData tests", () => { + it("should return CPOCs", async () => { + const result = await fetchCpocData(); + expect(result).toEqual(cpocsList.map((cpoc) => cpoc?._source)); + }); + + it("should handle an error when fetching CPOCs", async () => { + mockedServer.use(errorCpocHandler); + + const result = await fetchCpocData(); + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/react-app/src/api/useGetCPOCs.ts b/react-app/src/api/useGetCPOCs.ts index c157a5f69..35be30cdb 100644 --- a/react-app/src/api/useGetCPOCs.ts +++ b/react-app/src/api/useGetCPOCs.ts @@ -4,10 +4,8 @@ import { ReactQueryApiError } from "shared-types"; import { cpocs } from "shared-types/opensearch"; export async function fetchCpocData() { - const endpoint = "/getCpocs"; - try { - const response = await API.post("os", endpoint, { body: {} }); + const response = await API.post("os", "/getCpocs", { body: {} }); const results = response.hits?.hits || []; return results.map((hit: cpocs.ItemResult) => hit._source); } catch (error) { diff --git a/react-app/src/api/useGetPackageActions.test.ts b/react-app/src/api/useGetPackageActions.test.ts new file mode 100644 index 000000000..f2ce5dd78 --- /dev/null +++ b/react-app/src/api/useGetPackageActions.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, it, afterEach } from "vitest"; +import { getPackageActions } from "./useGetPackageActions"; +import { Action } from "shared-types"; +import { + errorPackageActionsHandler, + WITHDRAW_RAI_ITEM_C, + TEST_ITEM_ID, + NOT_FOUND_ITEM_ID, + NOT_EXISTING_ITEM_ID, + setMockUsername, + makoReviewer, + coStateSubmitter, + setDefaultStateSubmitter, +} from "mocks"; +import { mockedApiServer as mockedServer } from "mocks/server"; + +describe("getPackageActions test", () => { + afterEach(() => { + setDefaultStateSubmitter(); + }); + + it("should return actions for valid package", async () => { + const actions = await getPackageActions(WITHDRAW_RAI_ITEM_C); + expect(actions).toEqual([Action.RESPOND_TO_RAI, Action.WITHDRAW_PACKAGE]); + }); + + it("should return empty actions for package without actions", async () => { + const actions = await getPackageActions(TEST_ITEM_ID); + expect(actions).toEqual([]); + }); + + it("should return 400 if there is no package id", async () => { + await expect(() => getPackageActions(null)).rejects.toThrowError( + "Request failed with status code 400", + ); + }); + + it("should return 404 if the package is not found", async () => { + await expect(() => getPackageActions(NOT_FOUND_ITEM_ID)).rejects.toThrowError( + "Request failed with status code 404", + ); + }); + + it("should return 404 if the package does not exist", async () => { + await expect(() => getPackageActions(NOT_EXISTING_ITEM_ID)).rejects.toThrowError( + "Request failed with status code 404", + ); + }); + + it("should return 401 if the user is not a state submitter", async () => { + setMockUsername(makoReviewer); + + await expect(() => getPackageActions(WITHDRAW_RAI_ITEM_C)).rejects.toThrowError( + "Request failed with status code 401", + ); + }); + + it("should return 401 if the user is not a user for the state", async () => { + setMockUsername(coStateSubmitter); + + await expect(() => getPackageActions(WITHDRAW_RAI_ITEM_C)).rejects.toThrowError( + "Request failed with status code 401", + ); + }); + + it("should return 500 if there is a server error", async () => { + mockedServer.use(errorPackageActionsHandler); + + await expect(() => getPackageActions(WITHDRAW_RAI_ITEM_C)).rejects.toThrowError( + "Request failed with status code 500", + ); + }); +}); diff --git a/react-app/src/api/useGetPackageActions.ts b/react-app/src/api/useGetPackageActions.ts index 8349c9ef5..44a89bcf9 100644 --- a/react-app/src/api/useGetPackageActions.ts +++ b/react-app/src/api/useGetPackageActions.ts @@ -4,7 +4,7 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query"; type PackageActionsResponse = { actions: Action[]; }; -const getPackageActions = async (id: string): Promise => +export const getPackageActions = async (id: string): Promise => await API.post("os", "/getPackageActions", { body: { id } }); export const useGetPackageActions = ( From 1a0e238043e78f53dda2cbe34556a6413de4fde3 Mon Sep 17 00:00:00 2001 From: Thomas Walker Date: Fri, 17 Jan 2025 10:27:23 -0500 Subject: [PATCH 3/6] feat(test) new test for get form (#1036) * feat(test) new test for get form * get all forms changes --- lib/lambda/getAllForms.test.ts | 11 +++++++++-- lib/lambda/getAllForms.ts | 11 ++++++----- lib/lambda/getForm.test.ts | 8 ++++++++ lib/lambda/getForm.ts | 20 ++++++-------------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/lib/lambda/getAllForms.test.ts b/lib/lambda/getAllForms.test.ts index d166b0a0b..3830eb071 100644 --- a/lib/lambda/getAllForms.test.ts +++ b/lib/lambda/getAllForms.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it, vi } from "vitest"; -import { getAllForms } from "./getAllForms"; +import { getAllForms, mapWebformsKeys } from "./getAllForms"; +import * as wfv from "libs/webforms"; vi.mock("../libs/webforms", () => ({ webformVersions: { @@ -12,7 +13,6 @@ vi.mock("../libs/webforms", () => ({ }, }, })); - describe("getAllForms", () => { it("should return a response with status code 200 and the mapped webforms", async () => { const expectedResponse = { @@ -27,4 +27,11 @@ describe("getAllForms", () => { expect(result?.statusCode).toEqual(200); expect(result?.body).toEqual(JSON.stringify(expectedResponse.body)); }); + it("should return a response with status code 200 and the mapped webforms", async () => { + const mockconstant = wfv as { webformVersions: object }; + mockconstant.webformVersions = {}; + + const result = await getAllForms(); + expect(result?.statusCode).toEqual(502); + }); }); diff --git a/lib/lambda/getAllForms.ts b/lib/lambda/getAllForms.ts index 57817087c..0e2957deb 100644 --- a/lib/lambda/getAllForms.ts +++ b/lib/lambda/getAllForms.ts @@ -18,12 +18,13 @@ export const getAllForms = async () => { try { const formsWithVersions = mapWebformsKeys(webformVersions); - if (formsWithVersions) { - return response({ - statusCode: 200, - body: formsWithVersions, - }); + if (Object.keys(formsWithVersions).length === 0) { + throw new Error("No form Versions available"); } + return response({ + statusCode: 200, + body: formsWithVersions, + }); } catch (error: any) { console.error("Error:", error); return response({ diff --git a/lib/lambda/getForm.test.ts b/lib/lambda/getForm.test.ts index c64cd0a0e..5050b27a7 100644 --- a/lib/lambda/getForm.test.ts +++ b/lib/lambda/getForm.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { getForm } from "./getForm"; import { webformVersions } from "libs/webforms"; + describe("forms handler", () => { beforeEach(() => { // Reset mocks before each test @@ -48,4 +49,11 @@ describe("forms handler", () => { expect(result.statusCode).toBe(200); expect(result.body).toBe("{}"); }); + it("returns 502 because the body is invalid json", async () => { + const event = { + body: "kdjfkldjj:[df", + }; + const result = await getForm(event as any); + expect(result.statusCode).toBe(502); + }); }); diff --git a/lib/lambda/getForm.ts b/lib/lambda/getForm.ts index a07c55f3f..fdf07fa66 100644 --- a/lib/lambda/getForm.ts +++ b/lib/lambda/getForm.ts @@ -40,14 +40,12 @@ export const getForm = async (event: APIGatewayEvent) => { version += getMaxVersion(id); } - if (id && version) { - const formObj = webformVersions[id][version]; - const cleanedForm = convertRegexToString(formObj); - return response({ - statusCode: 200, - body: cleanedForm, - }); - } + const formObj = webformVersions[id][version]; + const cleanedForm = convertRegexToString(formObj); + return response({ + statusCode: 200, + body: cleanedForm, + }); } catch (error: any) { console.error("Error:", error); return response({ @@ -57,12 +55,6 @@ export const getForm = async (event: APIGatewayEvent) => { }, }); } - return response({ - statusCode: 500, - body: { - error: "Internal server error", - }, - }); }; function getMaxVersion(id: string): string { From 4a6f3cba34ae28eca82878fec9fecb712e62ee8c Mon Sep 17 00:00:00 2001 From: Ty Bolt Date: Fri, 17 Jan 2025 12:58:47 -0500 Subject: [PATCH 4/6] feat(ui): Updates schema to reflect correct casing (#1033) --- .../forms/post-submission/respond-to-rai/chip.test.tsx | 10 +++++----- react-app/src/formSchemas/respond-to-rai.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/react-app/src/features/forms/post-submission/respond-to-rai/chip.test.tsx b/react-app/src/features/forms/post-submission/respond-to-rai/chip.test.tsx index edbea9d39..9e87104a4 100644 --- a/react-app/src/features/forms/post-submission/respond-to-rai/chip.test.tsx +++ b/react-app/src/features/forms/post-submission/respond-to-rai/chip.test.tsx @@ -1,11 +1,11 @@ -import { screen } from "@testing-library/react"; -import { describe, expect, beforeAll, test } from "vitest"; import { RespondToRaiChip } from "@/features/forms/post-submission/respond-to-rai"; +import { formSchemas } from "@/formSchemas"; import { renderFormWithPackageSectionAsync } from "@/utils/test-helpers/renderForm"; import { skipCleanup } from "@/utils/test-helpers/skipCleanup"; import { uploadFiles } from "@/utils/test-helpers/uploadFiles"; -import { formSchemas } from "@/formSchemas"; +import { screen } from "@testing-library/react"; import { EXISTING_ITEM_PENDING_ID } from "mocks"; +import { beforeAll, describe, expect, test } from "vitest"; const upload = uploadFiles<(typeof formSchemas)["respond-to-rai-chip"]>(); @@ -15,12 +15,12 @@ describe("Respond To RAI CHIP", () => { await renderFormWithPackageSectionAsync(, EXISTING_ITEM_PENDING_ID); }); - test("REVISED AMENDED STATE PLAN LANGUAGE", async () => { + test("revised amended state plan language", async () => { const revisedAmendedStatePlanLanguageLabel = await upload("revisedAmendedStatePlanLanguage"); expect(revisedAmendedStatePlanLanguageLabel).not.toHaveClass("text-destructive"); }); - test("OFFICIAL RAI RESPONSE", async () => { + test("official RAI response", async () => { const officialRAIResponseLabel = await upload("officialRAIResponse"); expect(officialRAIResponseLabel).not.toHaveClass("text-destructive"); }); diff --git a/react-app/src/formSchemas/respond-to-rai.ts b/react-app/src/formSchemas/respond-to-rai.ts index 33da92d04..db6ec2ccc 100644 --- a/react-app/src/formSchemas/respond-to-rai.ts +++ b/react-app/src/formSchemas/respond-to-rai.ts @@ -8,11 +8,11 @@ export const formSchemaMedicaid = events["respond-to-rai"].baseSchema.extend({ export const formSchemaChip = events["respond-to-rai"].baseSchema.extend({ attachments: events["respond-to-rai"].chipSpaAttachments.extend({ revisedAmendedStatePlanLanguage: z.object({ - label: z.string().default("REVISED AMENDED STATE PLAN LANGUAGE"), + label: z.string().default("Revised Amended State Plan Language"), files: attachmentArraySchema(), }), officialRAIResponse: z.object({ - label: z.string().default("OFFICIAL RAI RESPONSE"), + label: z.string().default("Official RAI Response"), files: attachmentArraySchema(), }), }), From ac1d50fdbce91d9ab89e59b2afd522f2c4d7e76e Mon Sep 17 00:00:00 2001 From: Tiffany Forkner Date: Fri, 17 Jan 2025 14:48:00 -0500 Subject: [PATCH 5/6] feat(test): refactor test (#1037) * renaming handlers for clarity --- lib/lambda/getAllForms.test.ts | 2 +- lib/lambda/getCpocs.test.ts | 6 +-- lib/lambda/getSubTypes.test.ts | 4 +- lib/lambda/getTypes.test.ts | 4 +- lib/lambda/sinkMainProcessors.test.ts | 4 +- lib/vitest.setup.ts | 7 --- mocks/consts.ts | 4 +- mocks/data/users/index.ts | 2 +- mocks/handlers/api/cpocs.ts | 44 +++++++++-------- mocks/handlers/api/index.ts | 13 +++-- mocks/handlers/api/items.ts | 34 +++++++------ mocks/handlers/api/packageActions.ts | 11 +++-- mocks/handlers/api/search.ts | 39 +++++++++++++++ mocks/handlers/api/submissions.ts | 44 +++++++++-------- mocks/handlers/api/types.ts | 49 ++++++++++--------- mocks/handlers/api/user.ts | 2 +- .../handlers/{authUtils.ts => auth.utils.ts} | 0 mocks/handlers/aws/cognito.ts | 2 +- mocks/handlers/index.ts | 2 +- mocks/handlers/opensearch/changelog.ts | 6 +-- mocks/handlers/opensearch/cpocs.ts | 8 +-- mocks/handlers/opensearch/index.ts | 8 +-- mocks/handlers/opensearch/main.ts | 16 +++--- mocks/handlers/opensearch/subtypes.ts | 8 +-- mocks/handlers/opensearch/types.ts | 8 +-- .../{opensearch/util.ts => search.utils.ts} | 2 +- mocks/helpers/index.ts | 2 +- .../{kafka-test-helpers.ts => kafka.utils.ts} | 0 react-app/.env.test | 14 ++++++ react-app/src/api/getAttachmentUrl.test.ts | 8 ++- react-app/src/api/itemExists.test.ts | 4 +- react-app/src/api/useGetCPOCs.test.ts | 4 +- .../src/api/useGetPackageActions.test.ts | 4 +- react-app/src/api/useGetTypes.test.ts | 6 +-- react-app/src/api/useSearch.test.ts | 21 ++++++++ 35 files changed, 240 insertions(+), 152 deletions(-) create mode 100644 mocks/handlers/api/search.ts rename mocks/handlers/{authUtils.ts => auth.utils.ts} (100%) rename mocks/handlers/{opensearch/util.ts => search.utils.ts} (99%) rename mocks/helpers/{kafka-test-helpers.ts => kafka.utils.ts} (100%) create mode 100644 react-app/.env.test create mode 100644 react-app/src/api/useSearch.test.ts diff --git a/lib/lambda/getAllForms.test.ts b/lib/lambda/getAllForms.test.ts index 3830eb071..5319fc0bd 100644 --- a/lib/lambda/getAllForms.test.ts +++ b/lib/lambda/getAllForms.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { getAllForms, mapWebformsKeys } from "./getAllForms"; +import { getAllForms } from "./getAllForms"; import * as wfv from "libs/webforms"; vi.mock("../libs/webforms", () => ({ diff --git a/lib/lambda/getCpocs.test.ts b/lib/lambda/getCpocs.test.ts index c50f172e0..ee4acdd94 100644 --- a/lib/lambda/getCpocs.test.ts +++ b/lib/lambda/getCpocs.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; import { handler } from "./getCpocs"; import { mockedServiceServer } from "mocks/server"; -import { emptyCpocSearchHandler, errorCpocSearchHandler } from "mocks"; +import { emptyOSCpocSearchHandler, errorOSCpocSearchHandler } from "mocks"; import { cpocsList } from "mocks/data/cpocs"; describe("getCpocs Handler", () => { @@ -18,7 +18,7 @@ describe("getCpocs Handler", () => { // TODO - should this be removed? when will the result be empty and not // just a result with an empty hit array it("should return 400 if no Cpocs are found", async () => { - mockedServiceServer.use(emptyCpocSearchHandler); + mockedServiceServer.use(emptyOSCpocSearchHandler); const event = { body: JSON.stringify({}) } as APIGatewayEvent; @@ -39,7 +39,7 @@ describe("getCpocs Handler", () => { }); it("should return 500 if an error occurs during processing", async () => { - mockedServiceServer.use(errorCpocSearchHandler); + mockedServiceServer.use(errorOSCpocSearchHandler); const event = { body: JSON.stringify({}) } as APIGatewayEvent; diff --git a/lib/lambda/getSubTypes.test.ts b/lib/lambda/getSubTypes.test.ts index abb94ef66..0662063b1 100644 --- a/lib/lambda/getSubTypes.test.ts +++ b/lib/lambda/getSubTypes.test.ts @@ -12,7 +12,7 @@ import { medicaidSubtypes, chipSubtypes, } from "mocks/data/types"; -import { TestSubtypeItemResult, errorSubtypeSearchHandler } from "mocks"; +import { TestSubtypeItemResult, errorOSSubtypeSearchHandler } from "mocks"; import { mockedServiceServer as mockedServer } from "mocks/server"; describe("getSubTypes Handler", () => { @@ -40,7 +40,7 @@ describe("getSubTypes Handler", () => { }); it("should return 500 if there is a server error", async () => { - mockedServer.use(errorSubtypeSearchHandler); + mockedServer.use(errorOSSubtypeSearchHandler); const event = { body: JSON.stringify({ diff --git a/lib/lambda/getTypes.test.ts b/lib/lambda/getTypes.test.ts index 370a417b8..3ff0e65ff 100644 --- a/lib/lambda/getTypes.test.ts +++ b/lib/lambda/getTypes.test.ts @@ -8,7 +8,7 @@ import { medicaidTypes, chipTypes, } from "mocks/data/types"; -import { TestTypeItemResult, errorTypeSearchHandler } from "mocks"; +import { TestTypeItemResult, errorOSTypeSearchHandler } from "mocks"; import { mockedServiceServer as mockedServer } from "mocks/server"; describe("getTypes Handler", () => { @@ -33,7 +33,7 @@ describe("getTypes Handler", () => { }); it("should return 500 if there is a server error", async () => { - mockedServer.use(errorTypeSearchHandler); + mockedServer.use(errorOSTypeSearchHandler); const event = { body: JSON.stringify({ authorityId: MEDICAID_SPA_AUTHORITY_ID }), diff --git a/lib/lambda/sinkMainProcessors.test.ts b/lib/lambda/sinkMainProcessors.test.ts index 4b219a3ef..89f011bfc 100644 --- a/lib/lambda/sinkMainProcessors.test.ts +++ b/lib/lambda/sinkMainProcessors.test.ts @@ -17,7 +17,7 @@ import { NOT_FOUND_ITEM_ID, convertObjToBase64, createKafkaRecord, - errorMainMultiDocumentHandler, + errorOSMainMultiDocumentHandler, } from "mocks"; import { mockedServiceServer as mockedServer } from "mocks/server"; import { @@ -845,7 +845,7 @@ describe("insertNewSeatoolRecordsFromKafkaIntoMako", () => { }); it("handles errors in getting mako timestamps", async () => { - mockedServer.use(errorMainMultiDocumentHandler); + mockedServer.use(errorOSMainMultiDocumentHandler); await insertNewSeatoolRecordsFromKafkaIntoMako( [ diff --git a/lib/vitest.setup.ts b/lib/vitest.setup.ts index 61012f372..494cde920 100644 --- a/lib/vitest.setup.ts +++ b/lib/vitest.setup.ts @@ -1,8 +1,6 @@ import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest"; import { - API_CONFIG, API_ENDPOINT, - AUTH_CONFIG, IDENTITY_POOL_ID, OPENSEARCH_DOMAIN, OPENSEARCH_INDEX_NAMESPACE, @@ -22,12 +20,7 @@ import { } from "mocks"; import { ConfigResourceTypes } from "kafkajs"; import { mockedServiceServer as mockedServer } from "mocks/server"; -import { Amplify } from "aws-amplify"; type CreateType = T & { default: T }; -Amplify.configure({ - API: API_CONFIG, - Auth: AUTH_CONFIG, -}); vi.mock("kafkajs", async (importOriginal) => ({ ...(await importOriginal()), diff --git a/mocks/consts.ts b/mocks/consts.ts index 6228e82b2..1032a7a4b 100644 --- a/mocks/consts.ts +++ b/mocks/consts.ts @@ -37,8 +37,8 @@ export const AUTH_CONFIG = { userPoolWebClientId: USER_POOL_CLIENT_ID, oauth: { domain: USER_POOL_CLIENT_DOMAIN, - redirectSignIn: "http://localhost", - redirectSignOut: "http://localhost", + redirectSignIn: "http://localhost:5000/", + redirectSignOut: "http://localhost:5000/", scope: ["email", "openid"], responseType: "code", }, diff --git a/mocks/data/users/index.ts b/mocks/data/users/index.ts index 005d82e65..b51a1f2bd 100644 --- a/mocks/data/users/index.ts +++ b/mocks/data/users/index.ts @@ -3,7 +3,7 @@ import { reviewers, makoReviewer, superReviewer } from "./cmsReviewer"; import { helpDeskUsers, helpDeskUser } from "./helpDeskUsers"; import { readOnlyUsers, readOnlyUser } from "./readOnlyCMSUsers"; import { stateSubmitters, makoStateSubmitter, coStateSubmitter } from "./stateSubmitters"; -import { convertUserAttributes } from "mocks/handlers/authUtils"; +import { convertUserAttributes } from "mocks/handlers/auth.utils"; export const noRoleUser: TestUserData = { UserAttributes: [ diff --git a/mocks/handlers/api/cpocs.ts b/mocks/handlers/api/cpocs.ts index 2d8dd70fd..80901c2a7 100644 --- a/mocks/handlers/api/cpocs.ts +++ b/mocks/handlers/api/cpocs.ts @@ -1,30 +1,32 @@ import { http, HttpResponse } from "msw"; import { cpocsList } from "../../data/cpocs"; -const defaultCpocHandler = http.post(/\/getCpocs$/, async () => - HttpResponse.json({ - took: 3, - timed_out: false, - _shards: { - total: 5, - successful: 5, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 654, - relation: "eq", +const defaultApiCpocHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getCpocs", + async () => + HttpResponse.json({ + took: 3, + timed_out: false, + _shards: { + total: 5, + successful: 5, + skipped: 0, + failed: 0, }, - max_score: 1, - hits: cpocsList, - }, - }), + hits: { + total: { + value: 654, + relation: "eq", + }, + max_score: 1, + hits: cpocsList, + }, + }), ); -export const errorCpocHandler = http.post( - /\/getCpocs/, +export const errorApiCpocHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getCpocs", () => new HttpResponse(null, { status: 500 }), ); -export const cpocHandlers = [defaultCpocHandler]; +export const cpocHandlers = [defaultApiCpocHandler]; diff --git a/mocks/handlers/api/index.ts b/mocks/handlers/api/index.ts index 61f072f71..95756a555 100644 --- a/mocks/handlers/api/index.ts +++ b/mocks/handlers/api/index.ts @@ -1,6 +1,7 @@ import { cpocHandlers } from "./cpocs"; import { itemHandlers } from "./items"; import { packageActionHandlers } from "./packageActions"; +import { searchHandlers } from "./search"; import { submissionHandlers } from "./submissions"; import { typeHandlers } from "./types"; @@ -8,13 +9,15 @@ export const apiHandlers = [ ...cpocHandlers, ...itemHandlers, ...packageActionHandlers, + ...searchHandlers, ...submissionHandlers, ...typeHandlers, ]; -export { errorCpocHandler } from "./cpocs"; -export { errorItemHandler, errorItemExistsHandler } from "./items"; -export { errorPackageActionsHandler } from "./packageActions"; -export { errorAttachmentUrlHandler } from "./submissions"; -export { errorSubTypesHandler, errorTypeHandler } from "./types"; +export { errorApiCpocHandler } from "./cpocs"; +export { errorApiItemHandler, errorApiItemExistsHandler } from "./items"; +export { errorApiPackageActionsHandler } from "./packageActions"; +export { errorApiSearchHandler } from "./search"; +export { errorApiAttachmentUrlHandler } from "./submissions"; +export { errorApiSubTypesHandler, errorApiTypeHandler } from "./types"; export { mockCurrentAuthenticatedUser, mockUseGetUser, mockUserAttributes } from "./user"; diff --git a/mocks/handlers/api/items.ts b/mocks/handlers/api/items.ts index e5fa57e55..60eb6d236 100644 --- a/mocks/handlers/api/items.ts +++ b/mocks/handlers/api/items.ts @@ -2,22 +2,28 @@ import { http, HttpResponse } from "msw"; import items, { GET_ERROR_ITEM_ID } from "../../data/items"; import type { GetItemBody } from "../../index.d"; -const defaultItemHandler = http.post(/\/item$/, async ({ request }) => { - const { id } = await request.json(); +const defaultApiItemHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/item", + async ({ request }) => { + const { id } = await request.json(); - if (id == GET_ERROR_ITEM_ID) { - return new HttpResponse("Internal server error", { status: 500 }); - } + if (id == GET_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } - const item = items[id] || null; + const item = items[id] || null; - return item ? HttpResponse.json(item) : new HttpResponse(null, { status: 404 }); -}); + return item ? HttpResponse.json(item) : new HttpResponse(null, { status: 404 }); + }, +); -export const errorItemHandler = http.post(/\/item$/, () => new HttpResponse(null, { status: 500 })); +export const errorApiItemHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/item", + () => new HttpResponse(null, { status: 500 }), +); -const defaultItemExistsHandler = http.post( - /\/itemExists$/, +const defaultApiItemExistsHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/itemExists", async ({ request }) => { const { id } = await request.json(); if (id == GET_ERROR_ITEM_ID) { @@ -27,9 +33,9 @@ const defaultItemExistsHandler = http.post( }, ); -export const errorItemExistsHandler = http.post( - /\/itemExists$/, +export const errorApiItemExistsHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/itemExists", () => new HttpResponse(null, { status: 500 }), ); -export const itemHandlers = [defaultItemHandler, defaultItemExistsHandler]; +export const itemHandlers = [defaultApiItemHandler, defaultApiItemExistsHandler]; diff --git a/mocks/handlers/api/packageActions.ts b/mocks/handlers/api/packageActions.ts index 2cae91846..471e17081 100644 --- a/mocks/handlers/api/packageActions.ts +++ b/mocks/handlers/api/packageActions.ts @@ -4,8 +4,9 @@ import items from "mocks/data/items"; import { opensearch, UserRoles } from "shared-types"; import { getAvailableActions } from "shared-utils"; -const defaultPackageActionsHandler = http.post( - /\/getPackageActions$/, +const defaultApiPackageActionsHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getPackageActions", + async ({ request }) => { const { id } = await request.json(); @@ -38,9 +39,9 @@ const defaultPackageActionsHandler = http.post( - /\/getPackageActions$/, +export const errorApiPackageActionsHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getPackageActions", () => new HttpResponse(null, { status: 500 }), ); -export const packageActionHandlers = [defaultPackageActionsHandler]; +export const packageActionHandlers = [defaultApiPackageActionsHandler]; diff --git a/mocks/handlers/api/search.ts b/mocks/handlers/api/search.ts new file mode 100644 index 000000000..d1a51c076 --- /dev/null +++ b/mocks/handlers/api/search.ts @@ -0,0 +1,39 @@ +import { http, HttpResponse } from "msw"; +import { cpocsList } from "../../data/cpocs"; + +const defaultApiSearchHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/search/:index", + ({ params }) => { + const { index } = params; + + if (index === "cpocs") { + return HttpResponse.json({ + took: 3, + timed_out: false, + _shards: { + total: 5, + successful: 5, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 654, + relation: "eq", + }, + max_score: 1, + hits: cpocsList, + }, + }); + } + + return new HttpResponse(null, { status: 200 }); + }, +); + +export const errorApiSearchHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/search/:index", + () => new HttpResponse("Internal server error", { status: 500 }), +); + +export const searchHandlers = [defaultApiSearchHandler]; diff --git a/mocks/handlers/api/submissions.ts b/mocks/handlers/api/submissions.ts index aa64f5989..446000141 100644 --- a/mocks/handlers/api/submissions.ts +++ b/mocks/handlers/api/submissions.ts @@ -3,24 +3,26 @@ import { SUBMISSION_ERROR_ITEM_ID } from "../../data/items"; import { SubmitRequestBody, AttachmentUrlRequestBody } from "../../index.d"; import { REGION } from "../../consts"; -const defaultUploadHandler = http.put( - /\/upload/, +const defaultApiUploadHandler = http.put( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/upload", async () => new HttpResponse(null, { status: 200 }), ); -const defaultUploadUrlHandler = http.post(/\/getUploadUrl/, () => - HttpResponse.json( - { - url: "/upload", - key: "test-key", - bucket: "test-bucket", - }, - { status: 200 }, - ), +const defaultApiUploadUrlHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getUploadUrl", + () => + HttpResponse.json( + { + url: "/upload", + key: "test-key", + bucket: "test-bucket", + }, + { status: 200 }, + ), ); -const defaultAttachmentUrlHandler = http.post( - /\/getAttachmentUrl$/, +const defaultApiAttachmentUrlHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getAttachmentUrl", async ({ request }) => { const { id, bucket, key, filename } = await request.json(); return HttpResponse.json({ @@ -29,13 +31,13 @@ const defaultAttachmentUrlHandler = http.post( - /\/getAttachmentUrl$/, +export const errorApiAttachmentUrlHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getAttachmentUrl", async () => new HttpResponse(null, { status: 500 }), ); -const defaultSubmitHandler = http.post( - /\/submit$/, +const defaultApiSubmitHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/submit", async ({ request }) => { const { id } = await request.json(); @@ -48,8 +50,8 @@ const defaultSubmitHandler = http.post( ); export const submissionHandlers = [ - defaultUploadHandler, - defaultUploadUrlHandler, - defaultAttachmentUrlHandler, - defaultSubmitHandler, + defaultApiUploadHandler, + defaultApiUploadUrlHandler, + defaultApiAttachmentUrlHandler, + defaultApiSubmitHandler, ]; diff --git a/mocks/handlers/api/types.ts b/mocks/handlers/api/types.ts index fda525546..a9ccccd6d 100644 --- a/mocks/handlers/api/types.ts +++ b/mocks/handlers/api/types.ts @@ -4,29 +4,32 @@ import { types, subtypes } from "../../data/types"; type GetTypesBody = { authorityId: number }; type GetSubTypesBody = { authorityId: number; typeIds: number[] }; -const defaultTypeHandler = http.post(/\/getTypes$/, async ({ request }) => { - const { authorityId } = await request.json(); - - const hits = - types.filter( - (type) => - type?._source?.authorityId == authorityId && !type?._source?.name.match(/Do Not Use/), - ) || []; - - return HttpResponse.json({ - hits: { - hits, - }, - }); -}); - -export const errorTypeHandler = http.post( - /\/getTypes$/, +const defaultApiTypeHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getTypes", + async ({ request }) => { + const { authorityId } = await request.json(); + + const hits = + types.filter( + (type) => + type?._source?.authorityId == authorityId && !type?._source?.name.match(/Do Not Use/), + ) || []; + + return HttpResponse.json({ + hits: { + hits, + }, + }); + }, +); + +export const errorApiTypeHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getTypes", async () => new HttpResponse("Internal server error", { status: 500 }), ); -const defaultSubTypesHandler = http.post( - /\/getSubTypes$/, +const defaultApiSubTypesHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getSubTypes", async ({ request }) => { const { authorityId, typeIds } = await request.json(); @@ -46,9 +49,9 @@ const defaultSubTypesHandler = http.post( }, ); -export const errorSubTypesHandler = http.post( - /\/getSubTypes$/, +export const errorApiSubTypesHandler = http.post( + "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests/getSubTypes", () => new HttpResponse("Internal server error", { status: 500 }), ); -export const typeHandlers = [defaultTypeHandler, defaultSubTypesHandler]; +export const typeHandlers = [defaultApiTypeHandler, defaultApiSubTypesHandler]; diff --git a/mocks/handlers/api/user.ts b/mocks/handlers/api/user.ts index 7047e4f3b..48c07897f 100644 --- a/mocks/handlers/api/user.ts +++ b/mocks/handlers/api/user.ts @@ -1,6 +1,6 @@ import { CognitoUserAttribute } from "amazon-cognito-identity-js"; import { isCmsUser } from "shared-utils"; -import { findUserByUsername, convertUserAttributes } from "../authUtils"; +import { findUserByUsername, convertUserAttributes } from "../auth.utils"; import type { TestUserData } from "../.."; // using `any` type here because the function that this is mocking uses any diff --git a/mocks/handlers/authUtils.ts b/mocks/handlers/auth.utils.ts similarity index 100% rename from mocks/handlers/authUtils.ts rename to mocks/handlers/auth.utils.ts diff --git a/mocks/handlers/aws/cognito.ts b/mocks/handlers/aws/cognito.ts index 969cafea9..a83d392dc 100644 --- a/mocks/handlers/aws/cognito.ts +++ b/mocks/handlers/aws/cognito.ts @@ -15,7 +15,7 @@ import type { AdminGetUserRequestBody, TestUserData, } from "../../index.d"; -import { findUserByUsername } from "../authUtils"; +import { findUserByUsername } from "../auth.utils"; import { APIGatewayEventRequestContext } from "shared-types"; import { userResponses } from "../../data/users"; diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts index fe792b7f7..71de8b215 100644 --- a/mocks/handlers/index.ts +++ b/mocks/handlers/index.ts @@ -36,7 +36,7 @@ export { setDefaultReviewer, setDefaultStateSubmitter, setMockUsername, -} from "./authUtils.js"; +} from "./auth.utils"; export * from "./api"; export * from "./aws"; diff --git a/mocks/handlers/opensearch/changelog.ts b/mocks/handlers/opensearch/changelog.ts index 456c3a71b..99537d849 100644 --- a/mocks/handlers/opensearch/changelog.ts +++ b/mocks/handlers/opensearch/changelog.ts @@ -2,9 +2,9 @@ import { http, HttpResponse, PathParams } from "msw"; import { GET_ERROR_ITEM_ID } from "../../data"; import items from "../../data/items"; import { SearchQueryBody, TestChangelogDocument, TestChangelogItemResult } from "../../index.d"; -import { getTermKeys, getTermValues, filterItemsByTerm } from "./util"; +import { getTermKeys, getTermValues, filterItemsByTerm } from "../search.utils"; -const defaultChangelogSearchHandler = http.post( +const defaultOSChangelogSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-changelog/_search", async ({ request }) => { const { query } = await request.json(); @@ -73,4 +73,4 @@ const defaultChangelogSearchHandler = http.post( }, ); -export const changelogSearchHandlers = [defaultChangelogSearchHandler]; +export const changelogSearchHandlers = [defaultOSChangelogSearchHandler]; diff --git a/mocks/handlers/opensearch/cpocs.ts b/mocks/handlers/opensearch/cpocs.ts index 8e603ca86..2a3c34c65 100644 --- a/mocks/handlers/opensearch/cpocs.ts +++ b/mocks/handlers/opensearch/cpocs.ts @@ -1,7 +1,7 @@ import { http, HttpResponse } from "msw"; import { cpocsList } from "../../data/cpocs"; -const defaultCpocSearchHandler = http.post( +const defaultOSCpocSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-cpocs/_search", () => HttpResponse.json({ @@ -24,14 +24,14 @@ const defaultCpocSearchHandler = http.post( }), ); -export const emptyCpocSearchHandler = http.post( +export const emptyOSCpocSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-cpocs/_search", () => new HttpResponse(), ); -export const errorCpocSearchHandler = http.post( +export const errorOSCpocSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-cpocs/_search", () => new HttpResponse("Internal server error", { status: 500 }), ); -export const cpocSearchHandlers = [defaultCpocSearchHandler]; +export const cpocSearchHandlers = [defaultOSCpocSearchHandler]; diff --git a/mocks/handlers/opensearch/index.ts b/mocks/handlers/opensearch/index.ts index 48e8c921e..844c985c1 100644 --- a/mocks/handlers/opensearch/index.ts +++ b/mocks/handlers/opensearch/index.ts @@ -16,7 +16,7 @@ export const opensearchHandlers = [ ...typeSearchHandlers, ]; -export { emptyCpocSearchHandler, errorCpocSearchHandler } from "./cpocs"; +export { emptyOSCpocSearchHandler, errorOSCpocSearchHandler } from "./cpocs"; export { errorCreateIndexHandler, errorUpdateFieldMappingHandler, @@ -24,7 +24,7 @@ export { rateLimitBulkUpdateDataHandler, errorDeleteIndexHandler, } from "./indices"; -export { errorMainMultiDocumentHandler } from "./main"; +export { errorOSMainMultiDocumentHandler } from "./main"; export { errorSecurityRolesMappingHandler } from "./security"; -export { errorSubtypeSearchHandler } from "./subtypes"; -export { errorTypeSearchHandler } from "./types"; +export { errorOSSubtypeSearchHandler } from "./subtypes"; +export { errorOSTypeSearchHandler } from "./types"; diff --git a/mocks/handlers/opensearch/main.ts b/mocks/handlers/opensearch/main.ts index eae507d28..08e529580 100644 --- a/mocks/handlers/opensearch/main.ts +++ b/mocks/handlers/opensearch/main.ts @@ -9,9 +9,9 @@ import { TestMainDocument, GetMultiItemBody, } from "../../index.d"; -import { getTermKeys, filterItemsByTerm, getTermValues } from "./util"; +import { getTermKeys, filterItemsByTerm, getTermValues } from "../search.utils"; -const defaultMainDocumentHandler = http.get( +const defaultOSMainDocumentHandler = http.get( `https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_doc/:id`, async ({ params }) => { const { id } = params; @@ -29,7 +29,7 @@ const defaultMainDocumentHandler = http.get( }, ); -const defaultMainMultiDocumentHandler = http.post( +const defaultOSMainMultiDocumentHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_mget", async ({ request }) => { const { ids } = await request.json(); @@ -42,12 +42,12 @@ const defaultMainMultiDocumentHandler = http.post( }, ); -export const errorMainMultiDocumentHandler = http.post( +export const errorOSMainMultiDocumentHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_mget", () => new HttpResponse("Internal server error", { status: 500 }), ); -const defaultMainSearchHandler = http.post( +const defaultOSMainSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_search", async ({ request }) => { const { query } = await request.json(); @@ -156,7 +156,7 @@ const defaultMainSearchHandler = http.post( ); export const mainSearchHandlers = [ - defaultMainDocumentHandler, - defaultMainMultiDocumentHandler, - defaultMainSearchHandler, + defaultOSMainDocumentHandler, + defaultOSMainMultiDocumentHandler, + defaultOSMainSearchHandler, ]; diff --git a/mocks/handlers/opensearch/subtypes.ts b/mocks/handlers/opensearch/subtypes.ts index 94151831d..983cfbe89 100644 --- a/mocks/handlers/opensearch/subtypes.ts +++ b/mocks/handlers/opensearch/subtypes.ts @@ -1,9 +1,9 @@ import { http, HttpResponse, PathParams } from "msw"; import { subtypes } from "../../data/types"; import { SearchQueryBody } from "../../index.d"; -import { getFilterValueAsNumber, getFilterValueAsNumberArray } from "./util"; +import { getFilterValueAsNumber, getFilterValueAsNumberArray } from "../search.utils"; -const defaultSubtypeSearchHandler = http.post( +const defaultOSSubtypeSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-subtypes/_search", async ({ request }) => { const { query } = await request.json(); @@ -45,9 +45,9 @@ const defaultSubtypeSearchHandler = http.post( }, ); -export const errorSubtypeSearchHandler = http.post( +export const errorOSSubtypeSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-subtypes/_search", () => new HttpResponse("Internal server error", { status: 500 }), ); -export const subtypeSearchHandlers = [defaultSubtypeSearchHandler]; +export const subtypeSearchHandlers = [defaultOSSubtypeSearchHandler]; diff --git a/mocks/handlers/opensearch/types.ts b/mocks/handlers/opensearch/types.ts index 5c298ecd9..8928c2c4b 100644 --- a/mocks/handlers/opensearch/types.ts +++ b/mocks/handlers/opensearch/types.ts @@ -1,9 +1,9 @@ import { http, HttpResponse, PathParams } from "msw"; import { types } from "../../data/types"; import { SearchQueryBody } from "../../index.d"; -import { getFilterValueAsNumber } from "./util"; +import { getFilterValueAsNumber } from "../search.utils"; -const defaultTypeSearchHandler = http.post( +const defaultOSTypeSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-types/_search", async ({ request }) => { const { query } = await request.json(); @@ -42,9 +42,9 @@ const defaultTypeSearchHandler = http.post( }, ); -export const errorTypeSearchHandler = http.post( +export const errorOSTypeSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-types/_search", () => new HttpResponse("Internal server error", { status: 500 }), ); -export const typeSearchHandlers = [defaultTypeSearchHandler]; +export const typeSearchHandlers = [defaultOSTypeSearchHandler]; diff --git a/mocks/handlers/opensearch/util.ts b/mocks/handlers/search.utils.ts similarity index 99% rename from mocks/handlers/opensearch/util.ts rename to mocks/handlers/search.utils.ts index 53ffd55d8..608a7b2e1 100644 --- a/mocks/handlers/opensearch/util.ts +++ b/mocks/handlers/search.utils.ts @@ -1,4 +1,4 @@ -import { QueryContainer, TermQuery, TermsQuery, TestHit } from "../../index.d"; +import { QueryContainer, TermQuery, TermsQuery, TestHit } from ".."; export const getFilterValue = ( query: QueryContainer | QueryContainer[] | undefined, diff --git a/mocks/helpers/index.ts b/mocks/helpers/index.ts index f3ae14d1a..a99e252ff 100644 --- a/mocks/helpers/index.ts +++ b/mocks/helpers/index.ts @@ -1 +1 @@ -export * from "./kafka-test-helpers"; +export * from "./kafka.utils"; diff --git a/mocks/helpers/kafka-test-helpers.ts b/mocks/helpers/kafka.utils.ts similarity index 100% rename from mocks/helpers/kafka-test-helpers.ts rename to mocks/helpers/kafka.utils.ts diff --git a/react-app/.env.test b/react-app/.env.test new file mode 100644 index 000000000..188f9ca37 --- /dev/null +++ b/react-app/.env.test @@ -0,0 +1,14 @@ +VITE_API_REGION="us-east-1" +VITE_API_URL=https://test-domain.execute-api.us-east-1.amazonaws.com/mocked-tests +VITE_NODE_ENV="development" +VITE_COGNITO_REGION=us-east-1 +VITE_COGNITO_IDENTITY_POOL_ID=us-east-1::test-identity-pool-id +VITE_COGNITO_USER_POOL_ID=us-east-1_userPool1 +VITE_COGNITO_USER_POOL_CLIENT_ID=userPoolWebClientId +VITE_COGNITO_USER_POOL_CLIENT_DOMAIN=mocked-tests-login-userPoolWebClientId.auth.us-east-1.amazoncognito.com +VITE_COGNITO_REDIRECT_SIGNIN="http://localhost:5000/" +VITE_COGNITO_REDIRECT_SIGNOUT="http://localhost:5000/" +VITE_IDM_HOME_URL=https://test.home.idm.cms.gov +VITE_GOOGLE_ANALYTICS_GTAG="" +VITE_GOOGLE_ANALYTICS_DISABLE="true" +VITE_LAUNCHDARKLY_CLIENT_ID="6638280397c1bc569aea5f3f" diff --git a/react-app/src/api/getAttachmentUrl.test.ts b/react-app/src/api/getAttachmentUrl.test.ts index 67de816de..7c387e564 100644 --- a/react-app/src/api/getAttachmentUrl.test.ts +++ b/react-app/src/api/getAttachmentUrl.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from "vitest"; import { getAttachmentUrl } from "./getAttachmentUrl"; -import { ATTACHMENT_BUCKET_NAME, ATTACHMENT_BUCKET_REGION, errorAttachmentUrlHandler } from "mocks"; +import { + ATTACHMENT_BUCKET_NAME, + ATTACHMENT_BUCKET_REGION, + errorApiAttachmentUrlHandler, +} from "mocks"; import { mockedApiServer as mockedServer } from "mocks/server"; describe("getAttachmentUrl tests", () => { @@ -16,7 +20,7 @@ describe("getAttachmentUrl tests", () => { }); it("should throw an error if the response is 500", async () => { - mockedServer.use(errorAttachmentUrlHandler); + mockedServer.use(errorApiAttachmentUrlHandler); await expect(() => getAttachmentUrl(id, ATTACHMENT_BUCKET_NAME, key, filename), diff --git a/react-app/src/api/itemExists.test.ts b/react-app/src/api/itemExists.test.ts index 1dedd78fe..0cc125abc 100644 --- a/react-app/src/api/itemExists.test.ts +++ b/react-app/src/api/itemExists.test.ts @@ -4,7 +4,7 @@ import { TEST_ITEM_ID, NOT_FOUND_ITEM_ID, NOT_EXISTING_ITEM_ID, - errorItemExistsHandler, + errorApiItemExistsHandler, } from "mocks"; import { mockedApiServer as mockedServer } from "mocks/server"; @@ -25,7 +25,7 @@ describe("itemExists test", () => { }); it("should return false if there is an error getting the item", async () => { - mockedServer.use(errorItemExistsHandler); + mockedServer.use(errorApiItemExistsHandler); const found = await itemExists(TEST_ITEM_ID); expect(found).toBeFalsy(); diff --git a/react-app/src/api/useGetCPOCs.test.ts b/react-app/src/api/useGetCPOCs.test.ts index c576b5f55..60e5f9ff1 100644 --- a/react-app/src/api/useGetCPOCs.test.ts +++ b/react-app/src/api/useGetCPOCs.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { fetchCpocData } from "./useGetCPOCs"; -import { errorCpocHandler } from "mocks"; +import { errorApiCpocHandler } from "mocks"; import { mockedApiServer as mockedServer } from "mocks/server"; import { cpocsList } from "mocks/data/cpocs"; @@ -12,7 +12,7 @@ describe("useGetCPOCs test", () => { }); it("should handle an error when fetching CPOCs", async () => { - mockedServer.use(errorCpocHandler); + mockedServer.use(errorApiCpocHandler); const result = await fetchCpocData(); expect(result).toBeUndefined(); diff --git a/react-app/src/api/useGetPackageActions.test.ts b/react-app/src/api/useGetPackageActions.test.ts index f2ce5dd78..38fa24615 100644 --- a/react-app/src/api/useGetPackageActions.test.ts +++ b/react-app/src/api/useGetPackageActions.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, afterEach } from "vitest"; import { getPackageActions } from "./useGetPackageActions"; import { Action } from "shared-types"; import { - errorPackageActionsHandler, + errorApiPackageActionsHandler, WITHDRAW_RAI_ITEM_C, TEST_ITEM_ID, NOT_FOUND_ITEM_ID, @@ -64,7 +64,7 @@ describe("getPackageActions test", () => { }); it("should return 500 if there is a server error", async () => { - mockedServer.use(errorPackageActionsHandler); + mockedServer.use(errorApiPackageActionsHandler); await expect(() => getPackageActions(WITHDRAW_RAI_ITEM_C)).rejects.toThrowError( "Request failed with status code 500", diff --git a/react-app/src/api/useGetTypes.test.ts b/react-app/src/api/useGetTypes.test.ts index dacf6a638..b4b321e03 100644 --- a/react-app/src/api/useGetTypes.test.ts +++ b/react-app/src/api/useGetTypes.test.ts @@ -14,7 +14,7 @@ import { chipTypes, chipSubtypes, } from "mocks/data/types"; -import { errorSubTypesHandler, errorTypeHandler } from "mocks"; +import { errorApiSubTypesHandler, errorApiTypeHandler } from "mocks"; import { mockedApiServer as mockedServer } from "mocks/server"; describe("fetchData", () => { @@ -35,7 +35,7 @@ describe("fetchData", () => { }); it("throws an error when fetch fails", async () => { - mockedServer.use(errorTypeHandler); + mockedServer.use(errorApiTypeHandler); await expect(fetchData({ authorityId: ERROR_AUTHORITY_ID })).rejects.toThrow( "Failed to fetch types", @@ -69,7 +69,7 @@ describe("fetchData", () => { }); it("throws an error when fetch fails", async () => { - mockedServer.use(errorSubTypesHandler); + mockedServer.use(errorApiSubTypesHandler); await expect(fetchData({ authorityId: ERROR_AUTHORITY_ID, typeIds: [] })).rejects.toThrow( "Failed to fetch subtypes", diff --git a/react-app/src/api/useSearch.test.ts b/react-app/src/api/useSearch.test.ts new file mode 100644 index 000000000..c412d53ea --- /dev/null +++ b/react-app/src/api/useSearch.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vitest"; +import { getOsData } from "./useSearch"; +import { cpocsList } from "mocks/data/cpocs"; + +describe("getOsData tests", () => { + it("should return cpocs", async () => { + const results = await getOsData({ + index: "cpocs", + sort: { + field: "lastName", + order: "asc", + }, + pagination: { + number: 0, + size: 20, + }, + filters: [], + }); + expect(results.hits.hits).toEqual(cpocsList); + }); +}); From 8181a1c9d5fad3298be40dfbf76602665a3ef6c2 Mon Sep 17 00:00:00 2001 From: tiffanyvu Date: Fri, 17 Jan 2025 12:09:43 -0800 Subject: [PATCH 6/6] chore(lib): add 404 handling back to getItem (#1040) * add 404 handling back * fix eslint --- lib/libs/opensearch-lib.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/libs/opensearch-lib.ts b/lib/libs/opensearch-lib.ts index 397172bb3..9308162fe 100644 --- a/lib/libs/opensearch-lib.ts +++ b/lib/libs/opensearch-lib.ts @@ -180,13 +180,24 @@ export async function getItem( index: opensearch.Index, id: string, ): Promise { - client = client || (await getClient(host)); - const response = await client.get({ id, index }); - const item = decodeUtf8(response).body; - if (item.found === false || !item._source) { - return undefined; + try { + client = client || (await getClient(host)); + const response = await client.get({ id, index }); + const item = decodeUtf8(response).body; + if (item.found === false || !item._source) { + return undefined; + } + return item; + } catch (error) { + if ( + (error instanceof OpensearchErrors.ResponseError && error.statusCode === 404) || + error.meta?.statusCode === 404 + ) { + console.log("Error (404) retrieving in OpenSearch:", error); + return undefined; + } + throw error; } - return item; } export async function getItems(ids: string[]): Promise {