Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#IOPID-1604] redirect to confirm page #72

Merged
merged 10 commits into from
Feb 29, 2024
135 changes: 94 additions & 41 deletions ValidateProfileEmail/__tests__/handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { ResourceNotFoundCode } from "@pagopa/io-functions-commons/dist/src/utils/azure_storage";

import {
TokenQueryParam,
ValidateProfileEmailHandler,
ValidationErrors
} from "../handler";
import { ValidateProfileEmailHandler } from "../handler";
import { EmailString, FiscalCode } from "@pagopa/ts-commons/lib/strings";
import { IProfileEmailReader } from "@pagopa/io-functions-commons/dist/src/utils/unique_email_enforcement";
import { constTrue } from "fp-ts/lib/function";
import * as TE from "fp-ts/TaskEither";
import * as O from "fp-ts/Option";
import { ProfileModel } from "@pagopa/io-functions-commons/dist/src/models/profile";
import { aFiscalCode, aRetrievedProfile, anEmail } from "../__mocks__/profile";
import { ValidUrl } from "@pagopa/ts-commons/lib/url";
import { FlowChoiceEnum, TokenQueryParam } from "../../utils/middleware";
import {
confirmChoicePageUrl,
validationFailureUrl,
validationSuccessUrl
} from "../../utils/redirect_url";

const VALIDATION_TOKEN = "01DPT9QAZ6N0FJX21A86FRCWB3:8c652f8566ba53bd8cf0b1b9" as TokenQueryParam;

Expand All @@ -36,20 +39,14 @@ const contextMock = {
};

const validationCallbackUrl = {
href: ""
};
const timestampGeneratorMock = () => 1234567890;
href: "localhost/validation"
} as ValidUrl;

const errorUrl = (
error: keyof typeof ValidationErrors,
timestampGenerator: () => number
) => {
return `?result=failure&error=${error}&time=${timestampGenerator()}`;
};
const confirmChoiceUrl = {
href: "localhost/confirm-choice"
} as ValidUrl;

const successUrl = (timestampGenerator: () => number) => {
return `?result=success&time=${timestampGenerator()}`;
};
const timestampGeneratorMock = () => 1234567890;

const mockRetrieveEntity = jest
.fn()
Expand Down Expand Up @@ -95,14 +92,21 @@ const expiredTokenEntity = {
};

