Skip to content

Commit

Permalink
test: added test for lazy load discord groups page (#2310)
Browse files Browse the repository at this point in the history
* feat: added pagination for lazy loading in the /groups route to load the discord groups asynchrounously instead of all at once

* feat: Implement pagination for lazy loading in

- Added feature-flag-based () lazy loading to .
- Introduced support for , , and __TEXT	__DATA	__OBJC	others	dec	hex query parameters for pagination.
- Validated query parameters and returned structured pagination metadata (, __TEXT	__DATA	__OBJC	others	dec	hex, , ).
- Updated response structure to include enriched group membership information.
- Modified test cases in :
  - Added tests for cursor-based lazy loading behavior.
  - Handled scenarios with and without cursors.
  - Verified error handling for database query failures.

* fix: test cases

* fix: correct paginated group roles endpoint and dynamic link generation

- Fixed incorrect  and  link generation in the  endpoint.
  - Ensured  dynamically includes the correct path ().
  - Dynamically constructed  and  links using  and .

- Refactored  function:
  - Simplified conditional logic for  feature flag.
  - Added error handling for invalid page and size parameters.

- Removed hardcoding of URLs in response links to make them adaptive to the environment.

- Improved logging for easier debugging when issues arise.

- Added handling to ensure old behavior (non-dev mode) works as expected.

* test: added test for the validateLazyLoadingParams

* chore remove test from here as moving tests in a different PR

* fix: failing tests

* test: added test for getPaginatedGroupRolesByPage model functions

* test: add tests for getPaginatedAllGroupRoles function in the controller

* chore: remove comments from the code

---------

Co-authored-by: Vikas Singh <[email protected]>
  • Loading branch information
Achintya-Chatterjee and vikasosmium authored Dec 24, 2024
1 parent 9a75c3c commit a9bdb4d
Show file tree
Hide file tree
Showing 3 changed files with 233 additions and 10 deletions.
129 changes: 129 additions & 0 deletions test/integration/discordactions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1218,4 +1218,133 @@ describe("Discord actions", function () {
});
});
});

describe("GET /discord-actions/groups (getPaginatedAllGroupRoles)", function () {
let userId;
let userAuthToken;

beforeEach(async function () {
const user = await addUser(userData[0]);
userId = user;
userAuthToken = authService.generateAuthToken({ userId });

await discordRoleModel.add(groupData[0]);
await discordRoleModel.add(groupData[1]);
});

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

it("should return paginated results when dev=true is passed", function (done) {
chai
.request(app)
.get("/discord-actions/groups?dev=true&page=1&size=10")
.set("cookie", `${cookieName}=${userAuthToken}`)
.end((err, res) => {
if (err) {
return done(err);
}

expect(res).to.have.status(200);
expect(res.body).to.be.an("object");
expect(res.body.message).to.equal("Roles fetched successfully!");
expect(res.body.groups).to.be.an("array");

const groups = res.body.groups;
groups.forEach((group) => {
expect(group).to.have.keys([
"roleid",
"rolename",
"memberCount",
"firstName",
"lastName",
"image",
"isMember",
]);
});

expect(res.body.links).to.have.keys(["next", "prev"]);
return done();
});
});

it("should return null for next link on the last page", function (done) {
const size = 10;
const page = 2;

chai
.request(app)
.get(`/discord-actions/groups?dev=true&page=${page}&size=${size}`)
.set("cookie", `${cookieName}=${userAuthToken}`)
.end((err, res) => {
if (err) {
return done(err);
}

expect(res).to.have.status(200);
expect(res.body).to.be.an("object");
expect(res.body.message).to.equal("Roles fetched successfully!");
expect(res.body.groups).to.be.an("array");
expect(res.body.links).to.have.keys(["next", "prev"]);
// eslint-disable-next-line no-unused-expressions
expect(res.body.links.next).to.be.null;
expect(res.body.links.prev).to.equal(`/discord-actions/groups?page=${page - 1}&size=${size}&dev=true`);
return done();
});
});

it("should return a bad request error for invalid size parameter", function (done) {
chai
.request(app)
.get("/discord-actions/groups?dev=true&size=101&page=1")
.set("cookie", `${cookieName}=${userAuthToken}`)
.end((_err, res) => {
expect(res).to.have.status(400);
expect(res.body).to.be.an("object");
expect(res.body.message).to.equal('"size" must be less than or equal to 100');
return done();
});
});

it("should return an empty array for groups on a page with no data", function (done) {
const size = 10;
const page = 100;

chai
.request(app)
.get(`/discord-actions/groups?dev=true&page=${page}&size=${size}`)
.set("cookie", `${cookieName}=${userAuthToken}`)
.end((_err, res) => {
expect(res).to.have.status(200);
expect(res.body).to.be.an("object");
expect(res.body.message).to.equal("Roles fetched successfully!");
// eslint-disable-next-line no-unused-expressions
expect(res.body.groups).to.be.an("array").that.is.empty;
expect(res.body.links).to.have.keys(["next", "prev"]);
// eslint-disable-next-line no-unused-expressions
expect(res.body.links.next).to.be.null;
expect(res.body.links.prev).to.equal(`/discord-actions/groups?page=${page - 1}&size=${size}&dev=true`);
return done();
});
});

it("should handle internal server errors", function (done) {
sinon.stub(discordRolesModel, "getPaginatedGroupRolesByPage").throws(new Error("Database error"));

chai
.request(app)
.get("/discord-actions/groups?dev=true")
.set("cookie", `${cookieName}=${userAuthToken}`)
// eslint-disable-next-line node/handle-callback-err
.end((err, res) => {
expect(res).to.have.status(500);
expect(res.body).to.be.an("object");
expect(res.body.message).to.equal("An internal server error occurred");
sinon.restore();
return done();
});
});
});
});
70 changes: 60 additions & 10 deletions test/unit/middlewares/discordactions-validators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {
validateGroupRoleBody,
validateMemberRoleBody,
validateUpdateUsersNicknameStatusBody,
validateLazyLoadingParams,
} = require("../../../middlewares/validators/discordactions");
const { expect } = require("chai");

