Skip to content

Commit d0e3446

Browse files
authored
Merge pull request #25 from VehvilainenPooki/main
Update to guide channel and Course creation
2 parents dabc791 + bee45f8 commit d0e3446

16 files changed

+345
-140
lines changed

__tests__/jest/guide.test.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
const { updateGuideMessage } = require("../../src/discordBot/services/guide");
2+
3+
jest.mock("../../src/db/services/courseService", () => ({
4+
findCoursesFromDb: jest.fn(),
5+
}));
6+
jest.mock("../../src/db/services/courseMemberService", () => ({
7+
findCourseMemberCount: jest.fn(),
8+
}));
9+
10+
const { findCoursesFromDb } = require("../../src/db/services/courseService");
11+
const { findCourseMemberCount } = require("../../src/db/services/courseMemberService");
12+
13+
const createMockMessage = (id, content = "old content") => ({
14+
id,
15+
type: "DEFAULT",
16+
content,
17+
edit: jest.fn(),
18+
delete: jest.fn(),
19+
});
20+
21+
const setupMocks = (courseCount = 2, extraMessages = 1) => {
22+
const courses = Array.from({ length: courseCount }, (_, i) => ({
23+
id: i + 1,
24+
code: `TKT10${i + 1}`,
25+
fullName: `Course ${i + 1}`,
26+
name: `tkt10${i + 1}`,
27+
}));
28+
findCoursesFromDb.mockResolvedValue(courses);
29+
for (let i = 0; i < courseCount; i++) {
30+
findCourseMemberCount.mockResolvedValueOnce((i + 1) * 5);
31+
}
32+
33+
const infoMessage = { id: "info", edit: jest.fn() };
34+
let courseMessages = Array.from({ length: courseCount }, (_, i) =>
35+
createMockMessage(`msg${i + 1}`)
36+
);
37+
let extras = [];
38+
39+
if (extraMessages >= 0) {
40+
extras = Array.from({ length: extraMessages }, (_, i) =>
41+
createMockMessage(`extra${i + 1}`, "extra")
42+
);
43+
} else {
44+
const removeCount = Math.abs(extraMessages);
45+
if (removeCount > courseMessages.length) {
46+
throw new Error("extraMessages is too negative, cannot remove more course messages than exist.");
47+
}
48+
courseMessages = courseMessages.slice(0, courseMessages.length - removeCount);
49+
}
50+
51+
const sortedMessages = new Map([
52+
["info", infoMessage],
53+
...courseMessages.map((m) => [m.id, m]),
54+
...extras.map((m) => [m.id, m]),
55+
]);
56+
57+
const channel = {
58+
send: jest.fn((content) =>
59+
Promise.resolve({
60+
react: jest.fn(() => Promise.resolve()),
61+
})
62+
),
63+
};
64+
65+
return { infoMessage, courseMessages, extras, sortedMessages, channel, courses };
66+
};
67+
68+
describe("updateGuideMessage", () => {
69+
beforeEach(() => {
70+
jest.clearAllMocks();
71+
});
72+
73+
test("edits info message, edits existing course messages, deletes extras", async () => {
74+
const { infoMessage, courseMessages, extras, sortedMessages, channel } = setupMocks(5, 1);
75+
76+
await updateGuideMessage(infoMessage, sortedMessages, channel, { Course: {}, CourseMember: {} });
77+
78+
expect(infoMessage.edit).toHaveBeenCalledTimes(1);
79+
courseMessages.forEach(msg => expect(msg.edit).toHaveBeenCalledTimes(1));
80+
expect(channel.send).not.toHaveBeenCalled();
81+
extras.forEach(msg => expect(msg.delete).toHaveBeenCalledTimes(1));
82+
});
83+
84+
test("sends new course messages when there are too few messages", async () => {
85+
const { infoMessage, courseMessages, sortedMessages, channel } = setupMocks(5, -1);
86+
87+
await updateGuideMessage(infoMessage, sortedMessages, channel, { Course: {}, CourseMember: {} });
88+
89+
expect(infoMessage.edit).toHaveBeenCalledTimes(1);
90+
expect(courseMessages[0].edit).toHaveBeenCalledTimes(1);
91+
expect(channel.send).toHaveBeenCalledTimes(1);
92+
});
93+
});

__tests__/jest/initialize.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { client } = require("../mocks/mockClient");
55

66
jest.mock("../../src/db/services/courseService");
77
jest.mock("../../src/discordBot/services/service");
8+
jest.mock("../../src/discordBot/services/guide");
89
jest.mock("../../src/db/hookInit");
910

1011
initHooks.mockImplementation(() => true);

__tests__/jest/service.test.js

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,10 @@ const {
99
findOrCreateChannel,
1010
getCourseNameFromCategory,
1111
findChannelWithNameAndType,
12-
getWorkshopInfo,
13-
updateGuideMessage } = require("../../src/discordBot/services/service");
12+
getWorkshopInfo } = require("../../src/discordBot/services/service");
1413
const { createCourseToDatabase, removeCourseFromDb } = require("../../src/db/services/courseService");
1514
const { data } = require("../mocks/workshopData.json");
1615