describe("ValidateProfileEmailHandler", () => {
beforeEach(() => {
jest.clearAllMocks();
});

it.each`
scenario | expectedError | callbackInputs
${"GENERIC_ERROR in case the query versus the table storage fails"} | ${"GENERIC_ERROR"} | ${[new Error()]}
${"INVALID_TOKEN error in case the token if not found in the table"} | ${"INVALID_TOKEN"} | ${[{ code: ResourceNotFoundCode }]}
${"TOKEN_EXPIRED error in case the token is expired"} | ${"TOKEN_EXPIRED"} | ${[undefined, expiredTokenEntity]}
scenario | expectedError | callbackInputs | isConfirmFlow
${"GENERIC_ERROR in case the query versus the table storage fails during confirm flow"} | ${"GENERIC_ERROR"} | ${[new Error()]} | ${true}
${"GENERIC_ERROR in case the query versus the table storage fails"} | ${"GENERIC_ERROR"} | ${[new Error()]} | ${false}
${"INVALID_TOKEN error in case the token if not found in the table during confirm flow"} | ${"INVALID_TOKEN"} | ${[{ code: ResourceNotFoundCode }]} | ${true}
${"INVALID_TOKEN error in case the token if not found in the table"} | ${"INVALID_TOKEN"} | ${[{ code: ResourceNotFoundCode }]} | ${false}
${"TOKEN_EXPIRED error in case the token is expired during confirm flow"} | ${"TOKEN_EXPIRED"} | ${[undefined, expiredTokenEntity]} | ${true}
${"TOKEN_EXPIRED error in case the token is expired"} | ${"TOKEN_EXPIRED"} | ${[undefined, expiredTokenEntity]} | ${false}
arcogabbo marked this conversation as resolved.
Show resolved Hide resolved
`(
"should return a redirect with a $scenario",
async ({ callbackInputs, expectedError }) => {
async ({ callbackInputs, expectedError, isConfirmFlow }) => {
mockRetrieveEntity.mockImplementationOnce((_, __, ___, ____, f) => {
f(...callbackInputs);
});
Expand All @@ -114,54 +118,101 @@ describe("ValidateProfileEmailHandler", () => {
validationCallbackUrl as any,
timestampGeneratorMock,
profileEmailReader,
constTrue
constTrue,
confirmChoiceUrl
);

const response = await verifyProfileEmailHandler(
contextMock as any,
VALIDATION_TOKEN
VALIDATION_TOKEN,
isConfirmFlow ? FlowChoiceEnum.CONFIRM : FlowChoiceEnum.VALIDATE
);

expect(response.kind).toBe("IResponseSeeOtherRedirect");
expect(response.detail).toBe(
errorUrl(expectedError, timestampGeneratorMock)
validationFailureUrl(
validationCallbackUrl,
expectedError,
timestampGeneratorMock
).href
);
expect(mockFindLastVersionByModelId).not.toBeCalled();
expect(mockUpdate).not.toBeCalled();
}
);

it.each`
scenario | expectedError | isThrowing
${"should return IResponseSeeOtherRedirect if the e-mail is already taken (unique email enforcement = %uee) WHEN a citizen changes e-mail"} | ${"EMAIL_ALREADY_TAKEN"} | ${undefined}
${"return 500 WHEN the unique e-mail enforcement check fails"} | ${"GENERIC_ERROR"} | ${true}
`("should $scenario", async ({ expectedError, isThrowing }) => {
scenario | expectedError | isThrowing | isConfirmFlow
${"should return IResponseSeeOtherRedirect if the e-mail is already taken (unique email enforcement = %uee) WHEN a citizen changes e-mail during confirm flow"} | ${"EMAIL_ALREADY_TAKEN"} | ${undefined} | ${true}
${"should return IResponseSeeOtherRedirect if the e-mail is already taken (unique email enforcement = %uee) WHEN a citizen changes e-mail"} | ${"EMAIL_ALREADY_TAKEN"} | ${undefined} | ${false}
${"return 500 WHEN the unique e-mail enforcement check fails during confirm flow"} | ${"GENERIC_ERROR"} | ${true} | ${true}
${"return 500 WHEN the unique e-mail enforcement check fails"} | ${"GENERIC_ERROR"} | ${true} | ${false}
`(
"should $scenario",
async ({ expectedError, isThrowing, isConfirmFlow }) => {
const verifyProfileEmailHandler = ValidateProfileEmailHandler(
tableServiceMock as any,
"",
mockProfileModel,
validationCallbackUrl as any,
timestampGeneratorMock,
{
list: generateProfileEmails(1, isThrowing)
},
constTrue,
confirmChoiceUrl
);

const response = await verifyProfileEmailHandler(
contextMock as any,
VALIDATION_TOKEN,
isConfirmFlow ? FlowChoiceEnum.CONFIRM : FlowChoiceEnum.VALIDATE
);

expect(response.kind).toBe("IResponseSeeOtherRedirect");
expect(response.detail).toBe(
validationFailureUrl(
validationCallbackUrl,
expectedError,
timestampGeneratorMock
).href
);
expect(mockFindLastVersionByModelId).toBeCalledWith([aFiscalCode]);
expect(mockUpdate).not.toBeCalled();
}
);

it("should validate the email in profile if all the condition are verified and we are in the confirm flow", async () => {
const verifyProfileEmailHandler = ValidateProfileEmailHandler(
tableServiceMock as any,
"",
mockProfileModel,
validationCallbackUrl as any,
timestampGeneratorMock,
{
list: generateProfileEmails(1, isThrowing)
list: generateProfileEmails(0)
},
constTrue
constTrue,
confirmChoiceUrl
);

const response = await verifyProfileEmailHandler(
contextMock as any,
VALIDATION_TOKEN
VALIDATION_TOKEN,
FlowChoiceEnum.VALIDATE
);

expect(response.kind).toBe("IResponseSeeOtherRedirect");
expect(response.detail).toBe(
errorUrl(expectedError, timestampGeneratorMock)
validationSuccessUrl(validationCallbackUrl, timestampGeneratorMock).href
);
expect(mockFindLastVersionByModelId).toBeCalledWith([aFiscalCode]);
expect(mockUpdate).not.toBeCalled();
expect(mockUpdate).toBeCalledWith(
expect.objectContaining({ isEmailValidated: true })
);
});

it("should validate the email in profile if all the condition are verified", async () => {
it("should NOT validate the email in profile if we are in the CONFIRM flow", async () => {
const verifyProfileEmailHandler = ValidateProfileEmailHandler(
tableServiceMock as any,
"",
Expand All @@ -171,19 +222,21 @@ describe("ValidateProfileEmailHandler", () => {
{
list: generateProfileEmails(0)
},
constTrue
constTrue,
confirmChoiceUrl
);

const response = await verifyProfileEmailHandler(
contextMock as any,
VALIDATION_TOKEN
VALIDATION_TOKEN,
FlowChoiceEnum.CONFIRM
);

expect(response.kind).toBe("IResponseSeeOtherRedirect");
expect(response.detail).toBe(successUrl(timestampGeneratorMock));
expect(mockFindLastVersionByModelId).toBeCalledWith([aFiscalCode]);
expect(mockUpdate).toBeCalledWith(
expect.objectContaining({ isEmailValidated: true })
expect(response.detail).toBe(
confirmChoicePageUrl(confirmChoiceUrl, VALIDATION_TOKEN, anEmail).href
);
expect(mockFindLastVersionByModelId).toBeCalledWith([aFiscalCode]);
expect(mockUpdate).not.toHaveBeenCalled();
});
});
Loading
Loading