Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion test/unit/middlewares/impersonationRequests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import sinon from "sinon";
import {
createImpersonationRequestValidator,
getImpersonationRequestByIdValidator,
getImpersonationRequestsValidator
getImpersonationRequestsValidator,
updateImpersonationRequestValidator
} from "../../../middlewares/validators/impersonationRequests";
import {
CreateImpersonationRequest,
CreateImpersonationRequestBody,
ImpersonationRequestResponse,
GetImpersonationControllerRequest,
GetImpersonationRequestByIdRequest,
UpdateImpersonationRequest,
UpdateImpersonationRequestStatusBody,
} from "../../../types/impersonationRequest";
import { Request, Response } from "express";
import { getImpersonationRequestById } from "../../../models/impersonationRequests";
Expand All @@ -28,6 +31,11 @@ describe("Impersonation Request Validators", function () {
reason: "Testing purpose",
};

const updateRequestBody: UpdateImpersonationRequestStatusBody = {
status: "APPROVED",
message: "Testing",
};

beforeEach(function () {
res = {
boom: {
Expand Down Expand Up @@ -177,4 +185,51 @@ describe("Impersonation Request Validators", function () {
expect(nextSpy.called).to.be.false;
});
});

describe("updateImpersonationRequestValidator", function () {
it("should validate for a valid update impersonation request", async function () {
req = {
body: updateRequestBody,
};
await updateImpersonationRequestValidator(
req as UpdateImpersonationRequest,
res as ImpersonationRequestResponse,
nextSpy
);
expect(nextSpy.calledOnce).to.be.true;
});


it("should validate an update request even with missing message field", async function() {
req = {
body: {status:"APPROVED"}
}
await updateImpersonationRequestValidator(req as UpdateImpersonationRequest,res as ImpersonationRequestResponse, nextSpy);
expect(nextSpy.calledOnce).to.be.true;
})

it("should not validate for an invalid update impersonation request on missing status", async function () {
req = {
body: { ...updateRequestBody, status: "" },
};
await updateImpersonationRequestValidator(
req as UpdateImpersonationRequest,
res as ImpersonationRequestResponse,
nextSpy
);
expect(res.boom.badRequest.calledOnce).to.be.true;
expect(nextSpy.called).to.be.false;
});

it("should invalidate if status field is not of correct type", async function () {
req = {
body: { ...updateRequestBody, status: "ACTIVE" },
};
await updateImpersonationRequestValidator(req as UpdateImpersonationRequest,res as ImpersonationRequestResponse, nextSpy);
const errorMessageArg = res.boom.badRequest.firstCall.args[0];
expect(res.boom.badRequest.calledOnce).to.be.true;
expect(errorMessageArg).to.include("status must be APPROVED or REJECTED");
expect(nextSpy.called).to.be.false;
});
});
});
99 changes: 89 additions & 10 deletions test/unit/models/impersonationRequests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { expect } from "chai";
import cleanDb from "../../utils/cleanDb";
import * as impersonationModel from "../../../models/impersonationRequests";
import { impersonationRequestsBodyData } from "../../fixtures/impersonation-requests/impersonationRequests";
import { REQUEST_STATE, ERROR_WHILE_CREATING_REQUEST } from "../../../constants/requests";
import { REQUEST_STATE, ERROR_WHILE_CREATING_REQUEST, ERROR_WHILE_UPDATING_REQUEST } from "../../../constants/requests";
import addUser from "../../utils/addUser";
import userDataFixture from "../../fixtures/user/user";
import sinon from "sinon";
import { UpdateImpersonationRequestStatusBody, UpdateImpersonationRequestDataResponse } from "../../../types/impersonationRequest";
import { Timestamp } from "firebase-admin/firestore";
import firestore from "../../../utils/firestore";


const userData = userDataFixture();
const logger = require("../../../utils/logger");

describe("models/impersonationRequests", () => {
let impersonationRequest;
Expand All @@ -26,7 +30,7 @@ describe("models/impersonationRequests", () => {
await cleanDb();
});

describe("createImpersonationRequest", () => {
describe("createImpersonationRequest", () => {
it("should create a new impersonation request", async () => {
impersonationRequest = await impersonationModel.createImpersonationRequest(mockRequestBody);
expect(impersonationRequest).to.have.property("id");
Expand All @@ -39,7 +43,7 @@ describe("models/impersonationRequests", () => {
});

it("should throw an error if there is an existing PENDING impersonation request", async () => {
await impersonationModel.createImpersonationRequest(mockRequestBody);
await impersonationModel.createImpersonationRequest(mockRequestBody);
try {
await impersonationModel.createImpersonationRequest(mockRequestBody);
} catch (error) {
Expand All @@ -48,8 +52,8 @@ describe("models/impersonationRequests", () => {
});

it("should allow different super users to create requests for same user", async () => {
const request1 = await impersonationModel.createImpersonationRequest({ ...impersonationRequestsBodyData[0],createdBy: "user1" });
const request2 = await impersonationModel.createImpersonationRequest({ ...impersonationRequestsBodyData[0],createdBy: "user2", userId:"122" });
const request1 = await impersonationModel.createImpersonationRequest({ ...impersonationRequestsBodyData[0], createdBy: "user1" });
const request2 = await impersonationModel.createImpersonationRequest({ ...impersonationRequestsBodyData[0], createdBy: "user2", userId: "122" });
expect(request1).to.have.property("id");
expect(request1.createdBy).to.equal("user1");
expect(request1.impersonatedUserId).to.equal(impersonationRequestsBodyData[0].impersonatedUserId);
Expand All @@ -69,16 +73,16 @@ describe("models/impersonationRequests", () => {
}
});

it("should throw forbidden error if an APPROVED request with isImpersonationFinished as false is present", async ()=>{
it("should throw forbidden error if an APPROVED request with isImpersonationFinished as false is present", async () => {
try {
await impersonationModel.createImpersonationRequest({...impersonationRequestsBodyData[0],status:REQUEST_STATE.APPROVED});
await impersonationModel.createImpersonationRequest({ ...impersonationRequestsBodyData[0], status: REQUEST_STATE.APPROVED });
await impersonationModel.createImpersonationRequest(impersonationRequestsBodyData[0]);
} catch (error) {
expect(error.message).to.include("You are not allowed for this Operation at the moment");
}
})
});
});

describe("getImpersonationRequestById", () => {
it("should return the impersonation request by id", async () => {
const impersonationRequest = await impersonationModel.createImpersonationRequest(impersonationRequestsBodyData[0]);
Expand Down Expand Up @@ -206,4 +210,79 @@ describe("models/impersonationRequests", () => {
}
});
});

describe("updateImpersonationRequest", () => {
beforeEach(async () => {
impersonationRequest = await impersonationModel.createImpersonationRequest(impersonationRequestsBodyData[0]);
});

it("should approve an impersonation request", async () => {
const updatedRequest = await impersonationModel.updateImpersonationRequest({
id: impersonationRequest.id,
updatePayload: { status: "APPROVED" },
lastModifiedBy: impersonationRequest.impersonatedUserId,
}) as UpdateImpersonationRequestStatusBody;
expect(updatedRequest.status).to.equal(REQUEST_STATE.APPROVED);
});

it("should reject an impersonation request", async () => {
const updatedRequest = await impersonationModel.updateImpersonationRequest({
id: impersonationRequest.id,
updatePayload: { status: "REJECTED" },
lastModifiedBy: impersonationRequest.impersonatedUserId,
}) as UpdateImpersonationRequestStatusBody;
expect(updatedRequest.status).to.equal(REQUEST_STATE.REJECTED);
});

it("should change the startedAt,endedAt and isImpersonationFinished fields on update", async () => {
const updatedBody = {
isImpersonationFinished: true,
startedAt: Timestamp.fromDate(new Date(Date.now())),
endedAt: Timestamp.fromDate(new Date(Date.now() + 15 * 60 * 1000)),
};
const updatedRequest = await impersonationModel.updateImpersonationRequest({
id: impersonationRequest.id,
updatePayload: updatedBody,
lastModifiedBy: impersonationRequest.userId,
}) as UpdateImpersonationRequestDataResponse;
expect(updatedRequest.isImpersonationFinished).to.be.true;
expect(Number(updatedRequest.startedAt)).to.be.greaterThan(0);
expect(Number(updatedRequest.endedAt)).to.be.greaterThan(Number(updatedRequest.startedAt));
});

it("should change updatedAt timestamp on update", async () => {
const before = Number(impersonationRequest.updatedAt);
const updatedRequest = await impersonationModel.updateImpersonationRequest({
id: impersonationRequest.id,
updatePayload: { status: "APPROVED" },
lastModifiedBy: impersonationRequest.impersonatedUserId,
});
const result = await impersonationModel.getImpersonationRequestById(impersonationRequest.id);
expect(result).to.not.be.null;
expect(Number(result.updatedAt)).to.be.greaterThan(before);
});

it("should log and throw error if Firestore update fails in updateImpersonationRequest", async () => {
const error = new Error(ERROR_WHILE_UPDATING_REQUEST);
const loggerStub = sinon.stub(logger, "error");

const docUpdateStub = sinon.stub().rejects(error);
const docStub = sinon.stub().returns({ update: docUpdateStub });
const collectionStub = sinon.stub().returns({ doc: docStub });
sinon.stub(firestore, "collection").callsFake(collectionStub);

try {
await impersonationModel.updateImpersonationRequest({
id: "impersonationRequest.id",
updatePayload: { status: "APPROVED" },
lastModifiedBy: impersonationRequest.impersonatedUserId,
});
expect.fail("Should throw error");
} catch (err) {
expect(loggerStub.called).to.be.true;
expect(loggerStub.firstCall.args[0]).to.include(ERROR_WHILE_UPDATING_REQUEST);
expect(loggerStub.firstCall.args[1]).to.equal(err);
}
});
});
});
Loading
Loading