Skip to content

Commit

Permalink
feat: Add test cases for controller and validator
Browse files Browse the repository at this point in the history
- Remove failing tests and fix existing tests

- Add tests to check success and unexpected behaviour and fix existing tests

- Replace actual messages with constants for easily maintenance

- Add test for super user and request owner authorization check and fix existing failing tests

- Remove un-necessary changes

- Remove separate file for validator tests
  • Loading branch information
pankajjs committed Jan 16, 2025
1 parent 22fd044 commit 0abdafd
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 2 deletions.
246 changes: 244 additions & 2 deletions test/integration/onboardingExtension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import userDataFixture from "../fixtures/user/user";
import sinon from "sinon";
import chaiHttp from "chai-http";
import cleanDb from "../utils/cleanDb";
import { CreateOnboardingExtensionBody } from "../../types/onboardingExtension";
import { CreateOnboardingExtensionBody, OnboardingExtension } from "../../types/onboardingExtension";
import {
REQUEST_ALREADY_PENDING,
REQUEST_STATE, REQUEST_TYPE,
ONBOARDING_REQUEST_CREATED_SUCCESSFULLY,
UNAUTHORIZED_TO_CREATE_ONBOARDING_EXTENSION_REQUEST,
REQUEST_FETCHED_SUCCESSFULLY
REQUEST_FETCHED_SUCCESSFULLY,
INVALID_REQUEST_DEADLINE,

Check failure on line 15 in test/integration/onboardingExtension.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Module '"../../constants/requests"' has no exported member 'INVALID_REQUEST_DEADLINE'.
PENDING_REQUEST_UPDATED,

Check failure on line 16 in test/integration/onboardingExtension.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Module '"../../constants/requests"' has no exported member 'PENDING_REQUEST_UPDATED'.
REQUEST_UPDATED_SUCCESSFULLY,

Check failure on line 17 in test/integration/onboardingExtension.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

'"../../constants/requests"' has no exported member named 'REQUEST_UPDATED_SUCCESSFULLY'. Did you mean 'REQUEST_CREATED_SUCCESSFULLY'?
INVALID_REQUEST_TYPE,

Check failure on line 18 in test/integration/onboardingExtension.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Module '"../../constants/requests"' has no exported member 'INVALID_REQUEST_TYPE'.
REQUEST_DOES_NOT_EXIST,
UNAUTHORIZED_TO_UPDATE_REQUEST

Check failure on line 20 in test/integration/onboardingExtension.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Module '"../../constants/requests"' has no exported member 'UNAUTHORIZED_TO_UPDATE_REQUEST'.
} from "../../constants/requests";
const { generateToken } = require("../../test/utils/generateBotToken");
import app from "../../server";
Expand All @@ -21,6 +27,10 @@ const userStatusModel = firestore.collection("usersStatus");
import * as requestsQuery from "../../models/requests"
import { userState } from "../../constants/userStatus";
const { CLOUDFLARE_WORKER, BAD_TOKEN } = require("../../constants/bot");
import * as logUtils from "../../services/logService";
import { convertDaysToMilliseconds } from "../../utils/time";
import { OooStatusRequest } from "../../types/oooRequest";
import { generateAuthToken } from "../../services/authService";
const userData = userDataFixture();
chai.use(chaiHttp);

Expand Down Expand Up @@ -378,4 +388,236 @@ describe("/requests Onboarding Extension", () => {
});
});
});

