Skip to content

Commit a9bdb4d

Browse files
test: added test for lazy load discord groups page (#2310)
* 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]>
1 parent 9a75c3c commit a9bdb4d

File tree

3 files changed

+233
-10
lines changed

3 files changed

+233
-10
lines changed

test/integration/discordactions.test.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,4 +1218,133 @@ describe("Discord actions", function () {
12181218
});
12191219
});
12201220
});
1221+
1222+
describe("GET /discord-actions/groups (getPaginatedAllGroupRoles)", function () {
1223+
let userId;
1224+
let userAuthToken;
1225+
1226+
beforeEach(async function () {
1227+
const user = await addUser(userData[0]);
1228+
userId = user;
1229+
userAuthToken = authService.generateAuthToken({ userId });
1230+
1231+
await discordRoleModel.add(groupData[0]);
1232+
await discordRoleModel.add(groupData[1]);
1233+
});
1234+
1235+
afterEach(async function () {
1236+
sinon.restore();
1237+
await cleanDb();
1238+
});
1239+
1240+
it("should return paginated results when dev=true is passed", function (done) {
1241+
chai
1242+
.request(app)
1243+
.get("/discord-actions/groups?dev=true&page=1&size=10")
1244+
.set("cookie", `${cookieName}=${userAuthToken}`)
1245+
.end((err, res) => {
1246+
if (err) {
1247+
return done(err);
1248+
}
1249+
1250+
expect(res).to.have.status(200);
1251+
expect(res.body).to.be.an("object");
1252+
expect(res.body.message).to.equal("Roles fetched successfully!");
1253+
expect(res.body.groups).to.be.an("array");
1254+
1255+
const groups = res.body.groups;
1256+
groups.forEach((group) => {
1257+
expect(group).to.have.keys([
1258+
"roleid",
1259+
"rolename",
1260+
"memberCount",
1261+
"firstName",
1262+
"lastName",
1263+
"image",
1264+
"isMember",
1265+
]);
1266+
});
1267+
1268+
expect(res.body.links).to.have.keys(["next", "prev"]);
1269+
return done();
1270+
});
1271+
});
1272+
1273+
it("should return null for next link on the last page", function (done) {
1274+
const size = 10;
1275+
const page = 2;
1276+
1277+
chai
1278+
.request(app)
1279+
.get(`/discord-actions/groups?dev=true&page=${page}&size=${size}`)
1280+
.set("cookie", `${cookieName}=${userAuthToken}`)
1281+
.end((err, res) => {
1282+
if (err) {
1283+
return done(err);
1284+
}
1285+
1286+
expect(res).to.have.status(200);
1287+
expect(res.body).to.be.an("object");
1288+
expect(res.body.message).to.equal("Roles fetched successfully!");
1289+
expect(res.body.groups).to.be.an("array");
1290+
expect(res.body.links).to.have.keys(["next", "prev"]);
1291+
// eslint-disable-next-line no-unused-expressions
1292+
expect(res.body.links.next).to.be.null;
1293+
expect(res.body.links.prev).to.equal(`/discord-actions/groups?page=${page - 1}&size=${size}&dev=true`);
1294+
return done();
1295+
});
1296+
});
1297+
1298+
it("should return a bad request error for invalid size parameter", function (done) {
1299+
chai
1300+
.request(app)
1301+
.get("/discord-actions/groups?dev=true&size=101&page=1")
1302+
.set("cookie", `${cookieName}=${userAuthToken}`)
1303+
.end((_err, res) => {
1304+
expect(res).to.have.status(400);
1305+
expect(res.body).to.be.an("object");
1306+
expect(res.body.message).to.equal('"size" must be less than or equal to 100');
1307+
return done();
1308+
});
1309+
});
1310+
1311+
it("should return an empty array for groups on a page with no data", function (done) {
1312+
const size = 10;
1313+
const page = 100;
1314+
1315+
chai
1316+
.request(app)
1317+
.get(`/discord-actions/groups?dev=true&page=${page}&size=${size}`)
1318+
.set("cookie", `${cookieName}=${userAuthToken}`)
1319+
.end((_err, res) => {
1320+
expect(res).to.have.status(200);
1321+
expect(res.body).to.be.an("object");
1322+
expect(res.body.message).to.equal("Roles fetched successfully!");
1323+
// eslint-disable-next-line no-unused-expressions
1324+
expect(res.body.groups).to.be.an("array").that.is.empty;
1325+
expect(res.body.links).to.have.keys(["next", "prev"]);
1326+
// eslint-disable-next-line no-unused-expressions
1327+
expect(res.body.links.next).to.be.null;
1328+
expect(res.body.links.prev).to.equal(`/discord-actions/groups?page=${page - 1}&size=${size}&dev=true`);
1329+
return done();
1330+
});
1331+
});
1332+
1333+
it("should handle internal server errors", function (done) {
1334+
sinon.stub(discordRolesModel, "getPaginatedGroupRolesByPage").throws(new Error("Database error"));
1335+
1336+
chai
1337+
.request(app)
1338+
.get("/discord-actions/groups?dev=true")
1339+
.set("cookie", `${cookieName}=${userAuthToken}`)
1340+
// eslint-disable-next-line node/handle-callback-err
1341+
.end((err, res) => {
1342+
expect(res).to.have.status(500);
1343+
expect(res.body).to.be.an("object");
1344+
expect(res.body.message).to.equal("An internal server error occurred");
1345+
sinon.restore();
1346+
return done();
1347+
});
1348+
});
1349+
});
12211350
});