Expand All @@ -20,7 +21,7 @@ describe("Middleware | Validators | discord actions", function () {
expect(nextSpy.calledOnce).to.be.equal(true);
});

it("stops the propogation of the event to next function", async function () {
it("stops the propagation of the event to next function", async function () {
const res = {
boom: {
badRequest: () => {},
Expand Down Expand Up @@ -51,7 +52,7 @@ describe("Middleware | Validators | discord actions", function () {
expect(nextSpy.calledOnce).to.be.equal(true);
});

it("stops the propogation to the next function", async function () {
it("stops the propagation to the next function", async function () {
const res = {
boom: {
badRequest: () => {},
Expand Down Expand Up @@ -108,23 +109,72 @@ describe("Middleware | Validators | discord actions", function () {
});
expect(nextSpy.callCount).to.be.equal(0);
});
});

describe("validateLazyLoadingParams", function () {
it("should pass the request to the next function when valid params are provided", async function () {
const req = {
query: {
page: 1,
size: 10,
dev: "true",
},
};
const nextSpy = Sinon.spy();
const res = {};
await validateLazyLoadingParams(req, res, nextSpy);
expect(nextSpy.calledOnce).to.be.equal(true);
});

it("should throw error when the lastNicknameUpdate timestamp is not a string or timestamp", async function () {
it("should return a bad request error when size is out of range", async function () {
const res = {
boom: {
badRequest: () => {},
badRequest: Sinon.spy(),
},
};
const req = {
query: {
size: 200,
},
};
const nextSpy = Sinon.spy();
await validateLazyLoadingParams(req, res, nextSpy);
expect(nextSpy.called).to.be.equal(false);
expect(res.boom.badRequest.calledOnce).to.be.equal(true);
});

it("should return a bad request error when page is negative", async function () {
const res = {
boom: {
badRequest: Sinon.spy(),
},
};
const req = {
body: {
lastNicknameUpdate: 112.45478,
query: {
page: -1,
},
};
await validateMemberRoleBody(req, res, nextSpy).catch((err) => {
expect(err).to.be.an.instanceOf(Error);
});
expect(nextSpy.callCount).to.be.equal(0);
const nextSpy = Sinon.spy();
await validateLazyLoadingParams(req, res, nextSpy);
expect(nextSpy.called).to.be.equal(false);
expect(res.boom.badRequest.calledOnce).to.be.equal(true);
});

it("should return a bad request error when dev has an invalid value", async function () {
const res = {
boom: {
badRequest: Sinon.spy(),
},
};
const req = {
query: {
dev: "invalid",
},
};
const nextSpy = Sinon.spy();
await validateLazyLoadingParams(req, res, nextSpy);
expect(nextSpy.called).to.be.equal(false);
expect(res.boom.badRequest.calledOnce).to.be.equal(true);
});
});
});
44 changes: 44 additions & 0 deletions test/unit/models/discordactions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const tasksModel = firestore.collection("tasks");
const {
createNewRole,
getAllGroupRoles,
getPaginatedGroupRolesByPage,
isGroupRoleExists,
addGroupRoleToMember,
deleteRoleFromDatabase,
Expand Down Expand Up @@ -1299,4 +1300,47 @@ describe("discordactions", function () {
}
});
});

describe("getPaginatedGroupRolesByPage", function () {
let orderByStub, offsetStub, limitStub, getStub;

beforeEach(function () {
orderByStub = sinon.stub();
offsetStub = sinon.stub();
limitStub = sinon.stub();
getStub = sinon.stub();

orderByStub.returns({ offset: offsetStub });
offsetStub.returns({ limit: limitStub });
limitStub.returns({ get: getStub });

sinon.stub(discordRoleModel, "orderBy").returns(orderByStub);
});

afterEach(function () {
sinon.restore();
});

it("should return an empty array if no roles are found", async function () {
getStub.resolves({ docs: [] });

const result = await getPaginatedGroupRolesByPage({ offset: 0, limit: 10 });

expect(result).to.deep.equal({
roles: [],
total: 0,
});
});

it("should throw an error if a database error occurs", async function () {
getStub.rejects(new Error("Database error"));

try {
await getPaginatedGroupRolesByPage({ offset: 0, limit: 10 });
} catch (err) {
expect(err).to.be.instanceOf(Error);
expect(err.message).to.equal("Database error while paginating group roles");
}
});
});
});

0 comments on commit a9bdb4d

Please sign in to comment.