describe("PATCH /requests", () => {
const body = {
type: REQUEST_TYPE.ONBOARDING,
newEndsOn: Date.now() + convertDaysToMilliseconds(3),
reason: "<dummy-reason>"
}
let latestValidExtension: OnboardingExtension;
let userId: string;
let invalidUserId: string;
let superUserId: string;
let patchEndpoint: string;
let authToken: string;
let latestApprovedExtension: OnboardingExtension;
let latestInvalidExtension: OnboardingExtension;
let oooRequest: OooStatusRequest;

beforeEach(async () => {
userId = await addUser(userData[6]);
invalidUserId = await addUser(userData[0]);
superUserId = await addUser(userData[4]);
latestInvalidExtension = await requestsQuery.createRequest({
state: REQUEST_STATE.PENDING,
type: REQUEST_TYPE.ONBOARDING,
oldEndsOn: Date.now() + convertDaysToMilliseconds(5),
userId: userId,
});
latestValidExtension = await requestsQuery.createRequest({
state: REQUEST_STATE.PENDING,
type: REQUEST_TYPE.ONBOARDING,
oldEndsOn: Date.now() - convertDaysToMilliseconds(3),
userId: userId
});
latestApprovedExtension = await requestsQuery.createRequest({
state: REQUEST_STATE.APPROVED,
type: REQUEST_TYPE.ONBOARDING,
oldEndsOn: Date.now(),
userId: userId
});
oooRequest = await requestsQuery.createRequest({type: REQUEST_TYPE.OOO, userId: userId});
patchEndpoint = `/requests/${latestValidExtension.id}?dev=true`;
authToken = generateAuthToken({userId});
})

afterEach(async () => {
sinon.restore();
await cleanDb();
})

it("should return 400 response for incorrect type", (done) => {
chai.request(app)
.patch(patchEndpoint)
.set("authorization", `Bearer ${authToken}`)
.send({...body, type: "<invalid-type>"})
.end((err, res) => {
if(err) return done(err);
expect(res.statusCode).to.equal(400);
expect(res.body.error).to.equal("Bad Request");
expect(res.body.message).to.equal("Invalid type");
done();
})
})

it("should return Feature not implemented when dev is not true", (done) => {
chai.request(app)
.patch(`/requests/1111?dev=false`)
.send(body)
.set("authorization", `Bearer ${authToken}`)
.end((err, res)=>{
if (err) return done(err);
expect(res.statusCode).to.equal(501);
expect(res.body.message).to.equal("Feature not implemented");
done();
})
})

it("should return Unauthenticated User when authorization header is missing", (done) => {
chai
.request(app)
.patch(patchEndpoint)
.set("authorization", "")
.send(body)
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(401);
expect(res.body.message).to.equal("Unauthenticated User");
done();
})
})

it("should return Unauthenticated User for invalid token", (done) => {
chai.request(app)
.patch(patchEndpoint)
.set("authorization", `Bearer ${BAD_TOKEN}`)
.send(body)
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(401);
expect(res.body.message).to.equal("Unauthenticated User");
done();
})
})

it("should return 400 response for invalid value of newEndsOn", (done) => {
chai.request(app)
.patch(patchEndpoint)
.set("authorization", `Bearer ${authToken}`)
.send({...body, newEndsOn: Date.now()})
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(400);
expect(res.body.error).to.equal("Bad Request");
expect(res.body.message).contain(`"newEndsOn" must be greater than or equal to`)
done();
})
})

it("should return 404 response for invalid extension id", (done) => {
chai.request(app)
.patch(`/requests/1111?dev=true`)
.set("authorization", `Bearer ${authToken}`)
.send(body)
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(404);
expect(res.body.message).to.equal(REQUEST_DOES_NOT_EXIST);
expect(res.body.error).to.equal("Not Found");
done();
})
})

it("should return 403 response when super user and request owner are not updating the request", (done) => {
chai.request(app)
.patch(patchEndpoint)
.set("authorization", `Bearer ${generateAuthToken({userId: invalidUserId})}`)
.send(body)
.end((err, res)=>{
if(err) return done(err);
expect(res.statusCode).to.equal(403);
expect(res.body.error).to.equal("Forbidden");
expect(res.body.message).to.equal(UNAUTHORIZED_TO_UPDATE_REQUEST);
done();
})
})

it("should return 400 response when request type is not onboarding", (done) => {
chai.request(app)
.patch(`/requests/${oooRequest.id}?dev=true`)
.set("authorization", `Bearer ${authToken}`)
.send(body)
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(400);
expect(res.body.message).to.equal(INVALID_REQUEST_TYPE);
expect(res.body.error).to.equal("Bad Request");
done();
})
})

it("should return 400 response when extension state is not pending", (done) => {
chai.request(app)
.patch(`/requests/${latestApprovedExtension.id}?dev=true`)
.set("authorization", `Bearer ${authToken}`)
.send(body)
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(400);
expect(res.body.message).to.equal(PENDING_REQUEST_UPDATED);
expect(res.body.error).to.equal("Bad Request");
done();
})
})

it("should return 400 response when old dealdine is greater than new deadline", (done) => {
chai.request(app)
.patch(`/requests/${latestInvalidExtension.id}?dev=true`)
.set("authorization", `Bearer ${authToken}`)
.send(body)
.end((err, res) => {
if (err) return done(err);
expect(res.statusCode).to.equal(400);
expect(res.body.message).to.equal(INVALID_REQUEST_DEADLINE);
expect(res.body.error).to.equal("Bad Request");
done();
})
})

it("should return 200 success response when request owner is updating the request", (done) => {
chai.request(app)
.patch(patchEndpoint)
.set("authorization", `Bearer ${authToken}`)
.send(body)
.end((err, res)=>{
if(err) return done(err);
expect(res.statusCode).to.equal(200);
expect(res.body.message).to.equal(REQUEST_UPDATED_SUCCESSFULLY);
expect(res.body.data.id).to.equal(latestValidExtension.id);
expect(res.body.data.newEndsOn).to.equal(body.newEndsOn)
done();
})
})