test/unit/middlewares/discordactions-validators.test.js

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const {
33
validateGroupRoleBody,
44
validateMemberRoleBody,
55
validateUpdateUsersNicknameStatusBody,
6+
validateLazyLoadingParams,
67
} = require("../../../middlewares/validators/discordactions");
78
const { expect } = require("chai");
89

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

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

54-
it("stops the propogation to the next function", async function () {
55+
it("stops the propagation to the next function", async function () {
5556
const res = {
5657
boom: {
5758
badRequest: () => {},
@@ -108,23 +109,72 @@ describe("Middleware | Validators | discord actions", function () {
108109
});
109110
expect(nextSpy.callCount).to.be.equal(0);
110111
});
112+
});
113+
114+
describe("validateLazyLoadingParams", function () {
115+
it("should pass the request to the next function when valid params are provided", async function () {
116+
const req = {
117+
query: {
118+
page: 1,
119+
size: 10,
120+
dev: "true",
121+
},
122+
};
123+
const nextSpy = Sinon.spy();
124+
const res = {};
125+
await validateLazyLoadingParams(req, res, nextSpy);
126+
expect(nextSpy.calledOnce).to.be.equal(true);
127+
});
111128

112-
it("should throw error when the lastNicknameUpdate timestamp is not a string or timestamp", async function () {
129+
it("should return a bad request error when size is out of range", async function () {
113130
const res = {
114131
boom: {
115-
badRequest: () => {},
132+
badRequest: Sinon.spy(),
133+
},
134+
};
135+
const req = {
136+
query: {
137+
size: 200,
116138
},
117139
};
118140
const nextSpy = Sinon.spy();
141+
await validateLazyLoadingParams(req, res, nextSpy);
142+
expect(nextSpy.called).to.be.equal(false);
143+
expect(res.boom.badRequest.calledOnce).to.be.equal(true);
144+
});
145+
146+
it("should return a bad request error when page is negative", async function () {
147+
const res = {
148+
boom: {
149+
badRequest: Sinon.spy(),
150+
},
151+
};
119152
const req = {
120-
body: {
121-
lastNicknameUpdate: 112.45478,
153+
query: {
154+
page: -1,
122155
},
123156
};
124-
await validateMemberRoleBody(req, res, nextSpy).catch((err) => {
125-
expect(err).to.be.an.instanceOf(Error);
126-
});
127-
expect(nextSpy.callCount).to.be.equal(0);
157+
const nextSpy = Sinon.spy();
158+
await validateLazyLoadingParams(req, res, nextSpy);
159+
expect(nextSpy.called).to.be.equal(false);
160+
expect(res.boom.badRequest.calledOnce).to.be.equal(true);
161+
});
162+
163+
it("should return a bad request error when dev has an invalid value", async function () {
164+
const res = {
165+
boom: {
166+
badRequest: Sinon.spy(),
167+
},
168+
};
169+
const req = {
170+
query: {
171+
dev: "invalid",
172+
},
173+
};
174+
const nextSpy = Sinon.spy();
175+
await validateLazyLoadingParams(req, res, nextSpy);
176+
expect(nextSpy.called).to.be.equal(false);
177+
expect(res.boom.badRequest.calledOnce).to.be.equal(true);
128178
});
129179
});
130180
});

test/unit/models/discordactions.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const tasksModel = firestore.collection("tasks");
2020
const {
2121
createNewRole,
2222
getAllGroupRoles,
23+
getPaginatedGroupRolesByPage,
2324
isGroupRoleExists,
2425
addGroupRoleToMember,
2526
deleteRoleFromDatabase,
@@ -1299,4 +1300,47 @@ describe("discordactions", function () {
12991300
}
13001301
});
13011302
});
1303+
1304+
describe("getPaginatedGroupRolesByPage", function () {
1305+
let orderByStub, offsetStub, limitStub, getStub;
1306+
1307+
beforeEach(function () {
1308+
orderByStub = sinon.stub();
1309+
offsetStub = sinon.stub();
1310+
limitStub = sinon.stub();
1311+
getStub = sinon.stub();
1312+
1313+
orderByStub.returns({ offset: offsetStub });
1314+
offsetStub.returns({ limit: limitStub });
1315+
limitStub.returns({ get: getStub });
1316+
1317+
sinon.stub(discordRoleModel, "orderBy").returns(orderByStub);
1318+
});
1319+
1320+
afterEach(function () {
1321+
sinon.restore();
1322+
});
1323+
1324+
it("should return an empty array if no roles are found", async function () {
1325+
getStub.resolves({ docs: [] });
1326+
1327+
const result = await getPaginatedGroupRolesByPage({ offset: 0, limit: 10 });
1328+
1329+
expect(result).to.deep.equal({
1330+
roles: [],
1331+
total: 0,
1332+
});
1333+
});
1334+
1335+
it("should throw an error if a database error occurs", async function () {
1336+
getStub.rejects(new Error("Database error"));
1337+
1338+
try {
1339+
await getPaginatedGroupRolesByPage({ offset: 0, limit: 10 });
1340+
} catch (err) {
1341+
expect(err).to.be.instanceOf(Error);
1342+
expect(err.message).to.equal("Database error while paginating group roles");
1343+
}
1344+
});
1345+
});
13021346
});

0 commit comments

Comments
 (0)