17-
const createGuidePinnedMessage = async () => {
18-
const rows = courses
19-
.map((course) => {
20-
const code = course.code;
21-
const fullname = course.fullName;
22-
const count = 1;
23-
return ` - ${code} - ${fullname} 👤${count}`;
24-
});
25-
26-
let invite_url = "";
27-
process.env.NODE_ENV === "production" ? invite_url = `${process.env.BACKEND_SERVER_URL}` : invite_url = `${process.env.BACKEND_SERVER_URL}:${process.env.PORT}`;
28-
29-
const newContent = `
30-
Käytössäsi on seuraavia komentoja:
31-
- \`/join\` jolla voit liittyä kurssille
32-
- \`/leave\` jolla voit poistua kurssilta
33-
Kirjoittamalla \`/join\` tai \`/leave\` botti antaa listan kursseista.
34-
35-
You have the following commands available:
36-
- \`/join\` which you can use to join a course
37-
- \`/leave\` which you can use to leave a course
38-
The bot gives a list of the courses if you type \`/join\` or \`/leave\`.
39-
40-
Kurssit / Courses:
41-
${rows.join("\n")}
42-
43-
In course specific channels you can also list instructors with the command \`/instructors\`
44-
45-
See more with \`/help\` command.
46-
47-
Invitation link for the server ${invite_url}
48-
`;
49-
return newContent;
50-
};
51-
5216
const courses = [{ code: "tkt", fullName: "test course", name: "test" }];
5317

5418
const Course = {
@@ -61,23 +25,6 @@ const Course = {
6125
destroy: jest.fn(),
6226
};
6327

64-
const CourseMember = {
65-
create: jest.fn(),
66-
findOne: jest
67-
.fn(() => true)
68-
.mockImplementationOnce(() => false)
69-
.mockImplementationOnce(() => false),
70-
findAll: jest.fn(() => null),
71-
count: jest
72-
.fn()
73-
.mockImplementationOnce(() => 1),
74-
destroy: jest.fn(),
75-
};
76-
77-
const models = {
78-
Course, CourseMember,
79-
};
80-
8128
const { client } = require("../mocks/mockSlashClient");
8229

8330
afterEach(() => {
@@ -133,22 +80,6 @@ describe("Service", () => {
13380
expect(channelFound).toMatchObject(channel);
13481
});
13582

136-
test("Update guide message", async () => {
137-
const role = { name: "test", members: [] };
138-
const guide = { id: 1, name: "guide", type: "GUILD_TEXT", send: jest.fn(() => msg) };
139-
const commands = { id: 2, name: "commands", type: "GUILD_TEXT", send: jest.fn(() => msg) };
140-
const testCategory = { id: 3, name: "📚 test", type: "GUILD_CATEGORY", members: {} };
141-
client.guild.invites.cache.push({ channel: { name: "guide", code: 1 } });
142-
client.guild.channels.cache = [guide, commands, testCategory];
143-
client.guild.roles.cache = [role];
144-
const msg = { guild: client.guild, pin: jest.fn(), edit: jest.fn() };
145-
const guideMessage = await createGuidePinnedMessage(client.guild, Course);
146-
await updateGuideMessage(msg, models);
147-
expect(msg.edit).toHaveBeenCalledTimes(1);
148-
expect(msg.edit).toHaveBeenCalledWith(guideMessage);
149-
client.guild.channels.cache = [];
150-
});
151-
15283
test("creating guide invitation call createInvite", async () => {
15384
const msg = { pin: jest.fn() };
15485
const invite = { code: 1 };

src/db/hooks/channelHooks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ const {
77
getCategoryObject,
88
getCategoryChannelPermissionOverwrites,
99
createInvitation,
10-
setCoursePositionABC,
11-
updateGuide } = require("../../discordBot/services/service");
10+
setCoursePositionABC } = require("../../discordBot/services/service");
11+
const { updateGuide } = require("../../discordBot/services/guide");
1212
const { findCourseFromDbById } = require("../services/courseService");
1313
const { courseAdminRole } = require("../../../config.json");
1414
const { Op } = require("sequelize");

src/db/hooks/courseHooks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ const {
1111
setEmojisUnlock,
1212
setEmojisHide,
1313
setEmojisUnhide,
14-
setCoursePositionABC,
15-
updateGuide } = require("../../discordBot/services/service");
14+
setCoursePositionABC } = require("../../discordBot/services/service");
15+
const { updateGuide } = require("../../discordBot/services/guide");
1616
const { lockTelegramCourse, unlockTelegramCourse } = require("../../telegramBot/bridge/service");
1717
const { courseAdminRole } = require("../../../config.json");
1818
const { Op } = require("sequelize");

src/db/hooks/courseMemberHooks.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const {
22
updateAnnouncementChannelMessage,
3-
updateGuide,
43
updateInviteLinks } = require("../../discordBot/services/service");
4+
const { updateGuide } = require("../../discordBot/services/guide");
55
const { findCourseFromDbById } = require("../services/courseService");
66
const { findUserByDbId } = require("../services/userService");
77
const { courseAdminRole } = require("../../../config.json");
@@ -18,7 +18,7 @@ const initCourseMemberHooks = (guild, models) => {
1818
logInfo("Member: " + member);
1919
const courseRole = guild.roles.cache.find(r => r.name === course.name);
2020
await member.roles.add(courseRole);
21-
await updateGuide(guild, models);
21+
//await updateGuide(guild, models);
2222
joinedUsersCounter.inc({ course: course.name });
2323
});
2424

@@ -35,11 +35,11 @@ const initCourseMemberHooks = (guild, models) => {
3535
.map(async role => await member.roles.remove(role)));
3636
await member.fetch(true);
3737
const announcementChannel = guild.channels.cache.find(c => c.name === `${course.name}_announcement`);
38-
await updateAnnouncementChannelMessage(guild, announcementChannel);
39-
await updateGuide(guild, models);
38+
await updateAnnouncementChannelMessage(guild, announcementChannel); //This should be only done if the user is an instructor
39+
//await updateGuide(guild, models);
4040
});
4141