it("should return 200 success response when super user is updating the request", (done) => {
chai.request(app)
.patch(patchEndpoint)
.set("authorization", `Bearer ${generateAuthToken({userId: superUserId})}`)
.send(body)
.end((err, res)=>{
if(err) return done(err);
expect(res.statusCode).to.equal(200);
expect(res.body.message).to.equal(REQUEST_UPDATED_SUCCESSFULLY);
expect(res.body.data.id).to.equal(latestValidExtension.id);
expect(res.body.data.newEndsOn).to.equal(body.newEndsOn)
done();
})
})


it("should return 500 response for unexpected error", (done) => {
sinon.stub(logUtils, "addLog").throws("Error")
chai.request(app)
.patch(patchEndpoint)
.send(body)
.set("authorization", `Bearer ${authToken}`)
.end((err, res)=>{
if(err) return done(err);
expect(res.statusCode).to.equal(500);
expect(res.body.error).to.equal("Internal Server Error");
done();
})
})
})
});
78 changes: 78 additions & 0 deletions test/unit/middlewares/requests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createRequestsMiddleware,
getRequestsMiddleware,
updateRequestsMiddleware,
updateRequestValidator,

Check failure on line 9 in test/unit/middlewares/requests.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

Module '"../../../middlewares/validators/requests"' has no exported member 'updateRequestValidator'.
} from "../../../middlewares/validators/requests";
import {
validOooStatusRequests,
Expand All @@ -14,6 +15,9 @@ import {
invalidOooStatusUpdate,
} from "../../fixtures/oooRequest/oooRequest";
import { OooRequestCreateRequest, OooRequestResponse } from "../../../types/oooRequest";
import { REQUEST_TYPE } from "../../../constants/requests";
import { convertDaysToMilliseconds } from "../../../utils/time";
import { updateOnboardingExtensionRequestValidator } from "../../../middlewares/validators/onboardingExtensionRequest";

Check failure on line 20 in test/unit/middlewares/requests.test.ts

View workflow job for this annotation

GitHub Actions / build (20.11.x)

'"../../../middlewares/validators/onboardingExtensionRequest"' has no exported member named 'updateOnboardingExtensionRequestValidator'. Did you mean 'createOnboardingExtensionRequestValidator'?

describe("Create Request Validators", function () {
let req: any;
Expand Down Expand Up @@ -110,3 +114,77 @@ describe("Create Request Validators", function () {
});
});
});

describe("updateRequestValidator", () => {
let req, res, next: sinon.SinonSpy;

beforeEach(() => {
next = sinon.spy();
res = { boom: { badRequest: sinon.spy() } }
});

afterEach(() => {
sinon.restore();
})

it("should call next for correct type", async () => {
req = { body: { type: REQUEST_TYPE.ONBOARDING, newEndsOn: Date.now() + convertDaysToMilliseconds(2) } };
await updateRequestValidator(req, res, next);
expect(next.calledOnce).to.be.true;
})

it("should not call next for incorrect type", async () => {
req = { body: { type: REQUEST_TYPE.OOO } };
await updateRequestValidator(req, res, next);
expect(next.notCalled).to.be.true;
})
})

describe("updateOnboardingExtensionRequestValidator", () => {
let req, res, next: sinon.SinonSpy;

beforeEach(() => {
next = sinon.spy();
res = { boom: { badRequest: sinon.spy() } };
});

afterEach(() => {
sinon.restore();
})

it("should not call next for incorrect type ", async () => {
req = {
body: {
type: REQUEST_TYPE.OOO,
newEndsOn: Date.now() + convertDaysToMilliseconds(3)
}
}

await updateOnboardingExtensionRequestValidator(req, res, next);
expect(next.notCalled).to.be.true;
});

it("should not call next for incorrect newEndsOn ", async () => {
req = {
body: {
type: REQUEST_TYPE.ONBOARDING,
newEndsOn: Date.now() - convertDaysToMilliseconds(1)
}
}

await updateOnboardingExtensionRequestValidator(req, res, next);
expect(next.notCalled).to.be.true;
});

it("should call next for successful validaton", async () => {
req = {
body: {
type: REQUEST_TYPE.ONBOARDING,
newEndsOn: Date.now() + convertDaysToMilliseconds(3)
}
}

await updateOnboardingExtensionRequestValidator(req, res, next);
expect(next.calledOnce).to.be.true;
});
})

0 comments on commit 0abdafd

Please sign in to comment.