42-
models.CourseMember.addHook("afterUpdate", async (courseMember) => {
42+
models.CourseMember.addHook("afterUpdate", async (courseMember) => { // This makes no f****** sense. When a course gets new instructor update all courses announcement info ????
4343
if (courseMember._changed.has("instructor")) {
4444
await updateInviteLinks(guild);
4545
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"use strict";
2+
3+
module.exports = {
4+
up: async (queryInterface) => {
5+
await queryInterface.addConstraint("course", {
6+
fields: ["code"],
7+
type: "unique",
8+
});
9+
},
10+
11+
down: async (queryInterface) => {
12+
await queryInterface.removeConstraint("course", "unique_course_code_constraint");
13+
},
14+
};

src/discordBot/commands/admin/restore_server_from_database.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const {
1010
getCategoryChannelPermissionOverwrites,
1111
createInvitation,
1212
updateAnnouncementChannelMessage,
13-
updateGuide,
1413
setCoursePositionABC } = require("../../services/service");
14+
const { updateGuide } = require("../../services/guide");
1515
const { courseAdminRole, facultyRole } = require("../../../../config.json");
1616

1717
const execute = async (message, args, models) => {

src/discordBot/commands/faculty/create_course.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ const {
55
findCourseFromDb,
66
findCourseFromDbWithFullName } = require("../../../db/services/courseService");
77
const { sendErrorEphemeral, sendEphemeral, editEphemeral } = require("../../services/message");
8-
const { facultyRole } = require("../../../../config.json");
8+
const { courseAdminRole, facultyRole } = require("../../../../config.json");
9+
const { createCourseMemberToDatabase, findCourseMember } = require("../../../db/services/courseMemberService");
10+
const { findUserByDiscordId } = require("../../../db/services/userService");
911

1012
const execute = async (interaction, client, models) => {
1113
if (!interaction.member.permissions.has("ADMINISTRATOR") && !interaction.member.roles.cache.some(r => r.name === facultyRole)) {
@@ -41,7 +43,31 @@ const execute = async (interaction, client, models) => {
4143
await sendEphemeral(interaction, "Creating course...");
4244

4345
await createCourseToDatabase(courseCode, courseFullName, courseName, models.Course);
44-
await editEphemeral(interaction, `Created course ${courseName}.`);
46+
await editEphemeral(interaction, `Created course ${courseName}. Adding you as an instructor to the course.`);
47+
48+
//Adding user as a member of the course
49+
console.log("Adding user as a member of course")
50+
const course = await findCourseFromDb(courseName, models.Course);
51+
const user = await findUserByDiscordId(interaction.member.user.id, models.User);
52+
await createCourseMemberToDatabase(user.id, course.id, models.CourseMember);
53+
54+
//Modifying user to be an instructor of the course
55+
courseMember = await findCourseMember(user.id, course.id, models.CourseMember);
56+
courseMember.instructor = true;
57+
await courseMember.save();
58+
const instructorRole = await interaction.guild.roles.cache.find(r => r.name === `${courseName} ${courseAdminRole}`);
59+
await interaction.member.roles.add(instructorRole);
60+
61+
//Generating final ephemeral message with #channel link in the message
62+
const channels = await interaction.guild.channels.fetch();
63+
const courseChannel = channels.find(
64+
c => c.name === `${courseName}_general` && c.type === "GUILD_TEXT"
65+
);
66+
let channelLink = "";
67+
if (courseChannel) {
68+
channelLink = `<#${courseChannel.id}>`;
69+
}
70+
await editEphemeral(interaction, `Created course ${courseName}. You have been added as an instructor of the course. You can find the course by clicking: ${channelLink || "Channel not found."} or among the listed courses on the sidebar.`);
4571
};
4672

4773
module.exports = {

src/discordBot/events/guildMemberAdd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { updateGuide } = require("../../discordBot/services/service");
1+
const { updateGuide } = require("../../discordBot/services/guide");
22
const { createUserToDatabase } = require("../../db/services/userService");
33

44
const execute = async (member, client, models) => {

0 commit comments

Comments
 (0)